做 Java Web 開發的人多數都會需要使用到 session (會話),我們使用 session 來保存一些需要在兩個不同的請求之間共享數據。一般 Java 的 Web 容器像 Tomcat、Resin、Jetty 等等,它們會在內存中保存 session 數據。這樣做會有兩個不足:
- 服務重啓後 session 數據丟失
- 應用做集羣部署的時候,不同的節點無法共享 session 數據
我們以使用比例最高的 Tomcat 爲例,針對第二個問題 Tomcat 提供了集羣 session 複製的解決方案,詳情請看官方文檔。看完文檔你會發現 Tomcat 自帶的方法配置非常複雜,而且它沒有解決第一個問題 —— 服務重啓導致 session 數據丟失的問題。
現在還有另外一種方案就是使用 memcached 或者是 redis 來存儲 session 數據,於是就有了這麼一些開源項目:
- https://www.oschina.net/p/tomcat-redis-session-manager
- https://www.oschina.net/p/redis-manager
- https://www.oschina.net/p/memcached-session-manager
這些開源項目使用和配置都比較簡單,而且對應用完全透明,只需要在 server.xml 中配置好 Manager 即可。例如:
<Context>
...
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:host1.yourdomain.com:11211,n2:host2.yourdomain.com:11211"
sticky="false"
sessionBackupAsync="false"
lockingMode="uriPattern:/path1|/path2"
requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>
</Context>
但是這幾個開源項目都有一個最根本的問題沒法解決,當存放在 session 中的數據量比較大,而且讀取 session 數據非常頻繁時會導致 memcached 或者是 redis 吞吐量受帶寬限制使得性能變得非常差。逼迫你必須通過 memcached 或者 redis 的擴容和集羣來解決問題,大大的增加了硬件投入的成本和運維的成本。
--------
那麼用 J2Cache 的 session-manager 模塊就可以有效解決前面提到的所有問題。J2Cache 在 redis 的基礎上引入了內存緩存的概念,可以確保服務重啓後 session 數據不會丟失,其次極大的降低了 redis 的數據吞吐量,保證在高併發情況下依然有很好的性能表現。
與前面提到的幾個開源項目不同,J2Cache 的 session manager 採用 Filter 方式實現,支持各種 Java 的 Web 容器服務。
使用方法:
1. Web 項目添加 J2Cache 的 session-manager 模塊依賴:
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-session-manager</artifactId>
<version>1.0.0-beta4</version>
</dependency>
2. 在 web.xml 中配置 Filter:
<filter>
<filter-name>j2cache-session-filter</filter-name>
<filter-class>net.oschina.j2cache.session.J2CacheSessionFilter</filter-class>
<init-param><!-- 內存中存放會話數 -->
<param-name>session.maxSizeInMemory</param-name>
<param-value>2000</param-value>
</init-param>
<init-param><!-- 會話有效期,單位:秒鐘 -->
<param-name>session.maxAge</param-name>
<param-value>1800</param-value>
</init-param>
<!-- cookie configuration -->
<init-param>
<param-name>cookie.name</param-name>
<param-value>J2CACHE_SESSION_ID</param-value>
</init-param>
<init-param>
<param-name>cookie.path</param-name>
<param-value>/</param-value>
</init-param>
<init-param>
<param-name>cookie.domain</param-name>
<param-value></param-value>
</init-param>
<!-- redis configuration -->
<init-param>
<param-name>redis.mode</param-name>
<param-value>single</param-value>
</init-param>
<init-param>
<param-name>redis.hosts</param-name>
<param-value>127.0.0.1:6379</param-value>
</init-param>
<init-param>
<param-name>redis.channel</param-name>
<param-value>j2cache</param-value>
</init-param>
<init-param>
<param-name>redis.cluster_name</param-name>
<param-value>j2cache</param-value>
</init-param>
<init-param>
<param-name>redis.timeout</param-name>
<param-value>2000</param-value>
</init-param>
<init-param>
<param-name>redis.password</param-name>
<param-value></param-value>
</init-param>
<init-param>
<param-name>redis.database</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>redis.maxTotal</param-name>
<param-value>100</param-value>
</init-param>
<init-param>
<param-name>redis.maxIdle</param-name>
<param-value>10</param-value>
</init-param>
<init-param>
<param-name>redis.minIdle</param-name>
<param-value>1</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>j2cache-session-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3. 啓動應用,即可開啓分佈式 session 存儲。
當重啓 Web 應用時,內存中的 session 數據爲空,會自動從 redis 中讀取重啓前保存的 session 數據。所有節點又通過 redis 進行 session 數據的共享,當有 session 數據更新時會通過 Redis Pub/Sub 來通知其他節點重新從 Redis 中讀取數據,確保不同節點的 session 數據是一致的。
sesson-manager 模塊代碼 https://gitee.com/ld/J2Cache/tree/master/modules/session-manager
很簡單吧? 去 https://gitee.com/ld/J2Cache 點個贊吧?