依赖于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.覆盖机制

1.Server LevelLowest level
 
 2.Enterprise ApplicationOverrides the Server Level if Override is selected3.Web ApplicationOverrides the Server and Enterprise Application settings ifOverride is selected4.Application Level (web.xml)Overrides Server, Enterprise Application, and Web Application settings5.Application CodeOverrides all other settings
2.session失效后,调用监听方法的时间不确定性.

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}}


1 0
原创粉丝点击