用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 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(){

				@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 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 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、登录名等任何希望记录的业务信息。

你可能感兴趣的:(网络编程)