依赖于session的在线人数统计
最近工作中遇到一个问题,
在做在线人数统计时.我们实现了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 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的失效方法.这样就会正常触发监听方法.达到人数统计的目的.
贴上代码:
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
}
}
还没有评论,来说两句吧...