9. 实战之基于WebListener实现实时在线人数统计

一灰灰blogSpringBootWEB系列应用篇Listener约 894 字大约 3 分钟

很多pc网站都有一个实时在线人数的统计功能,那么一般这种是采用什么方式来实现的呢? 这里我们介绍一个最基础的是实现方式,基于Session结合WebListener来实现在线人数统计

项目配置

1. 依赖

首先搭建一个标准的SpringBoot项目工程,相关版本以及依赖如下

本项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

2. 启动入口

我们使用默认的配置进行测试,因此启动入口也可以使用最基础的

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

3. web配置

我们主要根据用户的session来创建与销毁来判断是否有新的用户访问站点、以及长时间没有访问之后认为已经离线,为了简化这个注销的模拟过程,我们将session的生命周期设置短一点

server:
  port: 8081
  servlet:
    session:
      timeout: 1m # 设置1分钟的有效时间

在线人数统计实现

接下来我们看一下具体的实现思路:

  • 借助Servelt的Listener机制,主要监听Session的创建与销毁
  • 当session创建时,认为新来一个用户,计数+1
  • 当session销毁时,认为用户已经离开,或者长时间没有访问,计数-1

1. 计数服务

一个简单基础的计数服务,借助 AtomicInteger 来实现计数统计(为啥不直接是int ?)

@Service
public class CountService {

    private AtomicInteger cnt = new AtomicInteger(0);

    public void incr(int cnt) {
        this.cnt.addAndGet(cnt);
    }

    public int getOnlineCnt() {
        return cnt.get();
    }

}

2. Session监听器

自定义一个Session的监听器,监听HttpSession的相关操作

@WebListener
public class LoginUserCountListener implements HttpSessionListener {
    @Autowired
    private CountService countService;

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        System.out.println("--------- 新增一个用户 ------- session = " + se.getSession().getId());
        HttpSessionListener.super.sessionCreated(se);
        countService.incr(1);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        System.out.println("--------- 销毁一个用户 -----------" + se.getSession().getId() + " = " + se.getSession().getAttribute("name"));
        HttpSessionListener.super.sessionDestroyed(se);
        countService.incr(-1);
    }
}

3. 登录登出接口

最后再设计一个登录、登出、查询实时在线人数的统计接口

@RestController
public class LoginController {
    @Autowired
    private CountService countService;

    @RequestMapping(path = "/login")
    public String login(String uname, HttpSession httpSession) {
        httpSession.setAttribute("name", uname);
        System.out.println("登录成功:" + uname);
        return "欢迎登录:" + uname + ", 当前在线人数: " + countService.getOnlineCnt();
    }


    /**
     * 查询当前在线人数
     *
     * @param session
     * @return
     */
    @RequestMapping("online")
    public String showOnlineUser(HttpSession session) {
        return session.getAttribute("name") + " ,当前时间为:" + LocalDateTime.now() + " 在线人数:" + countService.getOnlineCnt();
    }

    @RequestMapping(path = "logout")
    public String logout(HttpSession httpSession) {
        // 注销当前的session
        httpSession.invalidate();
        return "登出成功, 当前在线人数: " + countService.getOnlineCnt();
    }
}

接下来验证一下,实时在线人数统计情况

4. 小结

上面虽然是实现了实时在线人数统计,但是存在一个非常明显的短板问题,那就是只适用于单机的场景,如果后台有多个服务部署,那应该怎么处理呢?

基于此,自然而然想到的就是分布式session 结合 redis 计数来实现,但是这个思路可行么? 分布式session失效会抛出一个事件么?或许通过监听redis的key失效能处理,但是整体来看,还是有些麻烦,有没有更简单实用的场景呢

且待下文详解

不能错过的源码和相关知识点

0. 项目

Loading...