zoukankan      html  css  js  c++  java
  • 一个简单的分布式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

  • 相关阅读:
    以中间件,路由,跨进程事件的姿势使用WebSocket
    傻瓜式解读koa中间件处理模块koa-compose
    企业管理系统前后端分离架构设计 系列一 权限模型篇
    vue权限路由实现方式总结
    3YAdmin-专注通用权限控制与表单的后台管理系统模板
    lazy-mock ,一个生成后端模拟数据的懒人工具
    vue-quasar-admin 一个包含通用权限控制的后台管理系统
    CSS中line-height与vertical-align
    IdentityServer4实现Token认证登录以及权限控制
    利用AOP实现SqlSugar自动事务
  • 原文地址:https://www.cnblogs.com/Sicwen/p/10716429.html
Copyright © 2011-2022 走看看