Nginx Tomcat Redis整合实现Session缓存

本说明教程旨在实现使用Redis缓存EOS中Session和UserObject数据,其他java项目也可参考

1.Tomcat集群配置

修改均在conf/server.xml中,以一个tomcat为例
1. 修改Engine标签中jvmRoute属性值,tomcat官方文档中标识在负载均衡中一定要配置,以对服务器区分,且必须唯一

Identifier which must be used in load balancing scenarios to enable session affinity. The identifier, which must be unique across all Tomcat 6 servers which participate in the cluster, will be appended to the generated session identifier, therefore allowing the front end proxy to always forward a particular session to the same Tomcat 6 instance.

  1. 在Engine标签中添加子标签Cluster

The tomcat cluster implementation provides session replication, context attribute replication and cluster wide WAR file deployment.

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
  1. 修改端口号,一共有三个
<!-- tomcat服务访问端口port -->
<Connector port="8080" protocol="HTTP/1.1" onnectionTimeout="20000" redirectPort="8443" />
<!-- Apache JServ Protocol -->
<Connector port="11009" protocol="AJP/1.3" redirectPort="8443" />
<!-- 关闭Tomcat的监听端口 -->
<Server port="8006" shutdown="SHUTDOWN">

tomcat配置完成,同时在开发的应用中的web.xml添加\

2.Nginx负载均衡

这里只进行最简单的配置,配置文件为nginx.conf

http {
    ...
    <!-- tomcat server -->
    upstream  localhost {  
      server 127.0.0.1:8080  weight=1;  
      server 127.0.0.1:8888  weight=1;  
    }

    server {
        listen       80;
        <!-- tomcat server -->
        server_name  localhost;
        <!-- proxy_passz转发路径 -->
        location / {
            #root   html;
            #index  index.html index.htm;
            proxy_pass  http://localhost;
        }
}


3.Redis实现Session缓存

思路:实现HttpSessionListener和HttpSessionAtrributeListener监听Session创建销毁,session销毁时同步销毁redis中session数据、监听session属性增删改事件,当更改的属性为userObject时,同步userObject数据到redis
1. redis安装与配置(基于Ubuntu)
- 安装
sudo apt-get install redis-server
- 配置/etc/redis/redis.conf

打开远程访问的功能(注释bind)
# bind 127.0.0.1
设置密码(取消注释requirepass,后跟需设置的密码)
requirepass mypass

  1. 设置session缓存
    > 以SessionID为key,UserObject的JSON格式数据为value存储到redis
    > 在用户登录成功后进行redis数据创建,session失效redis数据删除
    > 配置RedisSessionListener实现redis中session数据与UserObject同步
    > 配置RedisFilter实现从redis获取session数据验证用户是否登陆
  • web.xml配置
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="defaultWebApp" version="2.4" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    ...

    <filter>
        <filter-name>RedisFilter</filter-name>
        <filter-class>com.primeton.eos.areamgt.RedisFilter</filter-class>
    </filter>


    <listener>
        <listener-class>com.primeton.eos.areamgt.RedisSessionListener</listener-class>
    </listener>
    <filter-mapping>
        <filter-name>RedisFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
    </filter-mapping>

    ...
    <distributable/>
</web-app>

  • 同步UserObject数据到Redis代码如下:
package com.primeton.eos.areamgt;
/**
 * 监听session数据,实现redis与UserObject同步
 */
import java.util.Map;

import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import org.apache.log4j.Logger;

import redis.clients.jedis.Jedis;

import com.alibaba.fastjson.JSONObject;
import com.eos.data.datacontext.IUserObject;

public class RedisSessionListener implements HttpSessionListener,
        HttpSessionAttributeListener {

    private Logger logger = Logger.getLogger(RedisSessionListener.class);

    public void attributeAdded(HttpSessionBindingEvent hse) {
        // 增加userObject数据时,redis数据同步
        if("userObject".equals(hse.getName())){
            doSyncWithUserObject(hse);
        }
    }

    public void attributeRemoved(HttpSessionBindingEvent hse) {
        // userObject数据移除时,同步移除redis数据
        if("userObject".equals(hse.getName())){
            String sessionid = hse.getSession().getId().split("\\.")[0];
            delRedisSession(sessionid);
        }
    }

    public void attributeReplaced(HttpSessionBindingEvent hse) {
        // userObject数据更改时,redis数据
        if("userObject".equals(hse.getName())){
            doSyncWithUserObject(hse);
        }
    }

    public void sessionCreated(HttpSessionEvent hse) {}

    public void sessionDestroyed(HttpSessionEvent hse) {
        // sesseion失效,移除redis数据
        String sessionid= hse.getSession().getId().split("\\.")[0];
        delRedisSession(sessionid);
    }

    /**
     * 保持Redis与UserObject同步
     * @param hse
     */
    public void doSyncWithUserObject(HttpSessionEvent hse){
        String sessionid = hse.getSession().getId().split("\\.")[0];
        Jedis jedis = RedisUtil.getJedis();
        IUserObject userObject = (IUserObject) hse.getSession().getAttribute("userObject");

        // session userobject信息存入redis
        Map<String, Object> attrMap = userObject.getAttributes();
        AttributesEntity attribute = new AttributesEntity();
        attribute.setExtendUserID((String) attrMap.get("EXETEND_USER_ID"));
        attribute.setTanantID((String) attrMap.get("TENANT_ID"));
        attribute.setMenutype((String) attrMap.get("menutype"));
        attribute.setOrgList((String) attrMap.get("orglist"));
        attribute.setParentOrgIds((String) attrMap.get("parentOrgIds"));
        attribute.setRoleList((String) attrMap.get("roleList"));
        UserObjectEntity userEntity = new UserObjectEntity();
        userEntity.setSessionId(sessionid);
        userEntity.setUniqueId(userObject.getUniqueId());
        userEntity.setUserId(userObject.getUserId());
        userEntity.setUserMail(userObject.getUserMail());
        userEntity.setUserName(userObject.getUserName());
        userEntity.setUserOrgId(userObject.getUserOrgId());
        userEntity.setUserOrgName(userObject.getUserOrgName());
        userEntity.setUserRemoteIP((String) userObject.get("userRemoteIP"));
        userEntity.setUserRealName(userObject.getUserRealName());
        userEntity.setAttributes(attribute);
        String obj = JSONObject.toJSONString(userEntity);
        jedis.set(sessionid, obj);
        logger.error(obj);
        if (jedis != null) {
            try {
                jedis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 从redis移除session
     * @param hse
     */
    public void delRedisSession(String sessionid ){
        Jedis jedis = RedisUtil.getJedis();
        if(sessionid != null && jedis.exists(sessionid)){
            jedis.del(sessionid);
        }
        if (jedis != null) {
            try {
                jedis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
  • 拦截器,从redis获取session验证用户是否登陆,将governor拦截器关闭
package com.primeton.eos.areamgt;

import java.io.IOException;
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.apache.log4j.Logger;

import redis.clients.jedis.Jedis;

public class RedisFilter implements Filter {

    private Logger logger = Logger.getLogger(RedisFilter.class);

    public void destroy() {
    }

    public void doFilter(ServletRequest requ, ServletResponse resp,
            FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httprequ = (HttpServletRequest) requ;
        HttpServletResponse httpresp = (HttpServletResponse) resp;

        String path = httprequ.getRequestURI();
        if(path.indexOf("/login.jsp") > -1 || path.indexOf("login.flow") > -1) {
            filterChain.doFilter(httprequ, httpresp);
            return;
        }


        Jedis jedis = RedisUtil.getJedis();
        String sessionid = httprequ.getSession().getId().split("\\.")[0];
        boolean login = jedis.exists(sessionid);
        if (jedis != null) {
            try {
                jedis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        if (login) {
            // 已登录
            filterChain.doFilter(requ, resp);
        } else {
            // 未登录
            httprequ.getRequestDispatcher("/coframe/auth/login/login.jsp").forward(requ, resp);
            logger.error("session id:" + sessionid + " no login");
        }
    }

    public void init(FilterConfig arg0) throws ServletException {}
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注