springboot 用监听器统计在线人数【测试通过】

本文在springboot 的项目,用HttpSessionListener 监听器(监听器的其中一种) 统计在线人数,实质是统计session 的数量。

 思路很简单,但是有个细节没处理好,让我调试了大半天,才把bug搞好。

 先写个HttpSessionListener 监听器。count  是session的数量(人数),session 创建的时候,会触发监听器的sessionCreated 方法,session销毁的时候,会触发监听器的sessionDestroyed 方法。 在监听器中计算完人数count,把他放进servletContext(可以理解为一个仓库,任意请求可以存储和获取里面的属性)。

 注意监听器加上@WebListener,这样就不用配置。

但启动类要加上注解@ServletComponentScan,这样才能扫描到监听器

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@WebListener

public class OnLineCount implements HttpSessionListener {

    public int count=0;//记录session的数量

    //监听session的创建,synchronized 防并发bug

    public synchronized void sessionCreated(HttpSessionEvent arg0) {

        System.out.println("【HttpSessionListener监听器】count++  增加");

        count++;

        arg0.getSession().getServletContext().setAttribute("count", count);

  

    }

    @Override

    public synchronized void sessionDestroyed(HttpSessionEvent arg0) {//监听session的撤销

        System.out.println("【HttpSessionListener监听器】count--  减少");

        count--;

        arg0.getSession().getServletContext().setAttribute("count", count);

    }

}

接着写一个查询session 数量的controller,我开始的时候是像下面这样写的,是错误的!

从servletContext 中取出count ,把count返回前端。

1

2

3

4

5

6

@RequestMapping("/count")

@ResponseBody

public String count(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){

    Object count=httpServletRequest.getServletContext().getAttribute("count");

    return "count : "+count;

}

这样是错误的,测试你会发现,页面看到count  是null ,因为没有创建session,没有触发监听器的统计方法。于是改一下:

1

2

3

4

5

6

7

8

9

10

@Controller

public class IndexController {

    @RequestMapping("/count")

    @ResponseBody

    public String count(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){

        HttpSession session = httpServletRequest.getSession();

        Object count=session.getServletContext().getAttribute("count");

        return "count : "+count;

    }

}

 

 HttpSession session = httpServletRequest.getSession();  作用:该用户如果没有sesision则创建session ,有则取得session不创建。

 改成这样测试,看起来是对的,但是有个问题。一个浏览器对应一个session,你打开2个浏览器,看到count是2 ,是对的。但是你关了一个浏览器,再打开,应该是2不变才对,但是变成3 了,原因是session销毁的方法没有执行,重新打开时,服务器找不到用户原来的session ,重新创建了一个session,于是有3个session了,但是浏览器只有2个,也就是模拟应该是只有2个人在线上。

 有2个方法可以解决这个问题,一个是在关闭网页的时候,前端去调用一个方法把session销毁。另一个更好的方法是,让服务器记得原来那个session,即把原来的sessionId 记录在浏览器,下次打开时,把这个sessionId发送过去,这样服务器就不会重新创建。

代码修改如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@Controller

public class IndexController {

    @RequestMapping("/count")

    @ResponseBody

    public String number(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){

        try{  //把sessionId记录在浏览器

            Cookie c = new Cookie("JSESSIONID", URLEncoder.encode(httpServletRequest.getSession().getId(), "utf-8"));

            c.setPath("/");

            //先设置cookie有效期为2天,不用担心,session不会保存2天

            c.setMaxAge( 48*60 60);

            httpServletResponse.addCookie(c);

        }catch (Exception e){

            e.printStackTrace();

        }

  

        HttpSession session = httpServletRequest.getSession();

        Object count=session.getServletContext().getAttribute("count");

        return "count : "+count;

    }

}

测试达到效果。

然后关闭浏览器,发现没有触发sessionDestroyed,然后怀疑是不是这个监听器的机制有问题,等了好几分钟都没有反应。
后来想起session是有一个超时时间的,浏览器关闭其实对于web服务器来说是不知道的,所以他需要等待超时时间到了之后自动销毁,上面关闭浏览器之后只所以没有促发sessionDesroyed,就是因为默认的超时时间没到。
默认超时时间太长了,所以在sessionCreated中添加如下代码,改小超时时间:
        arg0.getSession().setMaxInactiveInterval(15);
这样只要用浏览器访问该站点,然后15s不刷新之后,sessionDestroyed就会被自动调用了。一般不用设置这个超时时间,到了一定时间会自动销毁。

--- end ---

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
当使用Spring Boot项目时,你可以使用Redis作为缓存或数据存储的解决方案。以下是一个示例,展示了如何在Spring Boot项目中使用Redis集合存储数据: 1. 首先,确保你的Spring Boot项目中已经添加了Redis依赖。在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 2. 在application.properties或application.yml文件中配置Redis连接信息。例如: ```yaml spring.redis.host=localhost spring.redis.port=6379 ``` 3. 创建一个Redis配置类,用于配置Redis连接和操作。在该类上使用`@Configuration`注解,并添加`@EnableCaching`注解启用缓存。 ```java import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @EnableCaching public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); return template; } } ``` 4. 创建一个服务类,用于操作Redis集合存储数据。在该类中注入RedisTemplate,并使用`opsForSet()`方法获取集合操作对象。 ```java import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.Set; @Service public class RedisSetService { @Resource private RedisTemplate<String, Object> redisTemplate; public void addToSet(String key, Object value) { redisTemplate.opsForSet().add(key, value); } public Set<Object> getSet(String key) { return redisTemplate.opsForSet().members(key); } public void removeFromSet(String key, Object value) { redisTemplate.opsForSet().remove(key, value); } } ``` 这样,你就可以在其他组件或服务中使用`RedisSetService`类来操作Redis集合数据了。例如: ```java @Service public class ExampleService { @Resource private RedisSetService redisSetService; public void example() { String setKey = "mySet"; redisSetService.addToSet(setKey, "value1"); redisSetService.addToSet(setKey, "value2"); Set<Object> setValues = redisSetService.getSet(setKey); System.out.println("Set values: " + setValues); redisSetService.removeFromSet(setKey, "value1"); setValues = redisSetService.getSet(setKey); System.out.println("Updated set values: " + setValues); } } ``` 这是一个简单的示例,展示了如何在Spring Boot项目中使用Redis集合存储数据。你可以根据自己的需求进行相应的修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值