x-blog之实时显示在线登录人数简单介绍

前言

首先怎么才算登录人数,一般规定用户登录后才算,但是登陆后会出现几种情况,用户主动退出登录,长时间不点击和主动关闭浏览器都会导致会话失效,另外并发登录人数限制和管理员在线踢出用户都会影响登录人数
而一般的做法就是写个servlet会话监听器去监听登录,当有会话建立时并且已经成功登录和先判断该用户是否已经正在线,统计相关信息,然后就是退出浏览器只能等会话失效才算离线。用这种方法去做,简单.但是实时效果不是太好但是发现我得session是用shiro管理的,大部分应用离不开shiro,而且shiro的sessionId每次是不一样的,也就是说,同一用户登录,在缓存中通过用户名产生的会话id不一样,造成同一用户登录重新登录会变成不同用户而在线人数不对。
而另一种更好并且统计更准确的是利用redis缓存信息(遇到了瓶颈,也是这个sessionId每次不一样,但是如果把shiro换掉或者做成分布式可以解决),大概是这样的想法,现在redis中放两组key,根据需求一组存放登陆用户的SessionId与ClientUser的Json数据,另一组存放存放登录用户的UID与SessionId对于的数据,然后利用该监听器,,只要一有会话差生,就从redis登记表(封装各类实时统计的方法)中查看,以一定规则查看是否登录,这里可以判断是游客还是会员,然后记录相关登记信息等等,放在session中,这种统计方法更实时,统计内容更多,但是因为shiro的问题,最终统计很不准确,因为这个sessionId还必须是准确才行

会话列表

要能准确的实时统计,得先解决上面涉及的5个影响因素

  1. 并发登录人数控制 核心思想就是利用监听器根据那个用户名得到sessionId然后踢掉,缓存同步(见并发登陆人数控制)
  2. 用户主动退出和会话正常死亡通过监听器解决
    3.关闭浏览器通过缩短会话存活时间保证(不一定合理,因为该项目纯粹娱乐所以可以这样做)
  3. 强制用户退出 根据sessionId让session失效,logout就行了
    不过这里比较棘手的就是的就是那个强制退出的会话怎么清除,如果不清除,缓存中会得到空指针,最头疼的是服务器崩溃,利用缓存中的方法可以得到所有活跃的会话,包括失效的。通过异常删选出失效的会话并删除,最后就剩下正常的会话列表了

当然这时传统的做法,不能完全模拟实时统计,最有效的方法就是用心跳方式解决

码上有戏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
**
* <p>
* 在线人数服务实现类
* </p>
*
* @author slycmiaoxi
* @since 2019-06-23
*/
@Service
@Slf4j
public class ITSessionServiceImpl implements ITSessionService {
@Autowired
private SessionDAO sessionDAO;
@Override
public int countActivesSession() {
return CollectionUtils.isEmpty(listActivesSession()) ? 0 : listActivesSession().size();
}
@Override
public Collection<Session> listActivesSession() {
// 1. 获得当前所有活跃的在线用户
Collection<Session> currentSessionList = sessionDAO.getActiveSessions();
// 2.仍然存在缓存中但已失效的的用户会话
Collection<Session> invalidSessionList = new LinkedList<>();
// 3.筛选出失效的会话
Iterator<Session> it = currentSessionList.iterator();
while (it.hasNext()) {
try {
Session session = it.next();
PrincipalCollection principalCollection =
(PrincipalCollection)session.getAttribute(DefaultSubjectContext.AUTHENTICATED_SESSION_KEY);
invalidSessionList.add(session);
}
catch (Exception e) {
}
}
// 4.删除缓存活跃会话中失效的会话
invalidSessionList.stream().filter(x -> x != null).forEach(x -> {
if (currentSessionList.contains(x)) {
sessionDAO.delete(x);
}
});
// 5. 剩下正常的在线用户
Collection<Session> normalSessionList = sessionDAO.getActiveSessions();
return normalSessionList;
}
@Override
public SessionDAO getSessionDAO() {
return this.sessionDAO;
}
}

热评文章