Spring Boot应用通常会部署在多个Web服务器上同时提供服务,我们称之为水平拓展。
Spring Boot 应用水平扩展有两个问题需要解决,一个是将用户的请求派发到水平部署的任意一台Spring Boot应用,通常用一个反向代理服务器来实现,如使用Nginx作为反向代理。
另外一个需要解决的问题是会话管理, 单个Spring Boot 应用的会话由Tomcat 来管理,会话信息与Tomcat 存放在一起。如果部署多个Spring Boot 应用,对于同一个用户请求,即使请求通过Nginx 派发到不同的Web 服务器上,也能共享会话信息。
集中式会话: 所有Web 服务器都共享一个会话,会话信息通常存放在一台服务器上,本文使用Redis服务器来存放会话。
Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3 )、TCP/UDP代理服务器,并在一个BSD-like 协议下发行。 —— [ Nginx官网 ]
打开Nginx网站,点击download进入下载页面,根据自己的操作系统选择下载,以windows系统为例,下载nginx/Windows-1.14.0版本,直接解压,然后运行Nginx即可。
Nginx默认监听80端口 启动后,可以访问http://127.0.0.1:80 ,会看到Nginx 的欢迎页面。如下图所示。
Nginx 的配置文件conf/nginx.conf 下包含多个指令块,我们主要关注http块和location 块。
http块:可以嵌套多个Server ,配置代理、缓存、日志定义等绝大多数功能和第三方模块,如mime-type 定义、日志自定义、是否使用sendfile 传输文件、连接超时时间、单连接请求数等。
location 块:配置请求的路由,以及各种页面的处理情况。
我们需要在http 块中增加upstream 指令,内容如下:
http {
upstream backend {
server 127.0.0.1:9000;
server 127.0.0.1:9001;
}
}
backend 也可以为任意名字,我们在下面的配置将要引用到:
location / {
proxy_pass http://backend;
}
location后可以是一个正则表达式,我们这里用“ / ”表示所有客户端请求都会传给http://backend,也就是我们配置的backend指令的地址列表。因此,整个http块类似下面的样子:
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream backend {
server 127.0.0.1:9000;
server 127.0.0.1:9001;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://backend;
}
}
我们在后面将创建一个Spring Boot 应用,并分别以9000 和9001两个端口启动,然后在Spring Session 的基础上一步步来完成Spring Boot 应用的水平扩展。
在默认情况下, Spring Boot 使用Tomcat 服务器的Session 实现,我们编写一个例子用于测试:
@Slf4j
@Controller
public class SpringSessionCrontroller {
@RequestMapping("/putsession")
@ResponseBody
public String putSession(HttpServletRequest request) {
HttpSession session = request.getSession();
log.info(session.getClass().toString());
log.info(session.getId());
String name = "fly";
session.setAttribute("user", name);
return "hello," + name;
}
}
如果访问服务/putsession ,控制台输出为:
SpringSessionCrontroller : class org.apache.catalina.session.StandardSessionFacade
SpringSessionCrontroller : 1D8CC7826A3369BC33D867E4F4090D17
可以看到,Session 管理是通过Tomcat 提供的org.apache.catalina.session.StandardSessionFacade实现的。
在配置文件application.properties 中添加如下内容:
spring.session.store-type=RedislJDBCIHazelcastlnone
Spring Session 四种管理方式:
方式 | 含义 |
---|---|
Redis | Session数据存放在Redis中 |
JDBC | Session数据存放在数据库中,session会自动创建相关表 |
Hazelcast | Session数据存放在Hazelcast中 |
none | 禁用Spring Session功能 |
通过配置属性spring.session.store-type 来指定Session 的存储方式,如:
spring.session.store-type=Redis
修改为配置和增加Spring Session 依赖后,如果访问服务/putsession,控制台输出为:
SpringSessionCrontroller: class org.springframework.session.web.http.SessionRepositoryFilter SessionRepositoryRequestWrapper S e s s i o n R e p o s i t o r y R e q u e s t W r a p p e r HttpSessionWrapper
SpringSessionCrontroller: d6fdfb46-841c-4514-8c74-613ddba1a0a5
本文将使用Redis来保存Session,需自行安装Redis
application.properties配置如下:
#Redis服务器地址
spring.redis.host=127.0.0.1
#Redis服务器连接端口
spring.redis.port=6379
#Redis服务器连接密码(默认为空)
spring.redis.password=
#Session保存在Redis里
spring.session.store-type=Redis
pom.xml需要增加相关依赖如下:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.springframework.sessiongroupId>
<artifactId>spring-session-data-redisartifactId>
dependency>
再次访问/putsession后,我们通过Redis 客户端工具访问Redis ,比如使用redis-cli输入命令查看session
本文使用的是Spring Boot 2.0.4.RELEASE、JDK 8、Nginx、Redis 环境。
如果Nignx和redis都已经启动并按上述配置好,假设我们在本机上部署两个Spring Boot应用端口分别是9000和9001,进入工程目录, 运行mvn package给项目打包,如项目jar打好后为springSession-0.0.1-SNAPSHOT.jar 在此目录打开命令窗口运行如下命令:
java -jar target/springSession-0.0.1-SNAPSHOT.jar --server.port=9000
打开另外一个命令窗口,进入工程目录, 运行:
java -jar target/springSession-0.0.1-SNAPSHOT.jar --server.port=9001
这时候, 我们就有两台Spring Boot 应用。接下来,我们访问以下地址, 并刷新多次:
http://127.0.0.1/putsession
这时候就看到两个Spring Boot 应用均有日志输出,比如9000 端口的应用控制台输出如下:
SpringSessionCrontroller:class org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper
SpringSessionCrontroller:d6fdfb46-841c-4514-8c74-613ddba1a0a5
9001 端口的Spring Boot 应用也有类似输出:
SpringSessionCrontroller:class org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper
SpringSessionCrontroller:d6fdfb46-841c-4514-8c74-613ddba1a0a5
我们看到,两个Spring Boot 应用都具有相同的sessionId ,如果停掉任意一台应用, 系统还有另外一台服务器提供服务, 会话信息保存在Redis 中。