依赖于session的在线人数统计

淩亂°似流年 2022-07-13 08:47 244阅读 0赞

最近工作中遇到一个问题,

在做在线人数统计时.我们实现了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.

  1. Set-Cookie JSESSIONID=0000O0OB4W_4sxtn6elSmolMxI9:-1; Path=/; HttpOnly

3.不知道是那个用户进入sessionDestroyed()方法和attributeRemoveded()方法.但是sessionuser为空.并没有执行移除操作.

4.更多的情况是不进入这几个方法.

综上所述:session注意的点有如下:

1.覆盖机制










1. Server Level Lowest level



































 





 
2. Enterprise Application Overrides the Server Level if Override is selected
3. Web Application Overrides the Server and Enterprise Application settings ifOverride is selected
4. Application Level (web.xml) Overrides Server, Enterprise Application, and Web Application settings
5. Application Code Overrides all other settings

2.session失效后,调用监听方法的时间不确定性.

3.在was上,session失效时间的不确定性.

最终的解决办法是再实现一个过滤器,每次客户端发送请求是,先将所有session检查一遍,使用系统当前时间与最后一次操作时间相比,如果差大于session的有效时间.则主动调用session的失效方法.这样就会正常触发监听方法.达到人数统计的目的.

贴上代码:

  1. package com.sunshine.monitor.comm.filter;
  2. import java.io.IOException;
  3. import java.util.Map;
  4. import java.util.Map.Entry;
  5. import java.util.concurrent.ConcurrentHashMap;
  6. import java.util.concurrent.atomic.AtomicInteger;
  7. import java.util.concurrent.locks.ReentrantLock;
  8. import javax.servlet.Filter;
  9. import javax.servlet.FilterChain;
  10. import javax.servlet.FilterConfig;
  11. import javax.servlet.ServletException;
  12. import javax.servlet.ServletRequest;
  13. import javax.servlet.ServletResponse;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. import javax.servlet.http.HttpSession;
  17. import javax.servlet.http.HttpSessionAttributeListener;
  18. import javax.servlet.http.HttpSessionBindingEvent;
  19. import javax.servlet.http.HttpSessionEvent;
  20. import javax.servlet.http.HttpSessionListener;
  21. import org.slf4j.Logger;
  22. import org.slf4j.LoggerFactory;
  23. import com.sunshine.core.util.DateUtil;
  24. import com.sunshine.monitor.comm.bean.UserSession;
  25. import com.sunshine.monitor.comm.startup.AppSystemListener;
  26. import com.sunshine.monitor.system.manager.bean.SysUser;
  27. public class OnlineUserListener
  28. implements
  29. HttpSessionListener, HttpSessionAttributeListener, Filter {
  30. private Logger log = LoggerFactory.getLogger(OnlineUserListener.class);
  31. /**
  32. * 用户Session对象
  33. */
  34. public static final String USER_SESSION_NAME = "userSession";
  35. /**
  36. * 在线人数
  37. */
  38. public static AtomicInteger lineCount = new AtomicInteger(0);
  39. private final ReentrantLock lock = new ReentrantLock();
  40. public final static String LISTENER_NAME = "_login";
  41. private static volatile Map<String, SysUser> users = new ConcurrentHashMap<String, SysUser>();
  42. private static volatile Map<String, HttpSession> sessions = new ConcurrentHashMap<String, HttpSession>();
  43. /**
  44. * 需要人工添加属性_login,触发此方法
  45. */
  46. @Override
  47. public void attributeAdded(HttpSessionBindingEvent hbe) {
  48. if(LISTENER_NAME.equals(hbe.getName())){
  49. // 用户登录成功后,将HttpSession存储到一个map.
  50. HttpSession session = hbe.getSession();
  51. //System.out.println(hbe.getName() + "-HttpSessionAttributeListener(attributeAdded)...........");
  52. SysUser user = (SysUser) hbe.getValue();
  53. String yhdh = user.getYhdh();
  54. final ReentrantLock _lock = this.lock;
  55. _lock.lock();
  56. try {
  57. boolean flag = users.containsKey(yhdh);
  58. users.put(yhdh, user);
  59. sessions.put(yhdh, session);
  60. if(!flag){
  61. lineCount.addAndGet(1); // 累加器
  62. }
  63. System.out.println(yhdh + "-HttpSessionAttributeListener(attributeAdded)..........." + lineCount.intValue());
  64. } finally {
  65. _lock.unlock();
  66. }
  67. }
  68. }
  69. /**
  70. * 需要人工删除属性_login,触发此方法
  71. */
  72. @Override
  73. public void attributeRemoved(HttpSessionBindingEvent hbe) {
  74. if(LISTENER_NAME.equals(hbe.getName())){
  75. System.out.println(hbe.getName() + "-HttpSessionAttributeListener(attributeRemoved)...........");
  76. SysUser user = (SysUser) hbe.getValue();
  77. if(user == null)
  78. return;
  79. String yhdh = user.getYhdh();
  80. final ReentrantLock _lock = this.lock;
  81. _lock.lock();
  82. try{
  83. if(users.containsKey(yhdh)){
  84. users.remove(user.getYhdh());
  85. sessions.remove(user.getYhdh());
  86. lineCount.decrementAndGet();
  87. System.out.println(yhdh + "-HttpSessionAttributeListener(attributeRemoved)..........." + lineCount.intValue());
  88. }
  89. } finally {
  90. _lock.unlock();
  91. }
  92. }
  93. }
  94. @Override
  95. public void attributeReplaced(HttpSessionBindingEvent hbe) {
  96. }
  97. /**
  98. * 会话创建时执行
  99. */
  100. @Override
  101. public void sessionCreated(HttpSessionEvent he) {
  102. /*HttpSession session = he.getSession();
  103. UserSession userSession = (UserSession)session.getAttribute(USER_SESSION_NAME);
  104. if(userSession != null){
  105. final ReentrantLock _lock = this.lock;
  106. _lock.lock();
  107. //System.out.println(he.getSource()+ "-HttpSessionListener(sessionCreated)...........");
  108. SysUser user = userSession.getSysuser();
  109. if(user == null) return;
  110. String yhdh =user.getYhdh();
  111. try{
  112. boolean flag = users.containsKey(yhdh);
  113. if(!flag){
  114. users.put(yhdh, user);
  115. lineCount.addAndGet(1); // 累加器
  116. // System.out.println(yhdh+ "-HttpSessionListener(sessionCreated)..........." + lineCount.intValue());
  117. }
  118. } finally {
  119. _lock.unlock();
  120. }
  121. }*/
  122. }
  123. /**
  124. * 会话消毁执行
  125. */
  126. @Override
  127. public void sessionDestroyed(HttpSessionEvent he) {
  128. HttpSession session = he.getSession();
  129. UserSession userSession = (UserSession)session.getAttribute(USER_SESSION_NAME);
  130. if(userSession != null){
  131. log.info(he.getSource()+ "-HttpSessionListener(sessionDestroyed)...........");
  132. SysUser user = userSession.getSysuser();
  133. String yhdh = user.getYhdh();
  134. final ReentrantLock _lock = this.lock;
  135. _lock.lock();
  136. try{
  137. if(users.containsKey(yhdh)){
  138. users.remove(user.getYhdh());
  139. sessions.remove(user.getYhdh());
  140. lineCount.decrementAndGet();
  141. log.info(yhdh+ "-HttpSessionListener(sessionDestroyed)..........." + lineCount.intValue());
  142. }
  143. } finally {
  144. _lock.unlock();
  145. }
  146. }
  147. }
  148. public static Map<String, SysUser> getUsers() {
  149. return users;
  150. }
  151. public static void setUsers(Map<String, SysUser> vaildUsers) {
  152. users = vaildUsers;
  153. }
  154. public static Map<String, HttpSession> getSessions() {
  155. return sessions;
  156. }
  157. public static void setSessions(Map<String, HttpSession> vaildSessions) {
  158. sessions = vaildSessions;
  159. }
  160. @Override
  161. public void destroy() {
  162. }
  163. @Override
  164. public void doFilter(ServletRequest arg0, ServletResponse arg1,
  165. FilterChain arg2) throws IOException, ServletException {
  166. //HttpServletRequest request = (HttpServletRequest)arg0;
  167. //HttpSession session = request.getSession(false);
  168. for(Entry<String, HttpSession> entry : sessions.entrySet()){
  169. HttpSession ss = entry.getValue();
  170. if(ss!=null){
  171. log.info(ss.getId());
  172. log.info("系统当前时间:"+DateUtil.longToTime(System.currentTimeMillis()));
  173. log.info("最后一次操作时间:"+DateUtil.longToTime(ss.getLastAccessedTime()));
  174. // 系统当前时间-最后操作时间>则强制使session失效.
  175. if((System.currentTimeMillis() - ss.getLastAccessedTime())>ss.getMaxInactiveInterval()*1000){// session timeout
  176. log.info("强制使session:"+ss.getId()+"失效...");
  177. ss.invalidate();
  178. }
  179. }else{
  180. System.out.println("This session is null....");
  181. }
  182. }
  183. arg2.doFilter(arg0, arg1);
  184. }
  185. @Override
  186. public void init(FilterConfig arg0) throws ServletException {
  187. // TODO Auto-generated method stub
  188. }
  189. }

发表评论

表情:
评论列表 (有 0 条评论,244人围观)

还没有评论,来说两句吧...

相关阅读

    相关 监听器:统计在线人数

            接着,我来写一个监听器的案例来巩固学习监听器的知识,便于日后的查阅和复习。大概分为以下几个步骤: 1.编写统计人数的Servlet,实现特定的监听器接口 2

    相关 使用 Redis 统计在线用户人数

    在构建应用的时候, 我们经常需要对用户的一举一动进行记录, 而其中一个比较重要的操作, 就是对在线的用户进行记录。 本文将介绍四种使用 Redis 对在线用户进行记录的方