springboot redis实现session共享

当创建分布式服务时会出现session共享的问题,即第一次访问的时候负载均衡会将请求分配到server1上,但是当第二次访问的时候,如果请求没有分配到server1上,那么用户的会话状态将丢失。下面给出了一种使用springboot整合redis的共享session的例子。

工具

  • nginx-1.7.10
  • redis-2.8

模拟分布式

使用nginx模拟一个简单的分布式的环境,在nginx.conf中添加

upstream  tomcat  {  
        server    127.0.0.1:8088  weight=1;
        server    127.0.0.1:9090  weight=2;  
} 

修改监听的端口,即所有的访问都通过83进行反向代理

 server {
        listen       83;
        server_name  localhost;
    }

创建一个springboot项目

在pom文件中加入

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-redis</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework.session</groupId>
		<artifactId>spring-session-data-redis</artifactId>
	</dependency>

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>

配置redis和访问的端口

在application.properties中添加

spring.redis.host=localhost
spring.redis.port=16001

server.port=9090

配置redis缓存session

@Configuration
@EnableRedisHttpSession
public class RedisConfig {
}

编写一个测试的控制类

@RestController
@RequestMapping(value = "/api/v1")
@PropertySource("classpath:application.properties")
public class HomeController {

    @Value("${server.port}")
    public String port;

    @RequestMapping(value = "/first", method = RequestMethod.GET)
    public Object firstResp(HttpServletRequest request) {
        Map<String, Object> map = new HashMap<>();
        request.getSession().setAttribute("url", request.getRequestURL());
        map.put("url", request.getRequestURL());
        map.put("port",port);
        return map;
    }

    @RequestMapping(value = "/sessions", method = RequestMethod.GET)
    public Object sessions (HttpServletRequest request){
        Map<String, Object> map = new HashMap<>();
        map.put("sessionId", request.getSession().getId());
        map.put("message", request.getSession().getAttribute("url"));
        map.put("port",port);
        return map;
    }

}

测试

复制一份相同的代码,然后区分访问端口为8088和9090

  • 访问:http://localhost:83/api/v1/first
{
	port: "9090",
	url: "http://tomcat/api/v1/first"
}
  • 多次访问:http://localhost:83/api/v1/sessions
{
	port: "9090",
	sessionId: "2e35353c-4d9e-4588-8257-43b54da9adc7",
	message: "http://tomcat/api/v1/first"
}

{
	port: "8088",
	sessionId: "2e35353c-4d9e-4588-8257-43b54da9adc7",
	message: "http://tomcat/api/v1/first"
}

虽然访问落到不同的服务下,但是每次获取的sessionId是相同的,即达到了session共享的目的。

目前缺点

  • 目前nginx的配置,当一个服务挂掉的时候访问依然会落到这个服务商
  • 目前的redis没有实现集群管理,当redis挂掉的时候,所有的session都将失效

参考资料

上篇GeoHash实现周边推荐
下篇小觑Chrome Extension