背景:
一个普通项目配置在一台服务器上并发性并不高,但是像一些大型的电商 门户网站,每一天的访问量过千万,也就是说在同一时间可能会有近万人访问。一台服务器的并发量就难以满足。这个时候人们 就想到了利用分布式架构。采用集群的方式来提高并发性。
一般会配一个nginx集群来进行反向代理,使得多个服务器上分担的压力均衡,达到负载均衡。如图
这个时候就存在一个问题,传统方式我们登陆注册需要将登陆后的用户信息存储在session中,后面需要操作只需要在session中取就好了。但是现在每个服务器对应不同的会话,也就说session域也不一样,比如上图我第一次访问被nginx通知我去找第一个服务器,登陆以后将登陆信息存储在第一个session中,但第二次我访问同样的网站,又被分到第二个服务器上,这个服务器并没有上次登陆的信息,也取不到第一个session中的信息,那么还得登陆一下。
这样问题就来了:假如每次访问,请求被转发给不同的服务器,那么就得不停的登陆,这个体验感就很差,也没有网站这样做。
解决session共享
1、session复制
最简单的方式也容易想到,无论在哪个服务器上登陆,都会把session信息复制到另外的服务器上,这样一来无论被转发的哪个服务器,session信息都会存在。但是这样做弊端也很明显。
如果我有100个服务器,也就意味着每次登陆存入session的信息会被查到并复制到剩下的99个服务器上,而且还需要随时监视每个服务器上的用户登陆情况,既浪费了空间,效率也非常低。
2、session黏着
利用算法,将每个用户访问的服务器固定下来,比如下图中
利用hash法,将每个访问者的id对服务器总数取余,余数对应的服务器的s_id的请求就会被转发到指定的服务器上。也就是说每个访问者访问该网站被分到服务器是固定的,这样一来就不存在session中的信息不一致的情况。
这样做也会存在一些弊端,比如当服务器总数发生改变的时候,进行hash的结果也会发生变化,导致session存入的访问者登陆信息又不一致。
3、session共享
将登陆信息存入一个固定的session服务器(多用redis数据库)每次读写信息,只需要找session服务器即可。这样就可以达到session共享,但是存在跨域的问题。
4、不使用session的方式----jwt (json web token)
利用token(令牌)登陆成功以后会创建一个令牌,下一次访问只需要带着这个令牌去,就不需要再次登陆,这样种方式也被称作鉴权
JJWT API
导入maven依赖包
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.5</version>
</dependency>
生成token
String token = Jwts.builder()
.claim("sub", "内容部分")
.claim("exp", "时间的秒值")
//这里利用HS256对称算法 //此字符串为密钥
.signWith(SignatureAlgorithm.HS256, "ZYUf0Oym0Lhi9V7f6ML6tXcv0HvGH7YnuhbGhk8Y/+U=")
.compact();
sub是登陆成功以后的内容部分,也就是需要存储的内容信息
exp设置令牌过期时间
除了上述的设置方式,还可以这样设置
String token = Jwts.builder()
.setSubject("内容部分") // 等价于claims("sub", "内容部分")
.setExpiration(new Date(System.currentTimeMillis()+60*1000)) // 设置 1 分钟后过期
.signWith(SignatureAlgorithm.HS256, "ZYUf0Oym0Lhi9V7f6ML6tXcv0HvGH7YnuhbGhk8Y/+U=")
.compact();
验证令牌
Jwts.parser() // 设置秘钥
.setSigningKey("ZYUf0Oym0Lhi9V7f6ML6tXcv0HvGH7YnuhbGhk8Y/+U=").parse("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIiwiZXhwIjoiMTU1MjcxNTU1MyJ9.4rvHxsGNiZzuukQ2L-oT9uqKFymr94-371Am05qY7BQ");//传入令牌
jwts.parse()方法会有一个返回值Jwt类。可以通过jwt的get()获取密钥中的信息,如sub exp 或自定义的等等。