forked from doocs/advanced-java
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs(distributed-session): add distributed-session.md
分布式会话的两种实现方式
- Loading branch information
Showing
3 changed files
with
130 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
## 面试题 | ||
集群部署时的分布式 session 如何实现? | ||
|
||
## 面试官心理分析 | ||
面试官问了你一堆 dubbo 是怎么玩儿的,你会玩儿 dubbo 就可以把单块系统弄成分布式系统,然后分布式之后接踵而来的就是一堆问题,最大的问题就是**分布式事务**、**接口幂等性**、**分布式锁**,还有最后一个就是**分布式session**。 | ||
|
||
当然了,分布式系统中的问题何止这么一点,非常之多,复杂度很高,但是这里就是说下常见的几个,也是面试的时候常问的几个。 | ||
|
||
## 面试题剖析 | ||
session 是啥?浏览器有个 cookie,在一段时间内这个 cookie 都存在,然后每次发请求过来都带上一个特殊的 jsessionid cookie,就根据这个东西,在服务端可以维护一个对应的 session 域,里面可以放点数据。 | ||
|
||
一般只要你没关掉浏览器,cookie 还在,那么对应的那个 session 就在,但是 cookie 没了,session 就没了。常见于什么购物车之类的东西,还有登录状态保存之类的。 | ||
|
||
这个不多说了,懂 Java 的都该知道这个。 | ||
|
||
但是你单块系统的时候这么玩儿 session 没问题啊,但是你要是分布式系统了呢,那么多的服务,session 状态在哪儿维护啊? | ||
|
||
其实方法很多,但是常见常用的是两种: | ||
|
||
### tomcat + redis | ||
这个其实还挺方便的,就是使用 session 的代码跟以前一样,还是基于 tomcat 原生的 session 支持即可,然后就是用一个叫做 Tomcat RedisSessionManager 的东西,让所有我们部署的 tomcat 都将 session 数据存储到 redis 即可。 | ||
|
||
在 tomcat 的配置文件中,配置一下: | ||
|
||
```xml | ||
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" /> | ||
|
||
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager" | ||
host="{redis.host}" | ||
port="{redis.port}" | ||
database="{redis.dbnum}" | ||
maxInactiveInterval="60"/> | ||
``` | ||
|
||
搞一个类似上面的配置即可,你看是不是就是用了 RedisSessionManager,然后指定了 redis 的 host 和 port 就 ok 了。 | ||
```xml | ||
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" /> | ||
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager" | ||
sentinelMaster="mymaster" | ||
sentinels="<sentinel1-ip>:26379,<sentinel2-ip>:26379,<sentinel3-ip>:26379" | ||
maxInactiveInterval="60"/> | ||
``` | ||
|
||
还可以用上面这种方式基于 redis 哨兵支持的 redis 高可用集群来保存 session 数据,都是 ok 的。 | ||
|
||
### spring session + redis | ||
|
||
分布式会话的这个东西重耦合在 tomcat 中,如果我要将 web 容器迁移成 jetty,难道还要重新把 jetty 都配置一遍? | ||
|
||
因为上面那种 tomcat + redis 的方式好用,但是会**严重依赖于web容器**,不好将代码移植到其他 web 容器上去,尤其是你要是换了技术栈咋整?比如换成了 spring cloud 或者是 spring boot 之类的。还得好好思忖思忖。 | ||
|
||
所以现在比较好的还是基于 java 一站式解决方案,spring 了。人家 spring 基本上包掉了大部分我们需要使用的框架了,spirng cloud 做微服务了,spring boot 做脚手架了,所以用 sping session 是一个很好的选择。 | ||
|
||
在 pom.xml 中配置: | ||
```xml | ||
<dependency> | ||
<groupId>org.springframework.session</groupId> | ||
<artifactId>spring-session-data-redis</artifactId> | ||
<version>1.2.1.RELEASE</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>redis.clients</groupId> | ||
<artifactId>jedis</artifactId> | ||
<version>2.8.1</version> | ||
</dependency> | ||
``` | ||
|
||
spring 配置文件: | ||
```xml | ||
<bean id="redisHttpSessionConfiguration" | ||
class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"> | ||
<property name="maxInactiveIntervalInSeconds" value="600"/> | ||
</bean> | ||
|
||
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> | ||
<property name="maxTotal" value="100" /> | ||
<property name="maxIdle" value="10" /> | ||
</bean> | ||
|
||
<bean id="jedisConnectionFactory" | ||
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy"> | ||
<property name="hostName" value="${redis_hostname}"/> | ||
<property name="port" value="${redis_port}"/> | ||
<property name="password" value="${redis_pwd}" /> | ||
<property name="timeout" value="3000"/> | ||
<property name="usePool" value="true"/> | ||
<property name="poolConfig" ref="jedisPoolConfig"/> | ||
</bean> | ||
``` | ||
|
||
在 web.xml 中配置: | ||
```xml | ||
<filter> | ||
<filter-name>springSessionRepositoryFilter</filter-name> | ||
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> | ||
</filter> | ||
<filter-mapping> | ||
<filter-name>springSessionRepositoryFilter</filter-name> | ||
<url-pattern>/*</url-pattern> | ||
</filter-mapping> | ||
``` | ||
|
||
示例代码: | ||
```java | ||
@Controller | ||
@RequestMapping("/test") | ||
public class TestController { | ||
|
||
@RequestMapping("/putIntoSession") | ||
@ResponseBody | ||
public String putIntoSession(HttpServletRequest request, String username) { | ||
request.getSession().setAttribute("name", “leo”); | ||
|
||
return "ok"; | ||
} | ||
|
||
@RequestMapping("/getFromSession") | ||
@ResponseBody | ||
public String getFromSession(HttpServletRequest request, Model model){ | ||
String name = request.getSession().getAttribute("name"); | ||
return name; | ||
} | ||
} | ||
``` | ||
|
||
|
||
上面的代码就是 ok 的,给 sping session 配置基于 redis 来存储 session 数据,然后配置了一个 spring session 的过滤器,这样的话,session 相关操作都会交给 spring session 来管了。接着在代码中,就用原生的 session 操作,就是直接基于 spring sesion 从 redis 中获取数据了。 | ||
|
||
实现分布式的会话,有很多种很多种方式,我说的只不过比较常见的两种方式,tomcat + redis 早期比较常用,但是会重耦合到 tomcat 重;近些年,通过 spring session 来实现。 |