项目中的单体应用使用的session共享机制是基于memcached做的同步复制,在做登录的重构的时候,发现了一个自动退出登录的问题,所谓自动退登,就是用户在未使用系统的一段时间后登录态丢失,需要重新进行一次登录操作,这是一种正常现象,但是如果保持登录的时间没有按我们设定的时长维持的话,就需要一些额外的动作对其进行优化。

一、memcached-session-manager是什么

简称msm,是一个用于集群tomcat下session共享问题的开源解决方案。

二、作用

主要功能是修改tomcat的session存储机制,使之能够把session序列化存放到memcached中。

三、分类

粘性(Sticky)

Tomcat session为主session,memcached为备session。请求初次到来时,从memcached取备session加载到tomcat中;请求结束时,将tomcat sessioncopy到memcached中,以达到主备同步的作用。

非粘性(Non-Sticky)

Tomcat session为中转session,memcached1为主session,memcached2为备session。请求初次到来时,从对应的memcached中获取session(如何对应的memcached1出错不可用时,会从另一台memcached中获取session);请求结束时,将Tomcat session更新至 主memcached1(对应cached)和备memcached2,并且清除tomcat session 。以达到主备同步之目的。

所谓的主和备,主要看当前session主存哪个节点,另一个节点便是备session,主备并非绝对。

下面演示非粘性session的使用。

四、Non-Sticky 使用

1、安装

1)、docker / 2.3.0.2(启动memcache实例)

2)、tomcat / 8.5 (两实例)

3)、nginx / 1.17.10

2、启动服务

1)、使用docker启动两台memcache实例,端口分别为11211,11212

拉取memcached镜像

docker pull memcached

查看镜像如下

docker images

如图:

启动一个内存256M,暴露端口11211,名为memcached1 的容器,如下

docker run -d -p 11211:11211 --name memcached1 memcached -m 256

启动一个内存256M,暴露端口11212,名为memcached2 的容器,如下

docker run -d -p 11212:11211 --name memcached2 memcached -m 256

如图,可以看到两个memcached容器已成功启动:

2)、配置并启动tomcat实例

对应文件名分别为tomcat_v_8_5_p_8180(下文称tomcat1),tomcat_v_8_5_p_8182(下文称tomcat2)(文件名v与p表示版本为8.5、端口分别为8180与8182)

将以下 jar 包复制到两个tomcat的 $TOMCAT_HOME/lib/ 下:

http://central.maven.org/maven2/de/javakaffee/msm/memcached-session-manager-tc8/2.3.0/memcached-session-manager-tc8-2.3.0.jar

http://central.maven.org/maven2/net/spy/spymemcached/2.12.3/spymemcached-2.12.3.jar

http://central.maven.org/maven2/de/javakaffee/msm/memcached-session-manager/2.3.0/memcached-session-manager-2.3.0.jar

修改tomcat_v_8_5_p_8180/webapps/ROOT下的index.jsp

<%--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--%>
<%@ page language="java" %>
<html>
  <head><title>tomcat1</title></head>
  <body>
    <h1><font color="red">tomcat1</font></h1>
    <table align="centre" border="1">
      <tr>
        <td>Session ID</td>
        <% 
        String name = (String)session.getAttribute("username");
        if (name == null) {
            session.setAttribute("username","xudj"); 
        }
        %>
        <td><%= session.getId() %></td>
      </tr>
      <tr>
        <td>SessionAttribute-Name</td>
        <td><%= name %></td>
     </tr>
      <tr>
        <td>Created on</td>
        <td><%= session.getCreationTime() %></td>
     </tr>
    </table>
  </body>
</html>

修改tomcat_v_8_5_p_8180/conf/context.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<!-- The contents of this file will be loaded for each web application -->
<Context useHttpOnly="true" path="" docBase="ROOT" reloadable="false" crossContext="true"  allowLinking="true" sessionCookiePath="/" sessionCookieDomain="localhost" sessionCookieName="XJOBSESSIONID">

  <CookieProcessor className="org.apache.tomcat.util.http.LegacyCookieProcessor" />
    <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
                  memcachedNodes="n1:127.0.0.1:11211,n2:127.0.0.1:11212"
                  sticky="false"
                  sessionBackupAsync="false"
                  sessionBackupTimeout="500"
                  lockingMode="auto"
                  requestUriIgnorePattern=".*\.(ico\|png\|gif\|jpg\|css\|js)$"
                  transcoderFactoryClass="de.javakaffee.web.msm.JavaSerializationTranscoderFactory"
                  copyCollectionsForSerialization="false"/>

    <!-- Default set of monitored resources. If one of these changes, the    -->
    <!-- web application will be reloaded.                                   -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>

    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--
    <Manager pathname="" />
    -->
</Context>

修改tomcat_v_8_5_p_8182/webapps/ROOT下的index.jsp

<%--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
--%>
<%@ page language="java" %>
<html>
  <head><title>tomcat2</title></head>
  <body>
    <h1><font color="red">tomcat2</font></h1>
    <table align="centre" border="1">
      <tr>
        <td>Session ID</td>
        <% 
        String name = (String)session.getAttribute("username");
        if (name == null) {
            session.setAttribute("username","xudj"); 
        }
        %>
        <td><%= session.getId() %></td>
      </tr>
      <tr>
        <td>SessionAttribute-Name</td>
        <td><%= name %></td>
     </tr>
      <tr>
        <td>Created on</td>
        <td><%= session.getCreationTime() %></td>
     </tr>
    </table>
  </body>
</html>

修改tomcat_v_8_5_p_8182/conf/context.xml文件

同tomcat_v_8_5_p_8180/conf/context.xml文件配置

context.xml配置说明

Context节点定义了session存于客户端的命名等属性

Manager为核心配置:

a)、className:使用类类名:de.javakaffee.web.msm.MemcachedBackupSessionManager

b)、memcachedNodes:所有运行的memcached节点。每个节 点的定义格式为id:host:port(如果是单节点,id可省略)

c)、sticky:指定使用粘性的还是非粘性的Session机制,默认为true

d)、sessionBackupAsync:指定Session是否应该被异步保存到Memcached中,默认true

e)、backupThreadCount:用来异步保存Session的线程数(如果sessionBackupAsync=”true”有效),默认为CPU内核数

f)、sessionBackupTimeout:设置备份一个Session所用的时间,如果操作超过时间那么保存失败(如果sessionBackupAsync=”false”有效),默认100,单位毫米

g)、lockingMode:此属性只对非粘性Session有用。 指定非粘性Session的锁定策略,有以下几种:none(从来不加锁)、all(当请求时对Session锁定,直到请求结束)、auto(对只读的request不加锁,对非只读的request加锁)、uriPattern:(使用正则表达式来比较requestRUI + “?” + queryString来决定是否加锁),默认为none

h)、requestUriIgnorePattern:不用备份Session的请求的正则表达式,默认无

i)、transcoderFactoryClass:创建序列化和反序列化保存到Memcached中的Session的编码转换器的工厂类名,默认de.javakaffee.web.msm.JavaSerializationTranscoderFactory

j)、copyCollectionsForSerialization:集合序列化,默认false

以上除了标明默认的为可选项,其它均为必填项。

启动tomcat实例

sh tomcat_v_8_5_p_8180/bin/startup.sh

sh tomcat_v_8_5_p_8182/bin/startup.sh

3)、配置并启动nginx

修改配置文件:/usr/local/etc/nginx/nginx.conf

upstream tomsrv {
	server  127.0.0.1:8180;
    server  127.0.0.1:8182;
   }
   
    location / {
	     proxy_pass  http://tomsrv;
        }

启动nginx

nginx -c /usr/local/etc/nginx/nginx.conf

3、访问测试


第一次访问到tomcat1,写入sessionId,由于是第一新生成的session,未获取到session中的属性,同时写入一个属性值。


第二次访问到tomcat2,sessionId不变,并获取到第一访问到tomcat1时写入到session中的属性值。

五、扩展

使用memcached时,可以通过telnet命令进行连接查看相关信息。

连接memcached1

telnet 127.0.0.1 11211

执行获取session信息

get 8849443E0450D29F540D2E8E8E857E93-n1

返回结果:

查看过期时间

stats cachedump 6 1

返回:ITEM 8849443E0450D29F540D2E8E8E857E93-n1 [186 b; 1596376693 s]
ITME sessionId [字节大小;过期unix时间]

六、总结

对于项目中出现的自动退出问题,采用memcached扩容的方式可以解决一定客户量存储的问题。
最终熟悉了整个过程,使用memcached缓存(失效策略:LRU(最近最少使用)加上到期失效策略)加缓存不是万全策略,缓存总会别动失效,所以还是考虑了结合登录持久化客户端支持用户无感知自动登录。