引言
何为Session?
Session 是客户端与服务器通讯会话技术, 比如浏览器登陆、记录整个浏览会话信息。
Session原理:客户对向服务器端发送请求后,Session 创建在服务器端,返回Sessionid给客户端浏览器保存在本地,当下次发送请求的时候,在请求头中传递sessionId获取对应的从服务器上获取对应的Sesison对象。Session是保存在服务器的,及时关闭服务器。
熟悉下相关代码:
@SpringBootApplication @RestController public class TestSessionController { // 创建session 会话 @RequestMapping("/createSession") public String createSession(HttpServletRequest request, String nameValue) { HttpSession session = request.getSession(); System.out.println("存入Session sessionid:信息" + session.getId() + ",nameValue:" + nameValue); session.setAttribute("name", nameValue); return "success"; } // 获取session 会话 @RequestMapping("/getSession") public Object getSession(HttpServletRequest request) { HttpSession session = request.getSession(); System.out.println("获取Session sessionid:信息" + session.getId()); Object value = session.getAttribute("name"); return value; } public static void main(String[] args) { SpringApplication.run(TestSessionController.class, args); } }
引出问题:当服务器集群时,如果服务器产生了集群后,因为session是存放在服务器上,客户端会使用同一个Sessionid在多个不同的服务器上获取对应的Session,从而会导致Session不一致问题,那么该如何解决呢?
答:可以通过nginx或者haproxy实现IP绑定来解决Session一致性的问题 。
先看看Nginx相关配置:
1.Nginx负载均衡配置
Nginx负载均衡提供上游服务器(真实业务逻辑访问的服务器),负载均衡、故障转移、失败重试、容错、健康检查等。
当上游服务器(真实业务逻辑访问的服务器)发生故障时,可以转移到其他上游服务器(真实业务逻辑访问的服务器)。
upstream 主要配置如下(配置上游服务器的IP地址和端口):
###定义上游服务器(需要被nginx真实代理访问的服务器) 默认是轮训机制 upstream backServer{ server 127.0.0.1:8080; server 127.0.0.1:8081; } server { listen 80; server_name www.xxx.com; location / { ### 指定上游服务器负载均衡服务器 proxy_pass http://backServer; index index.html index.htm; } }
1.1 负载均衡算法
- 轮询(默认): 每个请求按时间顺序逐一分配到不同的后端服务,如果后端某台服务器死机,自动剔除故障系统,使用户访问不受影响。
- weight(轮询权值):weight的值越大分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下。或者仅仅为在主从的情况下设置不同的权值,达到合理有效的地利用主机资源。
- ip_hash :每个请求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题,俗称IP绑定。
- fair(第三方):比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间 来分配请求,响应时间短的优先分配。Nginx本身不支持fair,如果需要这种调度算法,则必须安装upstream_fair模块。
- url_hash(第三方):按访问的URL的哈希结果来分配请求,使每个URL定向到一台后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身不支持url_hash,如果需要这种调度算法,则必须安装Nginx的hash软件包。
2.分布式Session一致性解决方案
文章开头前已经提出了解决方案,解决Session一致性问题,可以使用Nginx或者Haproxy的IP绑定来处理:
- Nginx:用Nginx 做的负载均衡可以添加ip_hash这个配置
- Haproxy:用haproxy做的负载均衡可以用 balance source这个配置
使用Nginx和Haproxy是偏向于运营人员的,那么开发这边要怎么处理呢?其实最靠谱的分布式Session一致性解决方案是“基于令牌的方式”,因为Session本身就是分布式共享链接。
简单的来说,就是把Session保存到Redis服务器,token有服务端生成,通过token从Redis服务器获取Session对象,其伪代码如下:
1.TokenService:
@Service public class TokenService { @Autowired private RedisService redisService; // 新增 返回token public String put(Object object) { String token = getToken(); redisService.setString(token, object); return token; } // 获取信息 public String get(String token) { String reuslt = redisService.getString(token); return reuslt; } public String getToken() { return UUID.randomUUID().toString(); } }
TokenController :
@RestController public class TokenController { @Autowired private TokenService tokenService; @Value("${server.port}") private String serverPort; @RequestMapping("/put") public String put(String nameValue) { String token = tokenService.put(nameValue); return token + "-" + serverPort; } @RequestMapping("/get") public String get(String token) { String value = tokenService.get(token); return value + "-" + serverPort; } }
总结