redis实现跨服务器session共享

一般我们在做服务器集群的时候,都会配置nginx反向代理服务器。但是问题来了,多个服务器之间的session怎么共享的呢?

我在这里用的是redis,实现思路:

1.自己定义一个sessionId生成策略,在session.setAttribute操作的时候,生成一个CSESSIONID并写回服务器;

2.定义filter拦截请求,扩展HttpServletRequest,代理session实现类,扩展setAttribute方法和getAttribute方法;

3.每次请求更新cookie的生命周期和redis中键的生命周期,我这里设置的是30分钟。

我的代码并不优雅,也没有进行扩展,下面贴出我的实现:

1.简单的redis实现:

实际开发请与spring整合并配置连接池

package com.tanlei.utils;

import java.util.UUID;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class CookieUtil {

	/** 设置session的过期时间*/
	public static Integer KEY_EXPIRE_TIME = 30 * 60;

	/**
	 * 获取JessionId
	 * @param request
	 * @return
	 */
	public static String getJsessionId(HttpServletRequest request) {
		Cookie[] cookies = request.getCookies();
		// 从Cookie数据中遍历查找 并取CSESSIONID
		if (null != cookies && cookies.length > 0) {
			for (Cookie cookie : cookies) {
				if ("CSESSIONID".equals(cookie.getName())) {
					// 有 直接返回
					return cookie.getValue();
				}
			}
		}
		return null;
	}

	public static String addCookie(HttpServletRequest request, HttpServletResponse response, Integer KEY_EXPIRE_TIME) {
		String csessionId = getJsessionId(request);
		if (csessionId == null || "".equals(csessionId)) {
			csessionId = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
		}
		Cookie cookie = new Cookie("CSESSIONID", csessionId);
		cookie.setPath("/");
		cookie.setMaxAge(KEY_EXPIRE_TIME);
		response.addCookie(cookie);
		return csessionId;
	}
}

package com.tanlei.utils;

import redis.clients.jedis.Jedis;

public class JedisProvider {  
	  
    private static Jedis jedis;  
    public static Jedis getJedis() {  
        jedis = new Jedis("127.0.0.1", 6379);  
        return jedis;  
    }  

}  


package com.tanlei.filter;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
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.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.tanlei.bean.SerializeUtil;
import com.tanlei.utils.CookieUtil;
import com.tanlei.utils.JedisProvider;

import redis.clients.jedis.Jedis;

public class SessionFilter implements Filter {

	private Jedis jedis = JedisProvider.getJedis();

	public void init(FilterConfig filterConfig) throws ServletException {

	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// 当session不为空的时候,要刷新cookie存活周期和redis中键的存活日期
		String csessionId = CookieUtil.getJsessionId((HttpServletRequest) request);
		if (csessionId != null && !"".equals(csessionId)) {
			jedis.expire(csessionId.getBytes(), CookieUtil.KEY_EXPIRE_TIME);
			CookieUtil.addCookie((HttpServletRequest) request, (HttpServletResponse) response,
					CookieUtil.KEY_EXPIRE_TIME);
		}
		chain.doFilter(new RemoteSessionRequest((HttpServletRequest) request, (HttpServletResponse) response),
				response);
	}

	public void destroy() {

	}

}

class RemoteSessionRequest extends HttpServletRequestWrapper {
	private HttpServletRequest request;
	private HttpServletResponse response;

	public RemoteSessionRequest(HttpServletRequest request, HttpServletResponse response) {
		super(request);
		this.request = request;
		this.response = response;
	}

	@Override
	public HttpSession getSession() {
		return RemoteSessionHandler.getInstance(super.getSession(), request, response);
	}

}

class RemoteSessionHandler implements InvocationHandler {
	private Jedis jedis = JedisProvider.getJedis();

	private HttpSession session = null;
	private HttpServletRequest request = null;
	private HttpServletResponse response = null;

	private RemoteSessionHandler(HttpSession httpSession, HttpServletRequest request, HttpServletResponse response) {
		this.session = httpSession;
		this.request = request;
		this.response = response;
	};

	public static HttpSession getInstance(HttpSession httpSession, HttpServletRequest request,
			HttpServletResponse response) {
		InvocationHandler handler = new RemoteSessionHandler(httpSession, request, response);
		return (HttpSession) Proxy.newProxyInstance(httpSession.getClass().getClassLoader(),
				httpSession.getClass().getInterfaces(), handler);
	}

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if ("setAttribute".equals(method.getName())) {
			String csessionId = CookieUtil.getJsessionId(request);
			if (csessionId == null || "".equals(csessionId)) {
				csessionId = CookieUtil.addCookie(request, response, CookieUtil.KEY_EXPIRE_TIME);
				request.setAttribute("csessionId", csessionId); //1.ask:为什么这里会在request域里面set一次?因为session的一次连续的setAttribute和getAttribute,cookie的值并没有写入,会取不到
				String keyStr = args[0].toString();
				Object obj = args[1]; //获取对象
				jedis.hset(csessionId.getBytes(), keyStr.getBytes(), SerializeUtil.serialize(obj)); //向jedis写入对象
			}
		} else if ("getAttribute".equals(method.getName())) {
			String csessionId = CookieUtil.getJsessionId(request);
			if("".equals(csessionId) || null == csessionId) { //1.answer:所以这里在cookie中取不到的时候,再从request域里面取
				csessionId = (String) request.getAttribute("csessionId");
			}
			if("".equals(csessionId) || null == csessionId) { //为什么需要双次判断,这是因为在第三方jar包或servlet or jsp包中默认有些试用session的情况
				return null;
			}
			String keyStr = args[0].toString();
			byte[] bytes = jedis.hget(csessionId.getBytes(), keyStr.getBytes()); //从jedis取出二进制对象
			Object obj = SerializeUtil.unserialize(bytes);
			return obj; //反系列化
		} //按需求覆盖其他session的方法
		return method.invoke(session, args);
	}
}
package com.tanlei.controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.tanlei.bean.User;


public class SessionSetServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

    public SessionSetServlet() {
    }


	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session= request.getSession();  
        System.out.println("xxxxxxxxxxxxxxxxxxxx");
        session.setAttribute("user", new User(1, "ttttttttt66666666"));  //session中放入值
        request.getRequestDispatcher("index.jsp").forward(request, response);  
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

package com.tanlei.bean;

import java.io.Serializable;

/**
 * 我这里要求所有的对象必须实现系列化接口,也是一个弊端
 * @author t6
 *
 */
public class User implements Serializable{

	private Integer id;
	private String userName;

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getUserName() {
		return userName;
	}

	public User(Integer id, String userName) {
		super();
		this.id = id;
		this.userName = userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

}

package com.tanlei.bean;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializeUtil {
	public static byte[] serialize(Object object) {
		ObjectOutputStream oos = null;
		ByteArrayOutputStream baos = null;
		try {
			// 序列化
			baos = new ByteArrayOutputStream();
			oos = new ObjectOutputStream(baos);
			oos.writeObject(object);
			byte[] bytes = baos.toByteArray();
			return bytes;
		} catch (Exception e) {

		}
		return null;
	}

	public static Object unserialize(byte[] bytes) {
		ByteArrayInputStream bais = null;
		try {
			// 反序列化
			bais = new ByteArrayInputStream(bytes);
			ObjectInputStream ois = new ObjectInputStream(bais);
			return ois.readObject();
		} catch (Exception e) {

		}
		return null;
	}
	
	public static void main(String[] args) {
		byte[] b = serialize("xxxxxxxxx");
		System.out.println(unserialize(b));
	}
}

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.tanlei</groupId>
	<artifactId>testsession1</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>testsession1</name>
	<description>测试session</description>
	<dependencies>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>2.7.2</version>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1.3</version>
		</dependency>
	</dependencies>
</project>


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值