一、Spring-Session使用的场景
HttpSession是通过Servlet容器进行创建和管理的,在单机环境中。通过Http请求创建的Session信息是存储在Web服务器内存中,如Tomcat/Jetty。假如当用户通过浏览器访问应用服务器,session信息中保存了用户的登录信息,并且session信息没有过期失,效那么用户就一直处于登录状态,可以做一些登录状态的业务操作。
但是现在很多的服务器都采用分布式集群的方式进行部署,一个Web应用,可能部署在几台不同的服务器上,通过LVS或者Nginx等进行负载均衡(一般使用Nginx+Tomcat实现负载均衡)。此时来自同一用户的http请求将有可能被分发到不同的web站点中去(如:第一次分配到A站点,第二次可能分配到B站点)。那么问题就来了,如何保证不同的web站点能够共享同一份session数据呢?
假如用户在发起第一次请求时候访问了A站点,并在A站点的session中保存了登录信息,当用户第二次发起请求,通过负载均衡请求分配到B站点了,那么此时B站点能否获取用户保存的登录的信息呢?答案是不能的,因为上面说明,session是存储在对应Web服务器的内存的,不能进行共享,此时spring-session就出现了,来帮我们解决这个session共享的问题!
简单点说就是请求http请求经过Filter链,根据配置信息过滤器将创建session的权利由tomcat交给了spring-session中的SessionRepository,通过spring-session创建会话,Session的内容统一存储在一个数据库(如MySQL)或缓存(如Redis,Mongo)中。
当然使用Nginx的ip_hash策略也可以解决session同步的问题。
在使用Nginx的ip_hash策略时候,每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,这样就解决session的同步问题。
二、代码实现
1、pom引包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhouzy.redis</groupId>
<artifactId>zhouzyRedis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zhouzyRedis</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<!--修改jdk版本号-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>
<dependencies>
<!-- web模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.30</version>
</dependency>
<!-- 加解密工具 -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<!--使用Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--借助guava的布隆过滤器-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、application.yml
server:
port: 8081
spring:
redis:
database: 3
host: 127.0.0.1
port: 6379
jedis.pool.max-idle: 100
jedis.pool.max-wait: -1ms
jedis.pool.min-idle: 2
timeout: 2000ms
session:
store-type: redis
3、控制层接口
package com.zhouzy.redis.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RedisSessionController {
@RequestMapping("/show")
public String show(HttpServletRequest request) {
return "我是: " + request.getLocalPort();
}
@RequestMapping(value = "/session")
public String getSession(HttpServletRequest request) {
request.getSession().setAttribute("userName", "admin");
return "I'm " + request.getLocalPort() + " ,save session " + request.getSession().getId();
}
@RequestMapping(value = "/get")
public String get(HttpServletRequest request) {
String userName = (String) request.getSession().getAttribute("userName");
return "我是 " + request.getLocalPort() + " ,userName :" + userName;
}
}
4、启动类
package com.zhouzy.redis;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Hello world!
*
*/
@SpringBootApplication
public class WebApplication {
public static void main( String[] args ){
SpringApplication.run(WebApplication.class, args);
}
}
三、测试
1、启动redis
启动停止命令:
redis-server --service-install redis.windows.conf
启动服务:redis-server --service-start
停止服务:redis-server --service-stop
2、启动Nginx
upstream c{
server localhost:8081;
server localhost:8088;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://zhouzy;
}
}
启动命令:
D:\SOFT\nginx-1.12.2>nginx.exe
停止:
停止:
D:\SOFT\nginx-1.12.2>nginx.exe -s stop或
D:\SOFT\nginx-1.12.2>nginx.exe -s quit
3、启动应用
1)先启动8081端口
2)修改端口号为8088,在启动一个
4、访问测试
在8088上保存的session,然后在8081和8088上都可以取到,实现了session共享!Nginx也实现了分流!