一个简单的分布式session框架

该代码只是用来学习原理的,有很多不完善之处。

代码:  git@github.com:sicw/EasySpringSession.git

一. 整体设置

1. 实现Filter,封装新的request请求

2. 在newRequest中重写getSession

3. 在getSession中,从redis获取session,或存储session到redis

二. 过滤器

1. 封装request

2. 执行完过滤器链之后要设置sessionId到cookie

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if(!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)){
            throw new RuntimeException("just supports HTTP requests");
        }
        //封装新的请求
        SessionRepositoryRequestWrapper newRequest = new SessionRepositoryRequestWrapper((HttpServletRequest) request, (HttpServletResponse) response);
        try {
            chain.doFilter(newRequest, response);
        }finally {
            //持久化session到redis
            //设置sessionId到cookie
            newRequest.commitSession();
        }
    }

三. 封装Request

 封装Request的主要目的是重写他的getSession操作。

    public class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
        private HttpServletRequest httpServletRequest;
        private HttpServletResponse httpServletResponse;
        private HttpSession currentSession;

        public SessionRepositoryRequestWrapper(HttpServletRequest request,HttpServletResponse response) {
            super(request);
            httpServletRequest = request;
            httpServletResponse = response;
        }

        /**
         * 在servlet中调用getSession会触发该方法
         * @return session
         */
        @Override
        public HttpSession getSession() {
            HttpSession session = getRequestedSession();
            //没有sessionId或sessionId已过期
            if(session == null){
                session = sessionRepository.createSession();
            }
            currentSession = session;
            return session;
        }

        /**
         * 持久化session
         * 设置sessionId到Cookie
         */
        public void commitSession(){
            HttpSession session = currentSession;
            sessionRepository.save(session);
            String sessionId = session.getId();
            httpSessionIdResolver.setSessionId(httpServletRequest, httpServletResponse, sessionId);
        }

        private HttpSession getRequestedSession() {
            //获取sessionId
            List<String> sessionIds = httpSessionIdResolver.resolveSessionIds(httpServletRequest);
            //获取session
            for (String sessionId : sessionIds) {
                HttpSession session = sessionRepository.findById(sessionId);
                if(session != null){
                    return session;
                }
            }
            return null;
        }
    }

四. 持久化session

 这里持久化session使用的是spring-data-redis,把session通过HMSEt存储到redis中

    @Override
    public RedisSession createSession() {
        return new RedisSession();
    }
@Override
public void save(HttpSession session) { ((RedisSession)session).saveDelta(); } @Override public RedisSession findById(String id) { return getSession(id, false); } @Override public void deleteById(String id) { redisTemplate.delete(id); }

五. 测试

1. 创建webapp

2. 实现SetServlet和GetServlet

@WebServlet("/get")
public class GetServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String key = req.getParameter("name");
        HttpSession session = req.getSession();
        String value = (String) session.getAttribute(key);
        System.out.println("get ["+key+","+value+"]");
    }
}
@WebServlet("/set")
public class SetServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String key = req.getParameter("name");
        String value = req.getParameter("value");
        HttpSession session = req.getSession();
        session.setAttribute(key,value);
        System.out.println("set ["+key+","+value+"]");
    }
}

配置web.xml
因为我们的Filter需要设置属性,如果直接在这里配置Filter那么不能设置属性了。所以使用委派类DelegatingFilterProxy

他会根据Filter-Name去spring查找相应的bean,我们把Filter配置成bean就可以设置他的属性了。

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  
  <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>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 加载配置文件 -->
    <context:property-placeholder location="classpath:redis.properties" />

    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}" />
        <property name="maxTotal" value="${redis.maxTotal}" />
        <property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
        <property name="blockWhenExhausted" value="${redis.blockWhenExhausted}" />
        <property name="testOnBorrow" value="${redis.testOnBorrow}" />
    </bean>

    <bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">
        <property name="clusterNodes">
            <set>
                <bean id="redisNode1" class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.host1}"/>
                    <constructor-arg name="port" value="${redis.port1}"/>
                </bean>
                <bean id="redisNode2" class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.host2}"/>
                    <constructor-arg name="port" value="${redis.port2}"/>
                </bean>
                <bean id="redisNode3" class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.host3}"/>
                    <constructor-arg name="port" value="${redis.port3}"/>
                </bean>
                <bean id="redisNode4" class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.host4}"/>
                    <constructor-arg name="port" value="${redis.port4}"/>
                </bean>
                <bean id="redisNode5" class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.host5}"/>
                    <constructor-arg name="port" value="${redis.port5}"/>
                </bean>
                <bean id="redisNode6" class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg name="host" value="${redis.host6}"/>
                    <constructor-arg name="port" value="${redis.port6}"/>
                </bean>
            </set>
        </property>
    </bean>

    <!-- Spring-redis连接池管理工厂 -->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <constructor-arg name="clusterConfig" ref="redisClusterConfiguration"/>
        <property name="timeout" value="${redis.timeout}" />
        <property name="poolConfig" ref="poolConfig" />
        <property name="usePool" value="true"/>
    </bean>

    <!-- redis template definition -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory" />
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
        </property>
        <property name="hashKeySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="hashValueSerializer">
            <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
        </property>
    </bean>

    <bean id="redisSessionRepository" class="com.channelsoft.ccod.session.RedisSessionRepository">
        <constructor-arg name="redisTemplate" ref="redisTemplate"/>
    </bean>

  <!-- 在这里配置了Filter,并且设置属性 --> <bean id="springSessionRepositoryFilter" class="com.channelsoft.ccod.filter.SessionRepositoryFilter"> <property name="sessionRepository" ref="redisSessionRepository"/> </bean> </beans>

 redis.properties配置

#Common Config
redis.password=""
redis.maxIdle=400
redis.maxTotal=6000
redis.maxWaitMillis=1000
redis.blockWhenExhausted=true
redis.testOnBorrow=true
redis.timeout=100000
defaultCacheExpireTime=60

#Cluster Config
redis.host1=10.130.29.83
redis.port1=6379
redis.host2=10.130.29.83
redis.port2=6380
redis.host3=10.130.29.83
redis.port3=6381
redis.host4=10.130.29.83
redis.port4=6382
redis.host5=10.130.29.83
redis.port5=6383
redis.host6=10.130.29.83
redis.port6=6384

最后配置容器端口,比如Tomcat创建两个配置端口分别为8080,8081,启动容器。
通过url访问:
http://localhost:8080/set?name=k1&value=v1

http://localhost:8081/get?name=k1

原文地址:https://www.cnblogs.com/Sicwen/p/10716429.html