分布式会话的四种解决方案

1.介绍
分布式会话问题即分布式session的一致性问题,出现此问题的根本原因是http协议是无状态的协议。客户端和服务端在某次会话中产生的数据不会被保留下来,所以第二次请求服务端无法知道是否访问过。而记录用户信息,保持http状态的技术,就需要使用cookie和session。而基于服务端一般采用session进行实现。

2.分布式Session实现
(1)完全不用Session
(2)nginx的IP_HASH策略
(3)Tomcat + Redis
(4)Spring Session + Redis

(1)完全不用Session
使用 JWT Token 储存用户身份,然后再从数据库或者 cache 中获取其他的信息。这样无论请求分配到哪个服务器都无所谓。相当于直接将用户信息在数据库或缓存中进行共享了,也就不存在分布式session一致性问题了。

(2)nginx的IP_HASH策略
同一客户端的IP请求都会被路由到同一个目标服务器,也叫会话粘滞。这种方案,配置简单,不入侵应用,不需要额外修改代码;但存在问题是服务器重启session会丢失。

(3)Tomcat + Redis
这里有两种常见的方案,一种是session复制,即多个tomcat之间通过修改配置文件,达到session之间的复制;另一种是session共享,即将session集中存储到redis中,第二种方案的实现如下:
首先在 Tomcat 的配置文件中配置RedisSessionManager:

<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />

<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
         host="{redis.host}"
         port="{redis.port}"
         database="{redis.dbnum}"
         maxInactiveInterval="60"/>

然后指定 Redis 的 host 和 port :

<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"
     sentinelMaster="mymaster"
     sentinels="<sentinel1-ip>:26379,<sentinel2-ip>:26379,<sentinel3-ip>:26379"
     maxInactiveInterval="60"/>

(4)Spring Session + Redis
这里分spring中使用spring session整合redis和springboot中使用spring session整合redis使用。
在spring中使用spring session过程:
首先在 pom.xml 中配置:

<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
  <version>1.2.1.RELEASE</version>
</dependency>
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>2.8.1</version>
</dependency>

然后在 Spring 配置文件中配置:

<bean id="redisHttpSessionConfiguration"
  class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration">
    <property name="maxInactiveIntervalInSeconds" value="600"/>
</bean>

<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxTotal" value="100" />
    <property name="maxIdle" value="10" />
</bean>

<bean id="jedisConnectionFactory"
      class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy">
    <property name="hostName" value="${redis_hostname}"/>
    <property name="port" value="${redis_port}"/>
    <property name="password" value="${redis_pwd}" />
    <property name="timeout" value="3000"/>
    <property name="usePool" value="true"/>
    <property name="poolConfig" ref="jedisPoolConfig"/>
</bean>

接着在 web.xml 中配置:

<filter>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSessionRepositoryFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

最后进行测试:

@RestController
@RequestMapping("/test")
public class TestSessionController {

    @RequestMapping("/addSession")
    public String addSession(HttpServletRequest request, String username) {
        request.getSession().setAttribute("name",  "xiaoxiyuan");
        return "ok";
    }

    @RequestMapping("/getSession")
    public String getSession(HttpServletRequest request, Model model){
        String name = request.getSession().getAttribute("name");
        return name;
    }
}

总结:先给 Spring Session 配置基于 Redis 来存储 Session 数据,然后配置了一个 Spring Session 的过滤器,这样的话,Session 相关操作都会交给 Spring Session 来管理了。接着在代码中,就用原生的 Session 操作,就是直接基于 Spring Session 从 Redis 中获取数据了。

在springboot中使用spring session过程:
首先在项目中引入相关jar依赖:

<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>

然后配置redis:

spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379

最后在启动类上添加@EnableRedisHttpSession注解:

@SpringBootApplication
@EnableCaching
@EnableRedisHttpSession
@ServletComponentScan
public class LoginApplication  extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(LoginApplication.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(LoginApplication.class, args);
    }

}


总结:请求通过tomcat到达servlet容器的时候,通过过滤器对请求做了一次封装,如果没有过滤器,servlet就会从tomcat中获取session,有了过滤器之后,取出来的session就是redis中session,有的话就从redis中获取,没有的话就创建并提交到redis中去。

3.如何选用分布式session方案
根据业务场景,完全不用Session或配置nginx的IP_HASH策略这两种方案是可以使用的;
session复制不推荐使用,因为session复制性能低,不易存储太多数据,内存消耗大;
推荐使用session共享即session的集中存储,因为这种方案能适应各种负载均衡,扩展能力强,也可以存储大量数据等;
推荐使用基于spring或springboot的spring session+redis存储方案,这种方案使用简单,易于与项目整合。


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值