用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、登錄名等任何希望記錄的業務信息。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM