您的位置:首页 > 数据库 > Redis

apache+tomcat集群session共享-redis服务器

2015-03-05 11:05 459 查看
一.tomacat集群session共享方式

我所知道并且试验过的session共享的方式有两种:

1.tomcat的session复制功能

2.单独一台session服务器

还有一些方式

3.对于同一ip始终分配到一台服务器

4.cookie保存session

第三种方式的缺点比较明显,始终到同一台服务器看起来没什么问题,但是如果那台机器刚好宕机,那请求转发到其他服务器,其他服务器无法获取之前session,造成session丢失的情况。

第四种方式缺点主要是:

cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗(可设置:httponly)考虑到安全应当使用session。单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。

综上,session复制和单独一台session服务器可行性较高。在集群不是很庞大的情况下,session复制就可以工作的很好了。

下面重点介绍第二种方式。

二.nosql服务器保存session原理

将所有节点的Session放到一起进行统一管理,每个节点在未参与集群以前都有自己独立的Session体系,参与到集群以后可以让所有节点将各自的Session信息用一套相同的机制保存到一个统一的地方进行存取,这样不管请求被分发到哪个节点都可以访问到其它节点创建的Session。

放到同一地方可以是数据库,可以是cache,也可以是nosql。

做法是:

1.简单实现一个自己的session,支持序列化

2.创建session要同步保存在session服务器

3.每次更新session都要同步

4.设置cookie,将sessionId保存在浏览器,设置失效时间

5.session服务器对于session也要有失效管理

三.具体实现

环境:redis2.6 tomcat6.0 apache2.2

1.对象序列化工具类
顾名思义,就是将对象转化成可保存的字节码。

package com.chenwei.session;

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

public class SerializableUtil {

/**
* 序列化对象
*
* @param object
* @return
* @throws IOException
*/
public static byte[] writeObject(Object object) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(object);
oos.flush();
oos.close();
return bos.toByteArray();
}

/**
* 反序列话
* @param buf
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static Object readObject(byte[] buf) throws IOException, ClassNotFoundException {
ByteArrayInputStream bis = new ByteArrayInputStream(buf);
ObjectInputStream ins = new ObjectInputStream(bis);
Object object = ins.readObject();
ins.close();
return object;
}

public static void main(String[] args) {

}

}


2.session实现

有两种方式:

1.1包装map作为session存储单元

1.2包装httpsession

两种方式要注意,sessionId一定要唯一且不能使用httpsession自带的getId获取到的id,因为多个tomcat负载,每次请求都在不同tomcat,每次请求带去的jsessionID在当前服务器找不到就会创建一个新的,这个新的也会写回到浏览器,造成每次请求后cookie里面的jsessionid改变,永远在创建。

我这里是包装了map

/**
* 观察者模式,一旦有修改 自动同步到redis服务器
* @author chenwei
*
*/
public class RedisSession extends RedisSessionObservable implements Serializable {

/**
*
*/
private static final long serialVersionUID = -121659577343047214L;

public static final String SESSION_COOKIE_NAME ="rsessionid";

private Map<String, Object> sessionMap;

private String sid;

public RedisSession(){
this.sessionMap = new HashMap<String,Object>();
sid = UUID.randomUUID().toString().replaceAll("-", "");
System.out.println("init sid :"+sid);
addObserver(new RedisSessionObserver());
setChanged();
notifyObservers();
}

public RedisSession(String sid) {
this.sessionMap = new HashMap<String,Object>();
this.sid = sid;
addObserver(new RedisSessionObserver());
setChanged();
notifyObservers();
}

public static RedisSession getSession(String sid){
return RedisSessionDAO.getInstance().get(sid);
}

public String getId() {
return sid;
}

public Object getAttribute(String name) {
return sessionMap.get(name);
}

public void setAttribute(String name, Object value) {
sessionMap.put(name, value);
setChanged();
notifyObservers();
}

public void removeAttribute(String name) {
sessionMap.remove(name);
setChanged();
notifyObservers();
}
}


这里我使用了观察者模式,session继承了RedisSessionObservable,在每次更新session时候,发送通知到观察者,由观察者负责保存到redis。

观察者代码:

public class RedisSessionObserver implements Serializable{

/**
*
*/
private static final long serialVersionUID = 3004378138310570499L;

public void update(RedisSessionObservable o, Object arg) {
System.out.println("更新redis缓存");
RedisSessionDAO.getInstance().set(o);
}

}


3.redis操作

使用jedis框架对redis进行get/set

public class RedisSessionDAO {

private Jedis jsdis;
private JedisPool jedisPool;

private static RedisSessionDAO dao = new RedisSessionDAO();

public void init() {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(20);
config.setMaxIdle(5);
config.setMaxWaitMillis(1000l);
config.setTestOnBorrow(false);

jedisPool = new JedisPool(config, "127.0.0.1", 6379);
}

public void set(Object o) {
jsdis = jedisPool.getResource();
RedisSession session = (RedisSession) o;
try {
jsdis.set(session.getId().getBytes(), SerializableUtil.writeObject(o));
} catch (IOException e) {
e.printStackTrace();
}
jedisPool.returnResource(jsdis);
}

public RedisSession get(String sid) {
RedisSession session = null;
jsdis = jedisPool.getResource();
byte[] sessionByte = jsdis.get(sid.getBytes());
if (sessionByte == null) {
return session;
}
try {
session = (RedisSession) SerializableUtil.readObject(sessionByte);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
jedisPool.returnResource(jsdis);
return session;
}

public static RedisSessionDAO getInstance() {
dao.init();
return dao;
}
}


4.加载器

public class RedisSessionLoader {

public static RedisSession getSession(HttpServletRequest req, HttpServletResponse resp) {
SessionCookieReadWrapper reader = new SessionCookieReadWrapper(req);
SessionCookieWriteWrapper writer = new SessionCookieWriteWrapper(resp);
RedisSession session = null;
if (reader.exsitCookie()) {
System.out.println(reader.getSessionId());
session = RedisSessionDAO.getInstance().get(reader.getSessionId());
if (session == null) {
session = new RedisSession(reader.getSessionId());
}
} else {
System.out.println("新加一个session");
session
4000
= new RedisSession();
writer.addSessionCookie(session.getId());
}
return session;
}

}


其中SessionCookieReadWrapper和SessionCookieWriteWrapper 用来检查cookie是否有sessionid,有的话就查找session,没有就重新创建一个session,并且写回到cookie。

代码如下:
public class SessionCookieReadWrapper extends HttpServletRequestWrapper {

private HttpServletRequest request;

public SessionCookieReadWrapper(HttpServletRequest request) {
super(request);
this.request = request;
}

public boolean exsitCookie() {
Cookie[] cookies = request.getCookies();
if (cookies == null || cookies.length == 0) {
return false;
}

for (Cookie cookie : cookies) {
if (cookie.getName().equals(RedisSession.SESSION_COOKIE_NAME)) {
return true;
}
}
return false;
}

public String getSessionId() {
String id = null;
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if (cookie.getName().equals(RedisSession.SESSION_COOKIE_NAME)) {
id = cookie.getValue();
}
}
return id;
}

}

public class SessionCookieWriteWrapper extends HttpServletResponseWrapper {

private HttpServletResponse response;

public SessionCookieWriteWrapper(HttpServletResponse response) {
super(response);
this.response = response;
}

public void addSessionCookie(String sessionId){
Cookie cookie = new Cookie(RedisSession.SESSION_COOKIE_NAME,sessionId);
cookie.setMaxAge(3600);
response.addCookie(cookie);
}
}


5.使用样例和结果

protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String name = req.getParameter("name");
RedisSession session = RedisSessionLoader.getSession(req, resp);
session.setAttribute("curreantUser", name);
}

结果截图:







控制台信息:



中文乱码了。。。。。。。。。

打印依次是:

新加一个session

更新redis缓存
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息