文档章节

分布式架构 共享session的常见解决方案

太猪-YJ
 太猪-YJ
发布于 06/26 15:59
字数 2191
阅读 10
收藏 0

在使用分布式架构时,会遇到分布式架构常见的几个问题:

分布式事务、接口幂等性、分布式锁和分布式 session。

分布式session

一、什么是session

浏览器在访问一个web服务的时候,会在浏览器中生成一个cookie文件用于在浏览器本地缓存数据,这些数据可以根据浏览器的设置,持久保存在浏览器本地知道手动清除浏览器缓存文件。也可以在结束对一个web服务的会话后(既关闭所有与这个web服务有关的页面和请求)就清空对这个web服务的cookie信息。当浏览器生成cookie时,每次向web服务端发起请求,都会携带一个特殊的jsessionid cookie,根据这个东西,服务端容器(比如tomcat)就会生成一个与之对应的session域,在session域里存放缓存数据。

一般情况下,只要浏览器中的cookie还在,与之对应的session就在。但如果cookie没了,session会根据tomcat配置的生命周期时间,或者web应用的web.xml文件里配置的session生命周期时间而结束。我们一般会使用session来缓存用户的登录信息等,起到一个获取用户登录信息和登录状态检查的作用。

单服务系统中,可以直接拿session来用,但是分布式系统中,session状态怎么维护?

1.完全不用session

JSON WEB TOKEN (缩写 JWT)是目前最流行的跨域认证解决方案,我们来介绍一下它的原理和用法:

一、跨域认证问题

1、用户向服务器发送用户名和密码。

2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。

3、服务器向用户返回一个 session_id,写入用户的 Cookie。

4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。

5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。

举例来说,A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?

一种解决方案是 session 数据持久化,写入数据库或别的持久层。各种服务收到请求后,都向持久层请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层万一挂了,就会单点失败。

另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。

二、JWT 的原理

JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。

{
  "姓名": "张三",
  "角色": "管理员",
  "到期时间": "2018年7月1日0点0分"
}

以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。

既在客户端向服务端发起http请求时,可以在http请求的Header中加入自定义的key:value,自定义的信息包括了参与加密的信息,业务数据和签名信息,然后当服务端收到请求后,根据http请求头中的这些自定义信息,以下是 JWT约定的公共参数:

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号

通过密钥等方式,判断请求是否合法,对合法的请求进行处理,不合法的请求直接无视。不再通过session来判断用户合法性和获取用户的信息,实现了跨域系统的访问。

三、JWT 的几个特点

(1)JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。

(2)JWT 不加密的情况下,不能将秘密数据写入 JWT。

(3)JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。

(4)JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

(5)JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。

(6)为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

2.tomcat + redis

这个其实还挺方便的,就是使用 session 的代码,跟以前一样,还是基于 tomcat 原生的 session 支持即可,然后就是用一个叫做 Tomcat RedisSessionManager 的东西,让所有我们部署的 tomcat 都将 session 数据存储到 redis 即可

在 tomcat 的配置文件中配置:

<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 就 ok 了。

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

还可以用上面这种方式基于 redis 哨兵支持的 redis 高可用集群来保存 session 数据,都是 ok 的。

3.spring session + redis

上面所说的第二种方式会与 tomcat 容器重耦合,如果我要将 web 容器迁移成 jetty,难道还要重新把 jetty 都配置一遍?

因为上面那种 tomcat + redis 的方式好用,但是会严重依赖于web容器,不好将代码移植到其他 web 容器上去,尤其是你要是换了技术栈咋整?比如换成了 spring cloud 或者是 spring boot 之类的呢?

所以现在比较好的还是基于 Java 一站式解决方案,也就是 spring。人家 spring 基本上承包了大部分我们需要使用的框架,spirng cloud 做微服务,spring boot 做脚手架,所以用 sping session 是一个很好的选择。

在 pom.xml中,加入spring session和redis的依赖包,配置:​​​​​​​

<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的配置文件中,配置好redis:​​​​​​​

<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 TestController {
    @RequestMapping("/putIntoSession")    public String putIntoSession(HttpServletRequest request, String username) {        request.getSession().setAttribute("name",  "leo");        return "ok";    }
    @RequestMapping("/getFromSession")    public String getFromSession(HttpServletRequest request, Model model){        String name = request.getSession().getAttribute("name");        return name;    }}

上面的代码就是 ok 的,给 sping session 配置基于 redis 来存储 session 数据,然后配置了一个 spring session 的过滤器,这样的话,session 相关操作都会交给 spring session 来管了。接着在代码中,就用原生的 session 操作,就是直接基于 spring sesion 从 redis 中获取数据了。

实现分布式的会话有很多种方式,我说的只不过是比较常见的几种方式,tomcat + redis 早期比较常用,但是会重耦合到 tomcat 中;近些年,通过 spring session 来实现。

© 著作权归作者所有

太猪-YJ
粉丝 12
博文 92
码字总数 97999
作品 0
海淀
私信 提问
Session分布式共享 = Session + Redis + Nginx

原文:Session分布式共享 = Session + Redis + Nginx 一、Session 1、Session 介绍 我相信,搞Web开发的对Session一定再熟悉不过了,所以我就简单的介绍一下。 Session:在计算机中,尤其是在网...

杰克.陈
2017/12/07
0
0
分布式集群系统下的高可用session解决方案

目前,为了使web能适应大规模的访问,需要实现应用的集群部署. 而实现集群部署首先要解决session的统一,即需要实现session的共享机制。 目前,在集群系统下实现session统一的有如下几种方案:...

凯文加内特
2015/03/19
148
0
分布式web架构中Session管理的方法

分布式架构 单体架构中,Session管理方案是在用户进行登录的时候将Session存放在应用服务器的内存中,由于只有一个应用服务器节点,用户的所有请求都是这个唯一节点进行响应处理,所以能够轻...

刘诗书
2017/11/23
0
0
分布式Session共享解决方案

image.png Session是服务器用来保存用户操作的一系列会话信息,由Web容器进行管理。单机情况下,不存在Session共享的情况,分布式情况下,如果不进行Session共享会出现请求落到不同机器要重复...

架构之路
2017/12/13
0
0
百度、阿里、腾讯、京东、大型互联网分布式架构必备技能

分布式架构 迎接高并发大数据的挑战,从深度到广度完善知识体系,成为下一个互联网高薪人才。 理论结合实战,透彻理解分布式架构及其解决方案。 面向人群 1、工作1-5年需要突破瓶颈; 2、传统...

Java高级架构
2017/12/21
0
0

没有更多内容

加载失败,请刷新页面

加载更多

redis 学习2

网站 启动 服务端 启动redis 服务端 在redis 安装目录下 src 里面 ./redis-server & 可以指定 配置文件或者端口 客户端 在 redis 的安装目录里面的 src 里面 ./redis-cli 可以指定 指定 连接...

之渊
今天
2
0
Spring boot 静态资源访问

0. 两个配置 spring.mvc.static-path-patternspring.resources.static-locations 1. application中需要先行的两个配置项 1.1 spring.mvc.static-path-pattern 这个配置项是告诉springboo......

moon888
今天
4
0
hash slot(虚拟桶)

在分布式集群中,如何保证相同请求落到相同的机器上,并且后面的集群机器可以尽可能的均分请求,并且当扩容或down机的情况下能对原有集群影响最小。 round robin算法:是把数据mod后直接映射...

李朝强
今天
4
0
Kafka 原理和实战

本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/bV8AhqAjQp4a_iXRfobkCQ 作者简介:郑志彬,毕业于华南理工大学计算机科学与技术(双语班)。先后从事过电子商务、开放平...

vivo互联网技术
今天
24
0
java数据类型

基本类型: 整型:Byte,short,int,long 浮点型:float,double 字符型:char 布尔型:boolean 引用类型: 类类型: 接口类型: 数组类型: Byte 1字节 八位 -128 -------- 127 short 2字节...

audience_1
今天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部