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.对象序列化工具类
顾名思义,就是将对象转化成可保存的字节码。
2.session实现
有两种方式:
1.1包装map作为session存储单元
1.2包装httpsession
两种方式要注意,sessionId一定要唯一且不能使用httpsession自带的getId获取到的id,因为多个tomcat负载,每次请求都在不同tomcat,每次请求带去的jsessionID在当前服务器找不到就会创建一个新的,这个新的也会写回到浏览器,造成每次请求后cookie里面的jsessionid改变,永远在创建。
我这里是包装了map
这里我使用了观察者模式,session继承了RedisSessionObservable,在每次更新session时候,发送通知到观察者,由观察者负责保存到redis。
观察者代码:
3.redis操作
使用jedis框架对redis进行get/set
4.加载器
其中SessionCookieReadWrapper和SessionCookieWriteWrapper 用来检查cookie是否有sessionid,有的话就查找session,没有就重新创建一个session,并且写回到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缓存
我所知道并且试验过的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缓存
相关文章推荐
- Windows环境下Nginx+Tomcat+Redis实现应用服务器集群负载均衡和Session共享
- Nginx+Tomcat+Redis实现应用服务器集群负载均衡和Session共享
- tomcat原理及安装及反向代理、会话保持、session集群和session共享服务器的实现(一)
- 使用Tomcat+Redis来实现集群部署中的Session共享问题
- Tomcat7集群共享Session 基于redis进行统一管理
- 服务器安装Apache+Tomcat+Memcached共享Session的构架设计
- apache与 tomcat 集群配置与session共享
- Apache+Tomcat集群部署下的session共享解决
- 【原创】搭建Nginx(负载均衡)+Redis(Session共享)+Tomcat集群
- Linux+Nginx+Tomcat+Redis实现负载均衡,应用集群及session共享
- linux-tomcat-session共享-redis集群
- Apache+tomcat实现负载均衡集群和session共享、tengine+tomcat实现web动静分离
- Nginx + Tomcat + Redis 集群下的Session共享
- 搭建Nginx(负载均衡)+Redis(Session共享)+Tomcat集群
- apache+tomcat 均衡负载与集群中的session共享
- Apache+Tomcat集群配置+session共享
- linux下实现redis共享session的tomcat集群
- ngnix+ tomcat +memcached 集群设置服务器负载均衡,session共享
- 分布式高并发服务器做请求分发,session共享(nginx+tomcat+redis)
- 服务器安装Apache+Tomcat+Memcached共享Session的构架设计