面試官心理分析

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

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

面試題剖析

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

一般的話只要你沒關掉瀏覽器,cookie 還在,那麼對應的那個 session 就在,但是如果 cookie 沒了,session 也就沒了。常見於什麼購物車之類的東西,還有登錄狀態保存之類的。

這個不多說了,懂 Java 的都該知道這個。

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

其實方法很多,但是常見常用的是以下幾種

完全不用 session

使用 JWT Token 儲存用戶身份,然後再從資料庫或者 cache 中獲取其他的信息。這樣無論請求分配到哪個伺服器都無所謂。

tomcat + redis

這個其實還挺方便的,就是使用 session 的代碼,跟以前一樣,還是基於 tomcat 原生的 session 支持即可,然後就是用一個叫做TomcatRedisSessionManager 的東西,讓所有我們部署的 tomcat 都將 session 數據存儲到 redis 即可。

在 tomcat 的配置文件中配置:

<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 來實現。

贈送面試&學習福利資源

最近在網上發現一個不錯的 PDF 資源《Java 核心面試知識.pdf》分享給大家,不光是面試,學習,你都值得擁有!!!

資料獲取方式:點贊,點贊,點贊+關注我私信我【愛學習】獲取免費資源鏈接!!!

目錄以及部分截圖:

有需要的可以私信我【愛學習】獲取免費資源鏈接!!!

推薦閱讀:
相关文章