springboot 在线人数显示整理(HttpSessionListener,Redis,WebSocket)

这里我整理了几种方法:

方法一:通过监听器HttpSessionListener

步骤一:编写监听器

//实现HttpSessionListener接口
@WebListener
public class OnlineUserListener implements HttpSessionListener {
    public static List<String> list = new ArrayList<String>();

    //监听session的创建,synchronized 防并发bug
    @Override
    public synchronized void sessionCreated(HttpSessionEvent httpSessionEvent) {
        HttpSession session = httpSessionEvent.getSession();
        System.out.println("【HttpSessionListener监听器】list  增加");
        list.add(session.getId());
        session.getServletContext().setAttribute("count", list.size());
    }

    @Override
    public synchronized void sessionDestroyed(HttpSessionEvent httpSessionEvent) {//监听session的撤销
        HttpSession session = httpSessionEvent.getSession();
        System.out.println("【HttpSessionListener监听器】list  减少");
        list.remove(session.getId());
        session.getServletContext().setAttribute("count", list.size());
    }
}

步骤二:编写Controller:

@RestController
@Slf4j
public class OnlineUsersController {

    /**
     * 在线人数
     *
     * @return
     */
    @PostMapping("/getOnlineCount")
    public Integer getOnlineUsersStatus(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();
        Integer count = (Integer) session.getAttribute("count");
        log.debug(String.valueOf(count));
        return count;
    }
}

当用户打开浏览器,获取session,进行一个在线人数的展示,但是这个这个方法有两个弊端:

第一个问题:

当用户只是打开浏览器了,就已经有人数的显示,此时用户并没有登录网站

第二个问题:

当用户登录成功过,由于session的有效时间为30分钟,所以,在30分钟内,这个人数依旧是不准确的

方法二:通过读取Redis中登录成功的用户数量

步骤一:在配置文件中添加

# redis配置

spring.redis.host=192.168.8.92
spring.redis.port=6379

步骤二:编写Controller层

@RestController
@Slf4j
public class OnlineUsersContrller {
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 登录-获取在线人数
     *
     * @return
     */
    @PostMapping("/getOnlineCount")
    public Result<Integer> getRedis() {
        //获得所有的key
        Set<String> keys = redisTemplate.keys("sys:cache:user::" + "*");
        if (keys.size() > 0) {
            return Result.OK(keys.size());
        } else {
            return Result.OK(0);
        }
    }
}

当用户登录成功后,用户信息存储在redis缓存中的时候,通过获取redis中已存在的用户信息的数量,能够实现在线人数的显示。
同样,这个方法也存在一个问题,就是当用户没有点击退出,而是关闭了整个浏览器,redis中不能及时做出响应,虽然一段时间后redis能够自动做出处理,清理长时间无操作的用户信息,但是这个也并不进行实时效果

方法三:通过WebSocket实现实时展示在线人数

步骤一:导入依赖

		<dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-websocketartifactId>
        dependency>

步骤二:编写OnlineUserCountWebSocket类

@Component
@ServerEndpoint("/websocket")  //该注解表示该类被声明为一个webSocket终端
public class OnlineUserCountWebSocket {

    private Session session;

    //初始在线人数
    private static int onlineNum = 0;
    
    private static CopyOnWriteArraySet<OnlineUserCountWebSocket> userCountWebSockets =
            new CopyOnWriteArraySet<OnlineUserCountWebSocket>();
    
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        userCountWebSockets.add(this);
        addOnlineCount();
    }

    @OnClose
    public void onClose() {
        userCountWebSockets.remove(this);
        subOnlineCount();
    }

    public synchronized int getonlineNum() {
        return OnlineUserCountWebSocket.onlineNum;
    }

    public synchronized int subOnlineCount() {
        return OnlineUserCountWebSocket.onlineNum--;
    }

    public synchronized int addOnlineCount() {
        return OnlineUserCountWebSocket.onlineNum++;
    }

}

步骤三:前端html页面代码

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>My WebSockettitle>
head>
<body>
<h2 οnclick="openTanTan()" style="color:dodgerblue;text-align: center">WeTanTanh2>
<div id="tantanbox" style="width:800px;height:498px;margin:0 auto;">
    <div id="box-left" style="float:left;width:600px;height:500px;">
        <div id="content" style="height:350px;border:1px solid dodgerblue;">div>
        <div id="input-content" style="height:145px;border:1px solid dodgerblue;"><textarea id="input-message"
                                                                                            style="width:99%;height:96%">textarea>
        div>
        <button id="btn1" οnclick="sendMessage()"
                style="width:38px;height:18px;line-height:18px;border:0;margin:4px 0 0 10px;">send
        button>
        <button id="btn2" οnclick="ClosedWebSocket()"
                style="width:38px;height:18px;line-height:18px;border:0;margin:4px 0 0 10px;">close
        button>
    div>
    <div id="box-right" style="float:left;width:195px;height:497px;border:1px solid dodgerblue">
        <span style="font-size:10px;color:dodgerblue;font-weight:bold;">在线人数:span>
        <hr/>
        <div id="count-users" style="font-size:10px;color:dodgerblue;font-weight:bold">div>
    div>

div>
body>
<script>
    var usernames = [];
    var username = "";
    window.onload = function () {
        document.getElementById("tantanbox").style.display = 'none';
    };

    function openTanTan() {
        alert("open your WeTanTan chat!");
        username = window.prompt("输入你的名字:");
        //document.write("welcome to wetantan!

"+username+"

");
usernames.push(username); document.getElementById("tantanbox").style.display = 'block'; /*--------------------开始websocket部分----------- ---------*/ var ws = null;//申请一个websocket对象 //判断当前浏览器是不是支持websocket if ('window' in window) { startWebSocket(); } else { alert("NO SUPPORT!"); return; } }; function startWebSocket() { ws = new WebSocket("ws://localhost:8080/websocket"); document.getElementById("count-users").innerHTML = ""; ws.onclose = function () { sendInnerHtml("" + "Bye~~" + ""); }; ws.onerror = function () { sendInnerHtml("websocket error"); }; ws.onopen = function (event) { sendInnerHtml("Welcome to WeTanTan Light social web! " + "" + username + ""); }; ws.onmessage = function (e) { console.log(e) document.getElementById("count-users").innerHTML = e.data + "人"; sendInnerHtml('
'
+ "127.0.0.1" + ":" + e.data); }; //监听方法,监听websocket关闭 window.onbeforeunload = function () { ws.close(); }; } function sendInnerHtml(innerHtml) { document.getElementById("content").innerHTML += innerHtml + '
'
; }; function ClosedWebSocket() { ws.close(); setTimeout(function () { document.getElementById("tantanbox").style.display = 'none'; }, 1000); }; function sendMessage() { var message = document.getElementById("input-message").value; ws.send(message); document.getElementById("content").innerHTML = '
'
+ username + ":" + message; document.getElementById("input-message").value = ""; };
script> html>

方法三参考文章:https://blog.csdn.net/javaexploreroooo/article/details/52432111

总结:如果需求需要特别实时同步人数的话,建议使用webSocket来实现,如果需求能够接受在线人数的偏差,即关闭浏览器人数的偏差,可以使用方法二

你可能感兴趣的:(项目需求,springboot,websocket,redis,session)