一般我们在做服务器集群的时候,都会配置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>