面試官都愛問的分佈式Session實現,你都知道哪些方案?

面試官心理分析

面試官問了你一堆 dubbo 是怎麼玩兒的,你會玩兒 dubbo 就能夠把單塊系統弄成分佈式系統,而後分佈式以後接踵而來的就是一堆問題,最大的問題就是分佈式事務、接口冪等性、分佈式鎖,還有最後一個就是分佈式 sessionweb

固然了,分佈式系統中的問題何止這麼一點,很是之多,複雜度很高,這裏只是說一下常見的幾個問題,也是面試的時候常問的幾個。面試

面試題剖析

session 是啥?瀏覽器有個 cookie,在一段時間內這個 cookie 都存在,而後每次發請求過來都帶上一個特殊的 jsessionid cookie,就根據這個東西,在服務端能夠維護一個對應的 session 域,裏面能夠放點數據。redis

通常的話只要你沒關掉瀏覽器,cookie 還在,那麼對應的那個 session 就在,可是若是 cookie 沒了,session 也就沒了。常見於什麼購物車之類的東西,還有登陸狀態保存之類的。spring

這個很少說了,懂 Java 的都該知道這個。數據庫

單塊系統的時候這麼玩兒 session 沒問題,可是你要是分佈式系統呢,那麼多的服務,session 狀態在哪兒維護啊?瀏覽器

其實方法不少,可是常見經常使用的是如下幾種tomcat

徹底不用 session

使用 JWT Token 儲存用戶身份,而後再從數據庫或者 cache 中獲取其餘的信息。這樣不管請求分配到哪一個服務器都無所謂。服務器

tomcat + redis

這個其實還挺方便的,就是使用 session 的代碼,跟之前同樣,仍是基於 tomcat 原生的 session 支持便可,而後就是用一個叫作TomcatRedisSessionManager 的東西,讓全部咱們部署的 tomcat 都將 session 數據存儲到 redis 便可。cookie

在 tomcat 的配置文件中配置:session

<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"/>

而後指定 redis 的 host 和 port 就 ok 了。

<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 中配置:

<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 配置文件中配置:

<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 中配置:

<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>

示例代碼:

@RestController
@RequestMapping("/test")
public class TestController {

    @RequestMapping("/putIntoSession")
    public String putIntoSession(HttpServletRequest request, String username) {
        request.getSession().setAttribute("name",  "leo");
        return "ok";
    }

    @RequestMapping("/getFromSession")
    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 來實現。

相關文章
相關標籤/搜索