項目升級分佈式,由內到外兩種方式解決分佈式session共享問題|週末學習

這是我參與更文挑戰的第6天,活動詳情查看: 更文挑戰php

文章正文第一句:本文已參與週末學習計劃,點擊連接查看詳情html

javaweb中咱們項目稍微正規點java

javaweb中咱們項目稍微正規點,都會用到單點登陸這個技術。實現它的方法各家有各界的見解。這幾天因爲公司項目需求正在研究。下面整理一下最近整理的心得。
複製代碼

簡介

  • 在分佈式項目中另咱們頭疼的是多項目之間的數據共享(即Session共享),常常會出現數據丟失的狀況。爲了解決這種Bug。前人已經把咱們實現了兩種解決的辦法。今天我在這裏一下這兩種方式。側重偏向第二種方法

配tomcat實現session共享


配置Tomcat

  • 下載好上面的jar包,把他放在tomcat的lib文件下。

這裏寫圖片描述

  • 而後修改Tomcat裏的conf/context.xml文件
<Valve className="com.orangefunction.tomcat.redissessions.RedisSessionHandlerValve" />  
<Manager className="com.orangefunction.tomcat.redissessions.RedisSessionManager"  
host="192.168.1.130"   
port="7006"   
database="db0"   
maxInactiveInterval="60" />
複製代碼
  • 上面配置的host和port就是咱們redis的host和port,因此在此以前咱們須要先開啓一個redis服務出來。

點我查看如何配置redis(本文配置集羣,配置單節點同樣) 點我查看如何配置redis和spring的整合 點我查看如何配置redis中你遇到的問題彙總nginx

  • database就是講該tomcat與瀏覽器會話時產生的session存放在redis中的位置

這裏寫圖片描述

  • maxInactiveInterval 就是設置緩存的過時時間。可是在實際測試中我發現這個屬性並無發生做用。(你有啥看法歡迎點評)

到這裏咱們的第一個Tomcat配置完成。下面就是重複上面的步驟多配製幾個Tomcatweb

配置nginx

  • nginx功能豐富,能夠做爲HTTP服務器,也能夠做爲反向代理的服務器,郵件服務器。支持FastCGI、SSL、Virtual Host、URL Rewrite、Gzip等功能。而且支持不少第三方的模塊擴展。redis

  • nginx的下載官網有現成的,直接下載符合本身電腦的版本傻瓜式安裝就好了。(安裝或者解壓不要出現中文)spring

  • 找到conf/nginx.conf文件在裏面修改設置瀏覽器

-  listen       802: 咱們監聽的端口
- server_name  192.168.1.130:監聽的地址
- proxy_pass http://mynginxserver:根據狀況跳轉最後的鏈接
- 在mynginxserver配置多個Tomcat:
複製代碼
upstream mynginxserver {
	        server 192.168.1.78:8080 weight=1;
	        server 192.168.1.130:8080 weight=1;
        }
複製代碼
  • 完成配置
upstream mynginxserver {
        server 192.168.1.78:8080 weight=1;
        server 192.168.1.130:8080 weight=1;
    }
	
    server {
        listen       802;
        server_name  192.168.1.130;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
			proxy_pass http://mynginxserver;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }
複製代碼
  • 這個時候當咱們兩個Tomcat都在運行狀態咱們瀏覽器中輸入
192.168.1.130:802
複製代碼
  • nginx就會隨機選擇一個Tomcat來運行了。同時這兩個Tomcat的session是共享的。就是說A Tomcat的session B 能夠用。就算A 宕機了B也照樣按到A的session。緩存


配spring session實現session共享

  • 上面的一種方式我也是在網上案列操做的。在本身的項目中並不適合。由於個人項目中配置的是redis集羣(非sentinel).因此在Tomcat裏面配置是不行的。

解釋:第一種方式配置的redis默認是單節點的redis。也就是咱們的session會產生sessionID做爲redis中key存在咱們配置redis節點的庫上面。可是若是咱們是redis集羣的話,集羣會將根據key值算出slot值,在根據slot值決定存取在哪一臺redis服務商。也就是說在存session前咱們根本不知道會存在哪一臺redis上。因此這種方法不適合於redis集羣。tomcat

具體看看我畫的思惟導圖吧:

這裏寫圖片描述

第二章明顯出錯了,事實上並無存儲在實現約定的redis上

這裏寫圖片描述


spring session 配置

  • 說了這麼多了,咱們下面開始講解spring中集成的session共享配置。首先若是你是maven項目那麼直接引入jar
<dependency>													<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
<version>1.1.1.RELEASE</version>
</dependency>
複製代碼
  • 若果你不是maven項目,那麼就麻煩你本身去下載這些相關的jar包。
spring-data-redis.jar
redis.clients.jar
spring-session.jar
複製代碼
  • 一樣這裏你須要一下幾篇文章來配置redis集羣

點我查看如何配置redis 點我查看如何配置redis和spring的整合 點我查看如何配置redis中你遇到的問題彙總

spring配置文件配置

  • 有了上面的準備資料後,咱們能夠在是spring的配置文件中配置spring session了

  • 很簡單咱們須要引入spring session中的bean

<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"> 
複製代碼
  • 在spring的配置文件中咱們只須要配置這一個bean就能夠了。下面離將session共享到redis集羣上只差一步了。就是在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>
複製代碼
  • 到這裏配置完成了,咱們能夠重啓咱們的項目運行一下存取session的部分。而後再去redis集羣找看看有沒有session。在redis中默認的session的key是spring:session:sessions:c0d1fadd-b04a-4244-9525-0f13ea7173bf。其中後面一串id就是咱們Tomcat和瀏覽器會話時差生的sessionid。

這裏寫圖片描述

問題

  • 在上面咱們配置spring session 已經能夠實現了session共享了。可是細心的朋友能夠發現。session並無真正意義上的共享。上面咱們能夠看到session在redis集羣中的key包含了Tomcat和瀏覽器的sessionid。也就是說redis上這個session只能是個人這個Tomcat在該瀏覽器上才能夠訪問到。別的Tomcat就算和同一個瀏覽器交互也是拿不到redis上這個session的。這種效果是大家想要的嗎。答案並非。上面實現的效果我用一幅圖描述一下。

這裏寫圖片描述

  • 也就是說上面個人配置只是將Tomcat的session轉移到了redis上。多服務的session仍然是各區各的。這是一個大坑。坑了很久。而後我想了另一種辦法如今能夠解決這個侷限性。

解決辦法

  • 解決上面的問題。這裏我提供三種思路。三種思路我都實現了一下。各有利弊。最後決定採用第三種比較靠譜。

1-- 重寫spring session源碼,重寫裏面講session存儲在redis那部分代碼,主要就是爲了將redis 中session的key寫成固定值。之後咱們在去session中取這個固定的key就能夠實現session共享了。

2--只重寫spring session中咱們的session倉庫中去的策略,就是在A項目登陸後將產生的sessionID傳遞給B項目。B項目在根據這個sessionid去redis中獲取。

3-- 在向session中存值的地方,將sessionid做爲value,請求頭中的Host和Agent組合最爲key存儲在redis上。而後咱們在spring session去session的方法裏在根據Host和Agent組成的key去獲取sessionid,而後就能夠獲取對應的session了。

存取策略重構

  • 這裏主要講解一下第三種的實現步驟。首先我新建一個類用來讀取request求情頭中的host和agent拼接的key值。
public static Map<String, Object> getInfoFromUrl(HttpServletRequest request)
    {
        Map<String, Object> resultMap=new HashMap<String, Object>();
        String Agent = request.getHeader("User-Agent"); 
        String Host = request.getHeader("Host"); 
        resultMap.put("agent", Agent);
        resultMap.put("host", Host);
        return resultMap;
    } 
    
    public static String getKeyFromUrl(HttpServletRequest request)
    {
        String result="";
        Map<String, Object> map = getInfoFromUrl(request);
        Set<String> keySet = map.keySet();
        for (String key : keySet)
        {
            result+=map.get(key).toString();
        }
        return result;
    }
複製代碼
  • 而後在咱們調用session存值的地方,也就是登陸的地方。經過咱們項目中的redis操做類將其存儲起來。

這裏寫圖片描述

  • 而後咱們修改spring session中的cookie倉庫中的去request的sessionid的策略

這裏寫圖片描述

  • 最後咱們在spring的配置中修改是spring session的策略爲我麼你的cook策略
<bean id="redisCacheTemplate" class="com.bshinfo.web.base.cache.RedisCacheTemplate"/>
    <bean id="zxh" class="com.bshinfo.web.base.cache.CookieHttpSessionStrategyTest">
    	<property name="redisCacheTemplate" ref="redisCacheTemplate" />
    </bean>
 	<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"> 
 		<property name="httpSessionStrategy" ref="zxh"/>
 		<property name="maxInactiveIntervalInSeconds" value="60"/>
 	</bean> 
複製代碼
  • 最後就實現了咱們的效果。效果就是A項目在X瀏覽器上登陸後。B項目在X瀏覽器獲取session的用戶能夠正常獲取。不然獲取失敗。
相關文章
相關標籤/搜索