用socketio做web系统在线用户量统计,和消息提醒

一开始想用session监听器,不过有过期时间,并非实事。而且,如果不用jsp,就需要写个rest服务,供前段轮询调用接口,比如5秒一次,来刷新在线人数。影响性能。后来想到用WebSocket来做。刚好之前有用过socketio来推送消息,于是敲定方案。

我使用的是开源库,https://github.com/mrniko/netty-socketio, 有近3000个star,还是不错的。

因为业务上需求是跟进登录账号来统计,而对登录IP、浏览器不做区分,即同一个账号,无论在哪里登录,总数都算1.基于次,前端连接时只需把登录账号传到后台即可。

1. 后台服务OnlineUserCounter.java:

public class OnlineUserCounter {

private static SocketIOServer socketServer = null;
/**
* 在线用户数
*/
public static Set<String> userSet = new HashSet<>(500);
private static JSONObject result = new JSONObject();
/**
* 客户端计数
*/
public static AtomicInteger clientCount = new AtomicInteger();

private static SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

public static void startSocketIOServer() {
try {
Configuration config = new Configuration();
InetAddress localHost;
String localIp = "127.0.0.1";
try {
localHost = Inet4Address.getLocalHost();
localIp = localHost.getHostAddress();
} catch (Exception e) {
ServiceLog.error(e);
}
String ip = AppHandle.getHandle(Constants.MODULE).getProperty("ONLINE_SERVER_IP", localIp);
config.setHostname(ip);
int port = Integer.parseInt(AppHandle.getHandle(Constants.MODULE)
.getProperty("ONLINE_SERVER_PORT", "9092"));
config.setPort(port);

SocketConfig sockConfig = new SocketConfig();
sockConfig.setReuseAddress(true);//解决SOCKET服务端重启"Address already in use"异常
sockConfig.setTcpKeepAlive(false);
config.setSocketConfig(sockConfig);

socketServer = new SocketIOServer(config);
socketServer.addConnectListener(new ConnectListener() {

@Override
public void onConnect(SocketIOClient client) {
int count = clientCount.incrementAndGet();
HandshakeData handshakeData = client.getHandshakeData();
String userCode = handshakeData.getSingleUrlParam("userCode");
ServiceLog.debug(userCode + " connet, total count:" + count);
addUser(userCode);
sendOnlineMessage();
}
});
socketServer.addDisconnectListener(new DisconnectListener() {

@Override
public void onDisconnect(SocketIOClient client) {
if(clientCount.get() > 0) {
clientCount.decrementAndGet();
}
HandshakeData handshakeData = client.getHandshakeData();
String userCode = handshakeData.getSingleUrlParam("userCode");
ServiceLog.debug(userCode + " disconnet, total count:" + clientCount.get());
removeUser(userCode, client);
sendOnlineMessage();
}
});
socketServer.addEventListener("HEARTBEAT_EVENT", String.class, new DataListener<String>(){

@Override
public void onData(SocketIOClient client, String data, AckRequest ackSender) throws Exception {
JSONObject dataJson = JSON.parseObject(data);
String userCode = dataJson.getString("userCode");
addUser(userCode);
sendOnlineMessage();
}
});

socketServer.start();
}catch(Exception e) {
ServiceLog.error("启动在线统计用户服务失败: " + e.getMessage(), e);
}
}

private static void addUser(String userCode) {
if(StringUtil.isEmpty(userCode)) {
return;
}
if(!userSet.contains(userCode)) {
userSet.add(userCode);
}
}

private static void removeUser(String userCode, SocketIOClient client) {

if(StringUtil.isEmpty(userCode)) {
return;
}
userSet.clear();
Collection<SocketIOClient> clients = socketServer.getAllClients();
for (SocketIOClient c : clients) {
if(c.getSessionId().equals(client.getSessionId())) {
continue;
}
HandshakeData handshakeData = c.getHandshakeData();
String uCode = handshakeData.getSingleUrlParam("userCode");
userSet.add(uCode);
}
}

private static void sendOnlineMessage() {
Collection<SocketIOClient> clients = socketServer.getAllClients();
//result.put("CLIENT_NUM", clientCount.get());
result.put("ONLINE_NUM", userSet.size());
for (SocketIOClient c : clients) {
c.sendEvent("HEARTBEAT_EVENT", result);
}
}

public static void stopSocketIOServer(){
if(socketServer != null){
socketServer.stop();
socketServer = null;
}
}
}
只需要在ServletContextListener初始化时调用:

OnlineUserCounter.startSocketIOServer();
;销毁时调用:

OnlineUserCounter.stopSocketIOServer();
2.前端js示例代码:

var socket = io.connect('http://172.28.50.113:9093?userCode=admin');

socket.on('connect', function() {
console.log('连接成功,可以处理初始化工作');
});

socket.on('HEARTBEAT_EVENT', function(data) {
//监听HEARTBEAT_EVENT事件,只要用户数量有变化,即能及时收到推送消息
console.log("后台推送消息:"+data);
});
后记:利用socketio,不仅可以统计用户量,还可以记录所有客户端的连接列表,或者登录用户列表信息。包括用户的IP、登录名等任何希望记录的业务信息。

原文地址:https://www.cnblogs.com/java-llp/p/10892907.html