使用Spring session实现分布式应用session共享 系统管理员 2022-06-08 10:16 212阅读 0赞 ## Session与Cookie基础 ## 由于http协议是无状态的协议,为了能够记住请求的状态,于是引入了Session和Cookie的机制。我们应该有一个很明确的概念,那就是Session是存在于服务器端的,在单体式应用中,他是由tomcat管理的,存在于tomcat的内存中,当我们为了解决分布式场景中的session共享问题时,引入了redis,其共享内存,以及支持key自动过期的特性,非常契合session的特性,我们在企业开发中最常用的也就是这种模式。但是只要你愿意,也可以选择存储在JDBC,Mongo中,这些,spring都提供了默认的实现,在大多数情况下,我们只需要引入配置即可。而Cookie则是存在于客户端,更方便理解的说法,可以说存在于浏览器。 ## 代码示例 ## 使用Springboot编写一个非常简单的服务端,来加深对其的理解。需求很简单,当浏览器访问localhost:8000/test/cookie?browser=xxx时,如果没有获取到session,则将request中的browser存入session;如果获取到session,便将session中的browser值输出。顺便将request中的所有cookie打印出来。 创建一个hellospring项目。 @Controller public class CookieController { @RequestMapping("/test/cookie") public String cookie(@RequestParam("browser") String browser, HttpServletRequest request, HttpSession session) { //取出session中的browser Object sessionBrowser = session.getAttribute("browser"); if (sessionBrowser == null) { System.out.println("不存在session,设置browser=" + browser); session.setAttribute("browser", browser); } else { System.out.println("存在session,browser=" + sessionBrowser.toString()); } Cookie[] cookies = request.getCookies(); if (cookies != null && cookies.length > 0) { for (Cookie cookie : cookies) { System.out.println(cookie.getName() + " : " + cookie.getValue()); } } return "index"; } } ## 使用Redis集成Spring Session ## 引入依赖,Spring Boot的版本采用1.5.6 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> 配置类开启Redis Http Session @Configuration @EnableRedisHttpSession public class HttpSessionConfig { } 为了方便演示多节点之间的session共享,我们生成多个配置文件。 application.properties spring.redis.host=172.17.0.61 spring.redis.port=7200 spring.redis.database=0 spring.application.name=hellospring application-default.properties server.port=8001 application-dev1.properties server.port=8001 application-dev2.properties server.port=8002 进入cmd模式,在项目执行mvn package。 分别启动dev1和dev2。 java -jar hellospring-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev1 java -jar hellospring-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev2 安装NGINX,将请求分发到两个不同节点,配置文件如下。 listen 8000; server_name localhost; #charset koi8-r; #access_log logs/host.access.log main; location / { proxy_pass http://localhost; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } upstream localhost{ #hash $remote_addr consistent; server 127.0.0.1:8001 weight=1; server 127.0.0.1:8002 weight=1; } 访问[http://localhost:8000/test/cookie?browser=chrome][http_localhost_8000_test_cookie_browser_chrome],观察console输出内容。 ![这里写图片描述][SouthEast] 可以看到session当中没有值,browser被保存到session中,查看redis,发现session数据已经被缓存到redis。 ![这里写图片描述][SouthEast 1] 解析一下这个redis store。 1 spring:session是默认的Redis HttpSession前缀(redis中,我们常用’:’作为分割符)。 2 每一个session都会有三个相关的key,第三个key最为重要,它是一个HASH数据结构,将内存中的session信息序列化到了redis中。如上文的browser,就被记录为sessionAttr:browser=chrome,还有一些meta信息,如创建时间,最后访问时间等。 3 另外两个key,expirations:1504446540000和sessions:expires:7079…我发现大多数的文章都没有对其分析,前者是一个SET类型,后者是一个STRING类型,可能会有读者发出这样的疑问,redis自身就有过期时间的设置方式TTL,为什么要额外添加两个key来维持session过期的特性呢?这需要对redis有一定深入的了解才能想到这层设计。当然这不是本节的重点,简单提一下:redis清除过期key的行为是一个异步行为且是一个低优先级的行为,用文档中的原话来说便是,可能会导致session不被清除。于是引入了专门的expiresKey,来专门负责session的清除,包括我们自己在使用redis时也需要关注这一点。在开发层面,我们仅仅需要关注第三个key就行了。 再次访问[http://localhost:8000/test/cookie?browser=chrome][http_localhost_8000_test_cookie_browser_chrome] ![这里写图片描述][SouthEast 2] 现在dev1也能正常访问到session内容。 代码位置: [https://github.com/39627020/experience/tree/master/java/springboot/hellospring][https_github.com_39627020_experience_tree_master_java_springboot_hellospring] [http_localhost_8000_test_cookie_browser_chrome]: http://localhost:8000/test/cookie?browser=chrome [SouthEast]: /images/20220608/76957134440948a99aae947bf35717dd.png [SouthEast 1]: /images/20220608/d34f0ece9a4e4c23b2f74735bf0a2dbf.png [SouthEast 2]: /images/20220608/90a26ed983fe4a1a988a87d16e0208c8.png [https_github.com_39627020_experience_tree_master_java_springboot_hellospring]: https://github.com/39627020/experience/tree/master/java/springboot/hellospring
还没有评论,来说两句吧...