社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
由于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";
}
}
引入依赖,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,观察console输出内容。
可以看到session当中没有值,browser被保存到session中,查看redis,发现session数据已经被缓存到redis。
解析一下这个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
现在dev1也能正常访问到session内容。
代码位置:
https://github.com/39627020/experience/tree/master/java/springboot/hellospring
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!