依赖于session的在线人数统计

原创 2017年01月03日 17:38:41

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

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

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 OnlineUserListener
		implements
			HttpSessionListener, 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,触发此方法
	 */
	@Override
	public 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,触发此方法
	 */
	@Override
	public 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();
			}
		}
	}

	@Override
	public void attributeReplaced(HttpSessionBindingEvent hbe) {
		
	}
	
	/**
	 * 会话创建时执行
	 */
	@Override
	public 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();
			}
		}*/
	}
	/**
	 * 会话消毁执行
	 */
	@Override
	public 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;
	}

	@Override
	public void destroy() {
		
		
	}

	@Override
	public 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 timeout
					log.info("强制使session:"+ss.getId()+"失效...");
					ss.invalidate();
				}
			}else{
				System.out.println("This session is null....");
			}
		}
		arg2.doFilter(arg0, arg1);
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		// TODO Auto-generated method stub
		
	}
}


HttpSessionListener实现网站在线人数统计

在网站中经常需要进行在线人数的统计。过去的一般做法是结合登录和退出功能,即当用户输入用户名密码进行登录的时候计数器加1,然后当用户点击退出按钮退出系统的时候计数器减1。这种处理方式存在一些缺点,例如:...
  • g893465244
  • g893465244
  • 2014年11月05日 18:11
  • 1867

session监听器(可用于统计在线人数,了解系统当前的使用客户 ...

servlet中对session的监听有很多接口,功能很灵活,最常用的是监听Session和Attribute。这里要澄清一下概念,servlet中的session监听和Attribute监听含义有差...
  • mywordandyourword
  • mywordandyourword
  • 2014年02月05日 10:44
  • 1310

session统计在线人数,查看游客列表,登录人列表

1.首先创建一个类MySessionListener并且实现HttpSessionListener,       ServletContextListener,ServletContextAttr...
  • yangyz_love
  • yangyz_love
  • 2012年08月31日 16:50
  • 5677

CSDN,你在忽悠谁?——社区用户数量大曝光

          根据http://hi.csdn.net提供的好友搜索功能,按地区搜索结果得出,csdn社区用户数量是国内66964,国外用户总数不超过1千。详细清单请点击。    根据csdn官...
  • yizia
  • yizia
  • 2007年12月29日 14:07
  • 3222

--Listener (三)使用Listener来实现显示当前在线人数

主要的思路是:每当用户第一次访问web应用的时候都会创建一个session来跟踪会话,可以监听session的变化来记录当前在线的人数。每当创建session的时候,人数+1,当session销毁的时...
  • qq_25201665
  • qq_25201665
  • 2016年11月07日 21:07
  • 719

java web监听器统计在线用户及人数

在线用户使用HttpSessionListener监听器统计   每当一个session会话建立  在线用户人数+1 每当一个session会话销毁 在线用户人数-1 使用Servlet...
  • su20145104009
  • su20145104009
  • 2016年11月18日 16:04
  • 7985

spring-boot通过HttpSessionListener监听器统计在线人数

spring-boot通过HttpSessionListener监听器统计在线人数
  • zhangjq520
  • zhangjq520
  • 2016年12月15日 15:13
  • 5768

javaEE之--------统计网站在线人数,安全登录等(观察者设计模式)

总体介绍下:  监听器:监听器-就是一个实现待定接口的普通Java程序,此程序专门用于监听别一个类的方法调用。都是使用观察者设计模式。 小弟刚接触这个,做了些简单的介绍,大神请绕道,技术只是一...
  • yangxin_blog
  • yangxin_blog
  • 2016年01月02日 13:10
  • 3684

关于spring session redis共享session的一个坑

关于spring session redis共享session的一个坑,这两天写spring session redis发现几个小问题,挨个絮叨絮叨...
  • zdsdiablo
  • zdsdiablo
  • 2015年12月29日 17:44
  • 33918

实现统计在线用户的几种方式

一,每次用户操作更新其在线时间 这个方法很直接,在用户表里加一个字段update_time,每次用户进行操作,都更新这个字段为当前时间,一般是在一个被所有Action继承的基类里写这个操作。 然后...
  • sb___itfk
  • sb___itfk
  • 2014年09月02日 19:20
  • 3027
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:依赖于session的在线人数统计
举报原因:
原因补充:

(最多只允许输入30个字)