项目升级分布式,由内到外两种方式解决分布式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的用户能够正常获取。不然获取失败。
相关文章
相关标签/搜索