可以使用的Session存储机制为:
Redis(建议)
JDBC(传统数据库)
MongoDB
Hazelcast
在传统单机web应用中,一般使用tomcat/jetty等web容器时,用户的session都是由容器管理。浏览器使用cookie中记录sessionId,容器根据sessionId判断用户是否存在会话session。这里的限制是,session存储在web容器中,被单台服务器容器管理。
但是网站主键演变,分布式应用和集群是趋势(提高性能)。此时用户的请求可能被负载分发至不同的服务器,此时传统的web容器管理用户会话session的方式即行不通。除非集群或者分布式web应用能够共享session,尽管tomcat等支持这样做。但是这样存在以下两点问题:
Ø 需要侵入web容器,提高问题的复杂
Ø web容器之间共享session,集群机器之间势必要交互耦合

示例:
步1:创建Spring boot项目添加以下依赖
注意: Spring Session JDBC依赖负责将Session信息持久化到数据库中。
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>cn.isoft</groupId>
<artifactId>isoft-web</artifactId>
<version>1.0</version>
<name>isoft-web</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</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>
<executions>
<execution>
<configuration>
<mainClass>cn.isoft.IsoftWebApplication</mainClass>
</configuration>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
步2、创建数据表
默认情况下,在包:classpath:org/springframework/session/jdbc/ 里面包含了所有不同的数据库创建保存Sessionin信息的表结构。
CREATE TABLE SPRING_SESSION (
PRIMARY_ID CHAR(36) NOT NULL,
SESSION_ID CHAR(36) NOT NULL,
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
) ENGINE=INNODB ROW_FORMAT=DYNAMIC;
CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);
CREATE TABLE SPRING_SESSION_ATTRIBUTES (
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
ATTRIBUTE_BYTES BLOB NOT NULL,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
步3、配置数据连接
在application.properties文件中配置数据连接及JDBC Session信息如下:
server.port=9999
spring.profiles.active=dev
spring.datasource.url=jdbc:mysql://localhost:3306/study?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.platform=mysql
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.http.encoding.charset=UTF-8
spring.messages.encoding=UTF-8
spring.mvc.date-format=yyyy-MM-dd HH:mm:ss
#会话存储类型
spring.session.store-type=JDBC
spring.session.jdbc.schema=classpath:org/springframework/session/jdbc/schema-mysql.sql
# Cron expression for expired session cleanup job.
spring.session.jdbc.cleanup-cron=0 * * * * *
# Name of the database table used to store sessions.
spring.session.jdbc.table-name=SPRING_SESSION
logging.level.root=info
logging.file=logs/web.log
步4、开发启动类及Spring Boot MVC配置类
启动类:
package cn.isoft;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@SpringBootApplication
@EnableWebMvc
public class IsoftWebApplication {
public static void main(String[] args) {
SpringApplication.run(IsoftWebApplication.class, args);
}
}
WebMvc配置类:
package cn.isoft.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
@EnableJdbcHttpSession
public class WebConfig implements WebMvcConfigurer {
/**
* 设置一些默认的请求的地址转发
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
System.err.println("ViewControllers....");
registry.addViewController("/").setViewName("index");
registry.addViewController("").setViewName("index");
}
/**
* 设置哪些可以跨域请求
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:8888").allowedHeaders("*").allowCredentials(true);
}
/**
* 配置静态资源目录
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
}
开发一个测试用的控制器:
package cn.isoft.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
@EnableJdbcHttpSession
public class WebConfig implements WebMvcConfigurer {
/**
* 设置一些默认的请求的地址转发
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
System.err.println("ViewControllers....");
registry.addViewController("/").setViewName("index");
registry.addViewController("").setViewName("index");
}
/**
* 设置哪些可以跨域请求
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:8888").allowedHeaders("*").allowCredentials(true);
}
/**
* 配置静态资源目录
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
}
开发一个HTML页面,请求并获取SessionID:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" th:src="@{/static/jquery.min.js}"></script>
</head>
<body>
<p>主页</p>
<hr>
<button type="button" id="btn1">测试Ajax</button>
</body>
<script type="text/javascript">
$(function() {
$("#btn1").click(function() {
$.get("demo/one/hello", function(txt) {
$("#btn1").after(txt + "<br>");
}, "text");
});
});
</script>
</html>
步5、打包spring boot
将项目打包成两个项目,一个使用端口号8888,另一个使得端口号9999。打完的jar包可以重命名如下:
isoft-web-1.0_8888.jar
isoft-web-1.0_9999.jar
现在使用java -jar 启动这两个项目:
Java -jar isoft-web-1.0_8888.jar

Java -jar isoft-web-1.0_9999.jar

步6、配置nginx
在http{ }节点下配置 upstream如下:
upstream nginxdemo{
server 127.0.0.1:8888;
server 127.0.0.1:9999;
}
server {
listen 8080;
location {
proxy_pass http://nginxdemo;
}
}
步7、启动nginx(测试成功)
现在就可以启动nginx并多次访问,可以看到是同一个Session ID:

查看后台的输出以下信息:
8888服务器输出的信息:

9999服务器输出的信息:

由于没有配置比重,所以,分别调用了4次。
且共享了Session信息。




