分布式Session研究(一):Docker + spring boot +Nginx构建分布式应用
2017-01-11 16:33
1006 查看
由于自己一直痴迷于大型分布式系统的设计原理与实践。奈何条件有限,毕竟还在读书,根本无法接触到真正的分布式,真正的大数据。便只能在自己电脑上通过docker这种虚拟化技术来自己搭建”分布式系统”来玩玩,体验一下分布式Session,分布式事物等等。
这篇文章将搭建出一个”分布式”系统,并先体验分布式系统中Session管理的问题,并通过集中Session管理方案解决
Maven:
然后写一个比较简单的Controller:
现在一个SpringBoot项目就写完了。它只提供了两个Rest接口。一个设置Session值,一个获取Session值。注意一点,上面代码中返回值中有个One的标识。现在我们将其Docker化:
先通过mvn package将其打成jar包,命名为dockerOne.jar
编写Dockerfile
生成镜像并运行
Dockerfile:
然后运行 sudo docker build -t dockerone/jdk8 . 构建第一个镜像。
按上面步骤构建第二个镜像:代码中one改为two。镜像名称也改为dockertwo/jdk8。
启动上面两个镜像,通过inspect查看ip为192.168.110.2 192.168.110.3 (docker网桥为192.168.110.1)
启动Nginx并查看ip为 192.168.110.4
首先请求/setSession
1.请求分配到two服务器: 没有携带cookie值,服务器符合 E5DD28A3B255AEB9B95C2C5304342685
2.请求分配到one服务器: 携带cookie E5DD28A3B255AEB9B95C2C5304342685 却返回set-cookie 75965AEBC91A57B509BDE095602B9F97
3.请求到two 发送 75965AEBC91A57B509BDE095602B9F97 却返回60BC939E156DBB21676BCC02EC0C7E2A
4.请求到one 发送 60BC939E156DBB21676BCC02EC0C7E2A 却返回E090DA9B2ABA7B7D08915DF78EDFA31E
5.请求到two 发送 E090DA9B2ABA7B7D08915DF78EDFA31E 却返回96BA8FCD5305A75ECC0684F9D541EBDE
现在请求 /getSession
1.请求分配到one 携带cookie 96BA8FCD5305A75ECC0684F9D541EBDE
返回值为 I am One .You session is :null
返回set cookie值为95833D8765EB2818B220CA9AC53DA72C
2.请求分配到two 携带cookie 95833D8765EB2818B220CA9AC53DA72C
返回值为I am Two .You session is : null
返回set cookie值 6BFB1D62A6B3925A1774CC51C3B21893
现在我们应该清楚了分布式系统中Session的问题了吧,就是不清楚用户的请求会落地在那一台服务器上。而对于Session是默认仅保存在你请求落地的那台服务器上的,对于下一次的请求,如果落地在非上一个请求落地的服务器上,则没有Session信息。
分布式Session的解决方法之Session集中式管理
Session集中式管理就是将所以用户的Session统一到一台中央服务器上(也有可能这个中央服务器也搭建成分布式缓存集群)。
对于我们上述项目,现在已经有三台服务器了。一台Nginx负载均衡服务器。两台提供Rest接口的服务器。现在再加一台Session缓存服务器。让两台Rest接口服务器把Session全部交给Session缓存服务器来保持。
具体的实现我们使用Spring-Session,Session缓存保存于Redis中
首先启动一个redis的Dcoker镜像。查看ip为192.168.110.5
Maven中添加到Redis的依赖:
SpringBoot中的配置:
其实可以在properties配置文件中加入如下配置;
@EnableRedisHttpSession这个注解就是最重要的东西,加了它之后,spring生产一个新的拦截器,用来实现Session共享的操作,具体实现这里暂不展开。而配置的这个JedisConnectionFactory,则是让Spring根据配置文件中的配置连到Redis。
现在继续之前的步骤,发生Session问题即可解决。
因为无论最后请求落地到哪一台服务器,根据Session共享服务器只返回一个Session给浏览器,而浏览器也永远只携带了一个cookie值
此时我们查看Redis数据库:
其中一个为Session失效时间,一个则是前面我们说的cookie值。
这篇文章将搭建出一个”分布式”系统,并先体验分布式系统中Session管理的问题,并通过集中Session管理方案解决
构建两个SpringBoot镜像,提供Session服务。
先使用SpringBoot搭建构建一个Restful 接口,并做一些很简单的Session操作。Maven:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.0.RELEASE</version> <relativePath></relativePath> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
然后写一个比较简单的Controller:
@SpringBootApplication @RestController public class App{ @RequestMapping("/setSession") public String home(HttpServletRequest request) { request.getSession().setAttribute("user","jack"); return "I am One .You session has k-v user-wang!!"+request.getSession().getAttribute("user"); } @RequestMapping("/getSession") public String session(HttpServletRequest request){ return "I am One .You session is :"+request.getSession().getAttribute("user"); } public static void main(String[] args) { SpringApplication.run(App.class, args); } }
现在一个SpringBoot项目就写完了。它只提供了两个Rest接口。一个设置Session值,一个获取Session值。注意一点,上面代码中返回值中有个One的标识。现在我们将其Docker化:
先通过mvn package将其打成jar包,命名为dockerOne.jar
编写Dockerfile
生成镜像并运行
Dockerfile:
FROM registry.cn-hangzhou.aliyuncs.com/alirobot/oraclejdk8-nscd #基础源 VOLUME /tmp #挂载 add dockerOne.jar app.jar #传递jar包 ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"] #运行命令
然后运行 sudo docker build -t dockerone/jdk8 . 构建第一个镜像。
按上面步骤构建第二个镜像:代码中one改为two。镜像名称也改为dockertwo/jdk8。
启动上面两个镜像,通过inspect查看ip为192.168.110.2 192.168.110.3 (docker网桥为192.168.110.1)
构建Nginx并搭建负载均衡
docker pull 下载一个nginx后。我们在本机上写一个nginx的配置文件,然后通过 -v 已数据卷的形式挂载到docker镜像中。#使用轮询法做负载均衡,权重都是1,这样请求可以随机分配。 upstream webservers{ server 192.168.110.3:8080 weight=1; server 192.168.110.2:8080 weight=1; } server { listen 80; server_name localhost; #charset koi8-r; #access_log /var/log/nginx/log/host.access.log main; location =/index.html{ root /data; } location / { proxy_pass http://webservers; } }
启动Nginx并查看ip为 192.168.110.4
访问192.168.110.4并观察浏览器携带的cookie及Session的返回值
我们知道,第一次访问网站的时候(也就是该用户还没有创建Session的时候),服务器的response会携带一个set-cookie的值随机值。用来表示该用户的Session。当该用户下次访问网站则会在request中携带一个cookie的头信息,带入该cookie。这样服务器就能在内存中找到该用户的唯一Session。首先请求/setSession
1.请求分配到two服务器: 没有携带cookie值,服务器符合 E5DD28A3B255AEB9B95C2C5304342685
2.请求分配到one服务器: 携带cookie E5DD28A3B255AEB9B95C2C5304342685 却返回set-cookie 75965AEBC91A57B509BDE095602B9F97
3.请求到two 发送 75965AEBC91A57B509BDE095602B9F97 却返回60BC939E156DBB21676BCC02EC0C7E2A
4.请求到one 发送 60BC939E156DBB21676BCC02EC0C7E2A 却返回E090DA9B2ABA7B7D08915DF78EDFA31E
5.请求到two 发送 E090DA9B2ABA7B7D08915DF78EDFA31E 却返回96BA8FCD5305A75ECC0684F9D541EBDE
现在请求 /getSession
1.请求分配到one 携带cookie 96BA8FCD5305A75ECC0684F9D541EBDE
返回值为 I am One .You session is :null
返回set cookie值为95833D8765EB2818B220CA9AC53DA72C
2.请求分配到two 携带cookie 95833D8765EB2818B220CA9AC53DA72C
返回值为I am Two .You session is : null
返回set cookie值 6BFB1D62A6B3925A1774CC51C3B21893
现在我们应该清楚了分布式系统中Session的问题了吧,就是不清楚用户的请求会落地在那一台服务器上。而对于Session是默认仅保存在你请求落地的那台服务器上的,对于下一次的请求,如果落地在非上一个请求落地的服务器上,则没有Session信息。
分布式Session的解决方法之Session集中式管理
Session集中式管理就是将所以用户的Session统一到一台中央服务器上(也有可能这个中央服务器也搭建成分布式缓存集群)。
对于我们上述项目,现在已经有三台服务器了。一台Nginx负载均衡服务器。两台提供Rest接口的服务器。现在再加一台Session缓存服务器。让两台Rest接口服务器把Session全部交给Session缓存服务器来保持。
具体的实现我们使用Spring-Session,Session缓存保存于Redis中
首先启动一个redis的Dcoker镜像。查看ip为192.168.110.5
Maven中添加到Redis的依赖:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.0.RELEASE</version> <relativePath></relativePath> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session</artifactId> <version>1.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> <version>1.2.2.RELEASE</version> <type>pom</type> </dependency> </dependencies>
SpringBoot中的配置:
@SpringBootApplication @RestController @EnableRedisHttpSession public class App{ @RequestMapping("/") public String home(HttpServletRequest request) { request.getSession().setAttribute("user","xi"); return "I am Two .You session has k-v user-wang!!"+request.getSession().getAttribute("user"); } @RequestMapping("/getSession") public String session(HttpServletRequest request){ return "I am Two .You session is :"+request.getSession().getAttribute("user"); } public static void main(String[] args) { SpringApplication.run(App.class, args); } #缓存服务器的配置 @Bean public JedisConnectionFactory connectionFactory() { JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(); redisConnectionFactory.setUsePool(false); redisConnectionFactory.setHostName("192.168.110.2"); redisConnectionFactory.setPort(6379); return redisConnectionFactory; } }
其实可以在properties配置文件中加入如下配置;
spring.redis.host=192.168.110.5 spring.redis.port=6379
@EnableRedisHttpSession这个注解就是最重要的东西,加了它之后,spring生产一个新的拦截器,用来实现Session共享的操作,具体实现这里暂不展开。而配置的这个JedisConnectionFactory,则是让Spring根据配置文件中的配置连到Redis。
现在继续之前的步骤,发生Session问题即可解决。
因为无论最后请求落地到哪一台服务器,根据Session共享服务器只返回一个Session给浏览器,而浏览器也永远只携带了一个cookie值
此时我们查看Redis数据库:
dev@dev:~$ redis-cli -h 192.168.110.5 192.168.110.5:6379> keys * 1) "spring:session:sessions:96b04dd3-4045-435f-ac2b-935c2af5eb52" 3) "spring:session:expirations:1484127300000" 4) "spring:session:sessions:expires:96b04dd3-4045-435f-ac2b-935c2af5eb52" xxxx
其中一个为Session失效时间,一个则是前面我们说的cookie值。
相关文章推荐
- 用 Docker 构建、运行、发布来一个 Spring Boot 应用
- spring Boot 应用通过Docker 来实现构建、运行、发布流程
- 用 Docker 构建、运行、发布来一个 Spring Boot 应用
- 用_Docker、Gradle_来构建、运行、发布一个_Spring_Boot_应用
- 用 Docker 构建、运行、发布一个 Spring Boot 应用
- Windows环境下Spring Boot+Docker构建应用
- 用 Docker 构建、运行、发布来一个 Spring Boot 应用
- 用 Docker 构建、运行、发布来一个 Spring Boot 应用
- 第一个 spring Boot 应用通过Docker 来实现构建、运行、发布
- 用 Docker 构建、运行、发布一个 Spring Boot 应用
- 第一个 spring Boot 应用通过Docker 来实现构建、运行、发布
- 用 Docker、Gradle 来构建、运行、发布一个 Spring Boot 应用
- 用 Docker 构建、运行、发布一个 Spring Boot 应用
- 第一个SpringBoot web 应用使用maven构建Docker镜像并容器化运行
- [Docker]构建、运行、发布一个 Spring Boot 应用
- 用 Docker 构建、运行、发布一个 Spring Boot 应用
- Spring boot 与 Docker-compose构建微服务应用
- 使用maven集成docker构建和部署Spring Boot 应用
- Spring Boot 构建应用——整合 Mybatis