本文共 9422 字,大约阅读时间需要 31 分钟。
一个账户只对应一个session,也就是一个用户在不同浏览器登陆,后登陆的会导致前面登陆的session失效。
集群环境下,导致maximumSessions的配置失效。并不能实现预期的目标。因为session没有共享。
采用spring data redis session解决实现session共享,统一管理。
但是由于项目集成了过多的开源框架,由于版本原因,很难整合到一起。并且项目测试已经接近尾声,因此没有采用。zookeeper监听用户session 方式,后登陆时操作对应节点,触发监听事件,使其先创建的session失效。
package com.raymon.cloudq.util;import javax.servlet.http.HttpSession;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;public class SessionContext { private static SessionContext instance; private MapsessionMap; private SessionContext() { sessionMap = new ConcurrentHashMap (); } public synchronized static SessionContext getInstance() { if (instance == null) { instance = new SessionContext(); } return instance; } public void addSession(HttpSession session) { if (session != null) { sessionMap.put(session.getId(), session); } } public void delSession(HttpSession session) { if (session != null) { sessionMap.remove(session.getId()); } } public void delSession(String sessionId) { sessionMap.remove(sessionId); } public HttpSession getSession(String sessionId) { if (sessionId == null) return null; return sessionMap.get(sessionId); }}
** * 由于session在集群中没有实现共享,一个账户只能对应一个session * 基于zookeeper监听的 来控制 */public interface ClusterSessionsCtrlService { /** * 设置监听 */ void setMaximumSessionsListener(); /** * 注册zookeeper sesssion数据 * 后登陆的用户会删除先登录的节点,触发监听,让先登陆的session失效 * @param empId * @param httpSession */ void registerZookeeperSession(Integer empId,HttpSession httpSession); /** * session超时,删除zookeeper注册数据 * @param sessionId */ void deleteZookeeperSessionRegister(String sessionId);}
package com.raymon.cloudq.service.impl;import com.raymon.cloudq.service.ClusterSessionsCtrlService;import com.raymon.cloudq.util.SessionContext;import org.apache.commons.lang.StringUtils;import org.apache.curator.RetryPolicy;import org.apache.curator.framework.CuratorFramework;import org.apache.curator.framework.CuratorFrameworkFactory;import org.apache.curator.framework.api.transaction.CuratorOp;import org.apache.curator.framework.api.transaction.CuratorTransactionResult;import org.apache.curator.framework.recipes.cache.PathChildrenCache;import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;import org.apache.curator.retry.ExponentialBackoffRetry;import org.apache.zookeeper.CreateMode;import org.apache.zookeeper.data.Stat;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.InitializingBean;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import javax.servlet.http.HttpSession;import java.util.ArrayList;import java.util.Collection;import java.util.List;@Servicepublic class ClusterSessionsCtrlServiceImpl implements ClusterSessionsCtrlService, InitializingBean { @Value(" ${zookeeperhostName}") private String zookeeperConnectionString; private static String sessionPath = "/session"; private static String sessionReaPath = "/sessionrea"; protected static org.slf4j.Logger logger = LoggerFactory.getLogger(ClusterSessionsCtrlServiceImpl.class); private CuratorFramework client = null; @Override public void afterPropertiesSet() throws Exception { setMaximumSessionsListener(); } @Override public void setMaximumSessionsListener() { RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3); client = CuratorFrameworkFactory.builder().connectString(zookeeperConnectionString) .sessionTimeoutMs(8000).retryPolicy(retryPolicy).build(); client.start(); try { Stat stat = client.checkExists().forPath(sessionPath); if (stat == null) { client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(sessionPath); } stat = client.checkExists().forPath(sessionReaPath); if (stat == null) { client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath(sessionReaPath); } } catch (Exception e) { e.printStackTrace(); logger.error("zookeeper创建/session失败,原因{}", e.toString()); } PathChildrenCache cache = null; try { cache = new PathChildrenCache(client, sessionPath, true); cache.start(); } catch (Exception e) { e.printStackTrace(); logger.error(e.toString()); } PathChildrenCacheListener cacheListener = new PathChildrenCacheListener() { @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception { logger.info("事件类型:" + event.getType()); if( event.getData()!=null){ logger.info("节点数据:" + event.getData().getPath() + " = " + new String(event.getData().getData())); } if (event.getType().equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)) { HttpSession httpSession = SessionContext.getInstance().getSession(new String(event.getData().getData())); if (httpSession == null) { return; } httpSession.invalidate(); } } }; cache.getListenable().addListener(cacheListener); } @Override public void deleteZookeeperSessionRegister(String sessionId) { try { SessionContext.getInstance().delSession(sessionId); String empId = null; Stat stat = client.checkExists().forPath(sessionReaPath + "/" + sessionId); if (stat != null) { empId = new String(client.getData().forPath(sessionReaPath + "/" + sessionId)); client.delete().forPath(sessionReaPath + "/" + sessionId); logger.info("delete session:" + sessionReaPath + "/" + sessionId); } /* stat = client.checkExists().forPath(sessionPath + "/" + empId); if (StringUtils.isNotEmpty(empId) && stat != null) { client.delete().forPath(sessionPath + "/" + empId); logger.info("delete session:" + sessionPath + "/" + empId); }*/ } catch (Exception e) { e.printStackTrace(); logger.error(e.toString()); } } @Override public void registerZookeeperSession(Integer empId, HttpSession httpSession) { try { SessionContext.getInstance().addSession(httpSession); Stat stat = client.checkExists().forPath(sessionPath + "/" + empId); Listoperations = new ArrayList (); if (stat != null) { CuratorOp deleteOpt = client.transactionOp().delete().forPath(sessionPath + "/" + empId); operations.add(deleteOpt); } CuratorOp createSessionPathOpt = client.transactionOp().create().withMode(CreateMode.EPHEMERAL).forPath(sessionPath + "/" + empId, httpSession.getId().getBytes()); CuratorOp createSessionReaPathOpt = client.transactionOp().create().withMode(CreateMode.EPHEMERAL).forPath(sessionReaPath + "/" + httpSession.getId(), String.valueOf(empId).getBytes()); operations.add(createSessionPathOpt); operations.add(createSessionReaPathOpt); Collection results = client.transaction().forOperations(operations); for (CuratorTransactionResult result : results) { logger.info(result.getForPath() + " - " + result.getType()); } } catch (Exception e) { e.printStackTrace(); logger.error(e.toString()); } }}
/** * session监听 * * */public class SessionListener implements HttpSessionListener, HttpSessionAttributeListener{ private SessionContext context = SessionContext.getInstance(); Logger log = LoggerFactory.getLogger(SessionListener.class); @Override public void attributeAdded(HttpSessionBindingEvent arg0) { } @Override public void attributeRemoved(HttpSessionBindingEvent arg0) { } @Override public void attributeReplaced(HttpSessionBindingEvent arg0) { } @Override public void sessionCreated(HttpSessionEvent arg0) { if(log.isDebugEnabled()) { log.debug("创建session"); } } @Override public void sessionDestroyed(HttpSessionEvent arg0) { context.delSession(arg0.getSession()); ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(arg0.getSession().getServletContext()); ClusterSessionsCtrlService clusterSessionsCtrlService = ctx.getBean(ClusterSessionsCtrlService.class); clusterSessionsCtrlService.deleteZookeeperSessionRegister(arg0.getSession().getId()); }}
clusterSessionsCtrlService.registerZookeeperSession(empId,request.getSession());
转载地址:http://xdgxf.baihongyu.com/