如何使用Spring+redis实现对session的分布式管理

时间:2022-11-17 17:22:42

在spring中实现分布式 session管理

本文主要是在spring中实现分布式session,采用redis对session进行持久化管理,这样当应用部署的时候,不需要在resin、tomcat等容器里面进行分布式配置,方便加入新的节点服务器进行集群扩容,session不依赖各节点的服务器,可直接从redis获取。下面是功能的核心代码:

一、首先在web.xml里面配置

加入拦截器:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!-- 分布式session start -->
  <filter>
    <filter-name>distributedsessionfilter</filter-name>
    <filter-class>distributedsessionfilter</filter-class>
    <init-param>
      <!-- 必填,密钥.2种方式,1对应为bean,格式为bean:key。2字符串,格式如:afffrfgv-->
      <param-name>key</param-name>
      <param-value>xxxxxxxx</param-value>
    </init-param>
    <init-param>
      <!-- 必填,redis对应的bean,格式为bean:xx-->
      <param-name>cachebean</param-name>
      <param-value>bean:redispersistent</param-value>//distributedbaseinterface,对应于此接口,进行session的持久化操作
    </init-param>
    <init-param>
      <!-- 必填, -->
      <param-name>cookiename</param-name>
      <param-value>testsessionid</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>distributedsessionfilter</filter-name>
    <url-pattern>*.do</url-pattern>
  </filter-mapping>
  <!-- 分布式session end -->

二、拦截器的实现,核心代码如下

主要有以下的几个类:

  1. distributedsessionfilter,
  2. distributedsessionmanager,
  3. distributedhttpsessionwrapper,
  4. distributedhttpservletrequestwrapper

1、distributedsessionfilter实现filter:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.ioexception;
import java.util.hashmap;
import java.util.map;
 
import javax.servlet.filter;
import javax.servlet.filterchain;
import javax.servlet.filterconfig;
import javax.servlet.servletexception;
import javax.servlet.servletrequest;
import javax.servlet.servletresponse;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
 
import org.springframework.web.context.webapplicationcontext;
import org.springframework.web.context.support.webapplicationcontextutils;
 
public class distributedsessionfilter implements filter {
  private static final logger log = loggerfactory.getlogger(distributedsessionfilter.class);
 
  private string cookiename;
 
  //主要是对session进行管理的操作
  private distributedsessionmanager distributedsessionmanager;
 
  private string key;
}

容器启动时候的初始化方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@override
  public void init(filterconfig config) throws servletexception {
    webapplicationcontext wac = webapplicationcontextutils.getrequiredwebapplicationcontext(config
        .getservletcontext());
    string key = config.getinitparameter("key");
    string cookiename = config.getinitparameter("cookiename");
    string cachebean = config.getinitparameter("cachebean");
    // 获取bean的名称,配置是"bean:"
    string redisbeanstr = cachebean.substring(5);
    distributedbaseinterface distributedcache = (distributedbaseinterface) wac.getbean(redisbeanstr);
 
    // 获取key,有2种配置方式,1对应为bean,格式为bean:key。2字符串
    if (key.startswith("bean:")) {
      this.key = (string) wac.getbean(key.substring(5));
    } else {
      this.key = key;
    }
    this.cookiename = cookiename;
    this.distributedsessionmanager = distributedsessionmanager.getinstance(distributedcache);
 
    //异常处理省略。。。
  }

进行实际的请求拦截:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
@override
public void dofilter(servletrequest servletrequest, servletresponse servletresponse, filterchain filterchain)
    throws servletexception, ioexception {
  distributedhttpservletrequestwrapper distreq = null;
  try {
    //请求处理
    distreq = createdistributedrequest(servletrequest, servletresponse);
    filterchain.dofilter(distreq, servletresponse);
  } catch (throwable e) {
    //省略。。。
  } finally {
    if (distreq != null) {
      try {
        //处理完成request后,处理session(主要是保存session会话)
        dealsessionafterrequest(distreq.getsession());
      } catch (throwable e2) {
        //省略。。。
      }
    }
  }
}
 
//分布式请求
private distributedhttpservletrequestwrapper createdistributedrequest(servletrequest servletrequest,
    servletresponse servletresponse) throws ioexception, servletexception {
  httpservletrequest request = (httpservletrequest) servletrequest;
  httpservletresponse response = (httpservletresponse) servletresponse;
  string usersid = cookieutil.getcookie(cookiename, request);
  string actualsid = distributedsessionmanager.getactualsid(usersid, request, key);
  if (stringutil.isblank(actualsid)) {
    if (stringutil.isnotblank(usersid)) {
      log.info("usersid[{}]验证不通过", usersid);
    }
    // 写cookie
    string[] usersidarr = distributedsessionmanager.createusersid(request, key);
    usersid = usersidarr[0];
    cookieutil.setcookie(cookiename, usersid, request, response);
    actualsid = usersidarr[1];
  }
  actualsid = "sid:" + actualsid;
  distributedhttpsessionwrapper distsession = null;
  try {
    map<string, object> allattribute = distributedsessionmanager.getsession(actualsid, request.getsession()
        .getmaxinactiveinterval());
    distsession = new distributedhttpsessionwrapper(actualsid, request.getsession(), allattribute);
  } catch (throwable e) {
    // 出错,删掉缓存数据
    log.error(e.getmessage(), e);
    map<string, object> allattribute = new hashmap<string, object>();
    distsession = new distributedhttpsessionwrapper(actualsid, request.getsession(), allattribute);
    distributedsessionmanager.removesession(distsession);
  }
  distributedhttpservletrequestwrapper requestwrapper = new distributedhttpservletrequestwrapper(request,
      distsession);
  return requestwrapper;
 
}
 
// request处理完时操作session
private void dealsessionafterrequest(distributedhttpsessionwrapper session) {
  if (session == null) {
    return;
  }
  if (session.changed) {
    distributedsessionmanager.savesession(session);
  } else if (session.invalidated) {
    distributedsessionmanager.removesession(session);
  } else {
    distributedsessionmanager.expire(session);
  }
}

 2、distributedsessionmanager,主要处理分布式session,核心代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class distributedsessionmanager {
  protected static final logger log = loggerfactory.getlogger(distributedsessionmanager.class);
 
  private static distributedsessionmanager instance = null;
 
  //redis处理session的接口,自己根据情况实现
  private distributedbaseinterface distributedbaseinterface;
 
  private static byte[] lock = new byte[1];
 
  private distributedsessionmanager(distributedbaseinterface distributedbaseinterface) {
    this.distributedbaseinterface = distributedbaseinterface;
  }
 
  public static distributedsessionmanager getinstance(distributedbaseinterface redis) {
    if (instance == null) {
      synchronized (lock) {
        if (instance == null) {
          instance = new distributedsessionmanager(redis);
        }
      }
    }
    return instance;
  }
 
  //获取session
  public map<string, object> getsession(string sid,int second) {
    string json = this.distributedbaseinterface.get(sid,second);
    if (stringutil.isnotblank(json)) {
      return jsonutil.unserializemap(json);
    }
    return new hashmap<string, object>(1);
  }
 
  //保存session
  public void savesession(distributedhttpsessionwrapper session) {
    map<string, object> map=session.allattribute;
    if(maputil.isempty(map)){
      return;
    }
    string json = jsonutil.serializemap(map);
    this.distributedbaseinterface.set(session.getid(), json, session.getmaxinactiveinterval());
  }
 
  //删除session
  public void removesession(distributedhttpsessionwrapper session) {
    distributedbaseinterface.del(session.getid());
  }
 
  public void expire(distributedhttpsessionwrapper session) {
    distributedbaseinterface.expire(session.getid(), session.getmaxinactiveinterval());
  }
 
  /**
   * 创建cookie的sid
   */
  public string[] createusersid(httpservletrequest request, string key) {
    //...
  }
 
  public string getactualsid(string usersid, httpservletrequest request, string key) {
    //...
  }
}

3、distributedhttpsessionwrapper 实现了 httpsession,进行分布式session包装,核心代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
public class distributedhttpsessionwrapper implements httpsession {
 
  private httpsession orgisession;
 
  private string sid;
 
  boolean changed = false;
 
  boolean invalidated = false;
 
  map<string, object> allattribute;
 
  public distributedhttpsessionwrapper(string sid, httpsession session, map<string, object> allattribute) {
    this.orgisession = session;
    this.sid = sid;
    this.allattribute = allattribute;
  }
 
  @override
  public string getid() {
    return this.sid;
  }
 
  @override
  public void setattribute(string name, object value) {
    changed = true;
    allattribute.put(name, value);
  }
 
  @override
  public object getattribute(string name) {
    return allattribute.get(name);
  }
 
  @override
  public enumeration<string> getattributenames() {
    set<string> set = allattribute.keyset();
    iterator<string> iterator = set.iterator();
    return new myenumeration<string>(iterator);
  }
 
  private class myenumeration<t> implements enumeration<t> {
    iterator<t> iterator;
 
    public myenumeration(iterator<t> iterator) {
      super();
      this.iterator = iterator;
    }
 
    @override
    public boolean hasmoreelements() {
      return iterator.hasnext();
    }
 
    @override
    public t nextelement() {
      return iterator.next();
    }
 
  }
 
  @override
  public void invalidate() {
    this.invalidated = true;
  }
 
  @override
  public void removeattribute(string name) {
    changed = true;
    allattribute.remove(name);
  }
 
  @override
  public long getcreationtime() {
    return orgisession.getcreationtime();
  }
 
  @override
  public long getlastaccessedtime() {
    return orgisession.getlastaccessedtime();
  }
 
  @override
  public int getmaxinactiveinterval() {
    return orgisession.getmaxinactiveinterval();
  }
 
  @override
  public servletcontext getservletcontext() {
    return orgisession.getservletcontext();
  }
 
  @override
  public object getvalue(string arg0) {
    return orgisession.getvalue(arg0);
  }
 
  @override
  public string[] getvaluenames() {
    return orgisession.getvaluenames();
  }
 
  @override
  public boolean isnew() {
    return orgisession.isnew();
  }
 
  @override
  public void putvalue(string arg0, object arg1) {
    orgisession.putvalue(arg0, arg1);
  }
 
  @override
  public void removevalue(string arg0) {
    orgisession.removevalue(arg0);
  }
 
  @override
  public void setmaxinactiveinterval(int arg0) {
    orgisession.setmaxinactiveinterval(arg0);
  }
 
  @override
  public httpsessioncontext getsessioncontext() {
    return orgisession.getsessioncontext();
  }

4、distributedhttpservletrequestwrapper 实现了 httpservletrequestwrapper,包装处理过的session和原始request,核心代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class distributedhttpservletrequestwrapper extends javax.servlet.http.httpservletrequestwrapper {
  private httpservletrequest orgirequest;
  private distributedhttpsessionwrapper session;
 
  public distributedhttpservletrequestwrapper(httpservletrequest request, distributedhttpsessionwrapper session) {
    super(request);
    if (session == null){
      //异常处理。。
    }
    if (request == null){
      //异常处理。。
    }
    this.orgirequest = request;
    this.session = session;
  }
 
  public distributedhttpsessionwrapper getsession(boolean create) {
    orgirequest.getsession(create);
    return session;
  }
 
  public distributedhttpsessionwrapper getsession() {
    return session;
  }
 
}

5、另外,定义distributedbaseinterface接口,用来处理session入redis进行持久化操作:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public interface distributedbaseinterface {
 
  /**
   * 根据key获取缓存数据
   * @param key 
   * @param seconds 
   */
  public string get(string key,int seconds);
 
  /**
   * 更新缓存数据
   * @param key 
   * @param json
   * @param seconds
   */
  public void set(string key, string json,int seconds);
 
  /**
   * 删除缓存
   * @param key
   */
  public void del(string key);
 
  /**
   * 设置过期数据
   * @param key
   * @param seconds
   */
  public void expire(string key,int seconds);

注:本文只是在spring中采用redis的方式对session进行管理,还有其他诸多的实现方式,比如在容器里面配置等,设计路由算法让session依赖于集群中的各个节点服务器,,,,,,但redis这种方式在实际应用中还是比较广泛的,lz公司主要就是采用此方式。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/u014263388/article/details/56020889