这里我整理了几种方法:
方法一:通过监听器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.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</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 WebSocket</title>
</head>
<body>
<h2 οnclick="openTanTan()" style="color:dodgerblue;text-align: center">WeTanTan</h2>
<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!<p id='username'>"+username+"</p>");
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("<span style='font-size: 20px;color:greenyellow;font-weight: bold'>" + "Bye~~" + "</span>");
};
ws.onerror = function () {
sendInnerHtml("websocket error");
};
ws.onopen = function (event) {
sendInnerHtml("Welcome to WeTanTan Light social web! " + "<span style='font-size: 30px;font-weight: bold;color:orange'>" + username + "</span>");
};
ws.onmessage = function (e) {
console.log(e)
document.getElementById("count-users").innerHTML = e.data + "人";
sendInnerHtml('<br/>' + "127.0.0.1" + ":" + e.data);
};
//监听方法,监听websocket关闭
window.onbeforeunload = function () {
ws.close();
};
}
function sendInnerHtml(innerHtml) {
document.getElementById("content").innerHTML += innerHtml + '<br/>';
};
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 = '<br/>' + username + ":" + message;
document.getElementById("input-message").value = "";
};
</script>
</html>
方法三参考文章:
总结:如果需求需要特别实时同步人数的话,建议使用webSocket来实现,如果需求能够接受在线人数的偏差,即关闭浏览器人数的偏差,可以使用方法二