依赖于session的在线人数统计
来源:互联网 发布:如何修改淘宝地址 编辑:程序博客网 时间:2024/06/06 06:29
最近工作中遇到一个问题,
在做在线人数统计时.我们实现了HttpSessionListner,HttpSessionAttributeListener里面的attributedAdded()方法和attributeRemoved()的方法以及sessionDestroyed(),来操作一个map,将登陆的用户信息放入到map里面,用户退出时,再从map里面移除.
生产环境是在websphere上,出现了一个现象.很多天之前登录的用户还在在线人数里面,并且通过日志发现,此用户登录之后并没有做过任何业务操作.
第一步,重现问题.
开发环境是tomcat部署的,使用了一下四种方式产生的结果:
1.登录之后,点击注销,在线人数得到了更新
2.登录之后不做任何操作,等待session失效,在线人数得到了更新
3.登录之后关闭浏览器,等待session失效,在线人数得到了更新
4.登录之后,打开另一个浏览器,当前用户的在线人数信息得到了更新
在开发环境无法重现此问题,我们转战到生产环境:
1.登录一个用户,不做任何操作,等待到了session失效时间,在线人数没有更新---经测试,进入sessionDestroyed()方法.没有进入attributeRemoveded()方法
2.登录一个用户,关闭浏览器,在人数没有得到更新,等待session到失效时间--经过测试,没有进入sessionDestroyed()方法.没有进入attributeRemoveded()方法
3.登录一个用户,点击注销,在线人数得到更新
4.登录一个用户,打开另一个浏览器再次登录,在线人数用户信息没有得到更新.
5.登录一个用户,不做任何操作,等待session失效后,此时在线人数没有得到更新.再次登录此用户,在线人数没有更新.点击注销,在线人数得到更新.
第二步,分析问题.
也就是说在websphere上面,只有主动调用invalidate(),进入监听方法,执行操作.
问题来了,当到了到了session失效时间之后,有三种可能性:
1.session并没有失效;
2.session失效了,但是没有进入监听执行sessionDestroyed()方法
3.session失效了,也进入了sessionDestroyed()方法,但是此时已经获取不到session了,所以无法执行移除操作.
测试发现
1.登录一个用户,不做任何操作---经测试,进入sessionDestroyed()方法.没有进入attributeRemoveded()方法.在web.xml设置失效时间为2,但是时间是30min才执行的.
2.登录一个用户,关闭浏览器,等待session到失效时间 ---经过测试,没有进入sessionDestroyed()方法.没有进入attributeRemoveded()方法 liuqian15:50关闭浏览器在web.xml设置失效时间为2,但是时间是90min才执行的.
When timeout, server executerequest.getRemoteUser()
and re-login this user automatically, and sent aNew JESSIONID to user browser by including below header.
Set-Cookie JSESSIONID=0000O0OB4W_4sxtn6elSmolMxI9:-1; Path=/; HttpOnly
3.不知道是那个用户进入sessionDestroyed()方法和attributeRemoveded()方法.但是sessionuser为空.并没有执行移除操作.
4.更多的情况是不进入这几个方法.
综上所述:session注意的点有如下:
1.覆盖机制
3.在was上,session失效时间的不确定性.
最终的解决办法是再实现一个过滤器,每次客户端发送请求是,先将所有session检查一遍,使用系统当前时间与最后一次操作时间相比,如果差大于session的有效时间.则主动调用session的失效方法.这样就会正常触发监听方法.达到人数统计的目的.
贴上代码:
package com.sunshine.monitor.comm.filter;import java.io.IOException;import java.util.Map;import java.util.Map.Entry;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.atomic.AtomicInteger;import java.util.concurrent.locks.ReentrantLock;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.HttpServletResponse;import javax.servlet.http.HttpSession;import javax.servlet.http.HttpSessionAttributeListener;import javax.servlet.http.HttpSessionBindingEvent;import javax.servlet.http.HttpSessionEvent;import javax.servlet.http.HttpSessionListener;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.sunshine.core.util.DateUtil;import com.sunshine.monitor.comm.bean.UserSession;import com.sunshine.monitor.comm.startup.AppSystemListener;import com.sunshine.monitor.system.manager.bean.SysUser;public class OnlineUserListenerimplementsHttpSessionListener, HttpSessionAttributeListener, Filter {private Logger log = LoggerFactory.getLogger(OnlineUserListener.class);/** * 用户Session对象 */public static final String USER_SESSION_NAME = "userSession";/** * 在线人数 */public static AtomicInteger lineCount = new AtomicInteger(0);private final ReentrantLock lock = new ReentrantLock();public final static String LISTENER_NAME = "_login";private static volatile Map<String, SysUser> users = new ConcurrentHashMap<String, SysUser>();private static volatile Map<String, HttpSession> sessions = new ConcurrentHashMap<String, HttpSession>();/** * 需要人工添加属性_login,触发此方法 */@Overridepublic void attributeAdded(HttpSessionBindingEvent hbe) {if(LISTENER_NAME.equals(hbe.getName())){// 用户登录成功后,将HttpSession存储到一个map.HttpSession session = hbe.getSession();//System.out.println(hbe.getName() + "-HttpSessionAttributeListener(attributeAdded)...........");SysUser user = (SysUser) hbe.getValue();String yhdh = user.getYhdh();final ReentrantLock _lock = this.lock;_lock.lock();try {boolean flag = users.containsKey(yhdh);users.put(yhdh, user);sessions.put(yhdh, session);if(!flag){lineCount.addAndGet(1); // 累加器}System.out.println(yhdh + "-HttpSessionAttributeListener(attributeAdded)..........." + lineCount.intValue());} finally {_lock.unlock();}}}/** * 需要人工删除属性_login,触发此方法 */@Overridepublic void attributeRemoved(HttpSessionBindingEvent hbe) {if(LISTENER_NAME.equals(hbe.getName())){System.out.println(hbe.getName() + "-HttpSessionAttributeListener(attributeRemoved)...........");SysUser user = (SysUser) hbe.getValue();if(user == null)return;String yhdh = user.getYhdh();final ReentrantLock _lock = this.lock;_lock.lock();try{if(users.containsKey(yhdh)){users.remove(user.getYhdh());sessions.remove(user.getYhdh());lineCount.decrementAndGet();System.out.println(yhdh + "-HttpSessionAttributeListener(attributeRemoved)..........." + lineCount.intValue());}} finally {_lock.unlock();}}}@Overridepublic void attributeReplaced(HttpSessionBindingEvent hbe) {}/** * 会话创建时执行 */@Overridepublic void sessionCreated(HttpSessionEvent he) {/*HttpSession session = he.getSession();UserSession userSession = (UserSession)session.getAttribute(USER_SESSION_NAME);if(userSession != null){final ReentrantLock _lock = this.lock;_lock.lock();//System.out.println(he.getSource()+ "-HttpSessionListener(sessionCreated)...........");SysUser user = userSession.getSysuser();if(user == null) return;String yhdh =user.getYhdh();try{boolean flag = users.containsKey(yhdh);if(!flag){users.put(yhdh, user);lineCount.addAndGet(1); // 累加器//System.out.println(yhdh+ "-HttpSessionListener(sessionCreated)..........." + lineCount.intValue());}} finally {_lock.unlock();}}*/}/** * 会话消毁执行 */@Overridepublic void sessionDestroyed(HttpSessionEvent he) {HttpSession session = he.getSession();UserSession userSession = (UserSession)session.getAttribute(USER_SESSION_NAME);if(userSession != null){log.info(he.getSource()+ "-HttpSessionListener(sessionDestroyed)...........");SysUser user = userSession.getSysuser();String yhdh = user.getYhdh();final ReentrantLock _lock = this.lock;_lock.lock();try{if(users.containsKey(yhdh)){users.remove(user.getYhdh());sessions.remove(user.getYhdh());lineCount.decrementAndGet();log.info(yhdh+ "-HttpSessionListener(sessionDestroyed)..........." + lineCount.intValue());}} finally {_lock.unlock();}}}public static Map<String, SysUser> getUsers() {return users;}public static void setUsers(Map<String, SysUser> vaildUsers) {users = vaildUsers;}public static Map<String, HttpSession> getSessions() {return sessions;}public static void setSessions(Map<String, HttpSession> vaildSessions) {sessions = vaildSessions;}@Overridepublic void destroy() {}@Overridepublic void doFilter(ServletRequest arg0, ServletResponse arg1,FilterChain arg2) throws IOException, ServletException {//HttpServletRequest request = (HttpServletRequest)arg0;//HttpSession session = request.getSession(false);for(Entry<String, HttpSession> entry : sessions.entrySet()){HttpSession ss = entry.getValue();if(ss!=null){log.info(ss.getId());log.info("系统当前时间:"+DateUtil.longToTime(System.currentTimeMillis()));log.info("最后一次操作时间:"+DateUtil.longToTime(ss.getLastAccessedTime()));// 系统当前时间-最后操作时间>则强制使session失效.if((System.currentTimeMillis() - ss.getLastAccessedTime())>ss.getMaxInactiveInterval()*1000){// session timeoutlog.info("强制使session:"+ss.getId()+"失效...");ss.invalidate();}}else{System.out.println("This session is null....");}}arg2.doFilter(arg0, arg1);}@Overridepublic void init(FilterConfig arg0) throws ServletException {// TODO Auto-generated method stub}}
- 依赖于session的在线人数统计
- session统计在线人数
- Session的监听,统计网站在线人数
- Session、SessionListener、在线人数统计
- Session实现统计在线人数
- java session统计在线人数
- java统计在线人数(session存储信息的)
- JSP基于session,application的在线人数统计
- Session监听统计网站的在线人数HashSet
- Session监听在线统计人数,简单的单点登录
- session监听器,用于统计在线人数
- JavaWeb中的Session、SessionListener、在线人数统计
- JavaWeb中的Session、SessionListener、在线人数统计
- JavaWeb中的Session、SessionListener、在线人数统计
- Session应用问题-统计在线人数
- JavaWeb中的Session、SessionListener、在线人数统计
- JavaWeb中的Session、SessionListener、在线人数统计
- JavaWeb中的Session、SessionListener、在线人数统计
- hsf dubbo学习六--泛化,回声测试,上下文信息,隐式传参,异步调用,本地调用
- IntelliJ IDEA平台下JNI编程(二)—类型映射
- ubuntu下安装深度学习python工具包keras
- 菜单查询,关联别的表
- 高斯——克吕格投影正算
- 依赖于session的在线人数统计
- CycleRotationView:自定义控件之轮播图
- ArcGis点抽稀方法
- idea 自动导包 设置
- 一个简单的linux命令——which
- linux 如何快速的查找日志中你所要查找的信息
- Font Awesome可缩放的矢量图标
- png.9 图片编辑边框细节操作小计
- 七种跨域方法【4.script篇】