【博客大赛】100行js代码实现网站在线用户数量统计 nodejs + socket.io方案

  • Post author:
  • Post category:其他


需求提出

公司的在线培训平台,需要增加一个新功能:实时统计当前在线的用户数量并在终端界面上显示,需要的时候可以查询当前在线的用户的明细。

有3种技术方案可以选用:

1)改动后台代码,在用户登录和退出时将用户在线信息记录到数据库中,通过查询数据库查询用户明细。这种方案稍微重了点,要改动原来后台的代码,这个功能的加入需要重新进行后台代码的更新和测试。总觉得不妥,实时性和准确性也难以保障。

2)使用消息队列(message queue),将用户登录和退出的消息实时分发给一个独立的记录器模块,由记录器进行在线用户的记录和保存。这种方案依赖消息队列消息传递的准确性和及时性。

3)采用socket.io技术,建立一个独立的微服务,进行在线用户的记录。socket.io具有轻量、易维护的特点,客户端具有自动重连机制,可以保障数据的准确性和及时性。

权衡后决定采用第3种方案。

技术方案

nodejs + socket.io

nodejs是后台运行环境,使用socket.io模块进行在线用户的记录和通信。

服务器端

代码


  1. 主程序代码 app.js
var express = require('express');           
var app     = express();
var http    = require('http');  
var server  = http.createServer(app);
var io      = require('socket.io'); 
var ios     = io.listen(server);
var port    = 86;
server.listen(port);
console.log("start server on port: " + port);
//辅助函数,根据room名称获取room对象
function ioRoom(name){
    return ios.nsps['/'].adapter.rooms[name];
}

//socket.io handling
ios.on('connection', function(socket){
    console.log("new client connected...");
    socket.on('user', function(roomName,data, callback){
        if(!data) data= {};
        socket.join(roomName);

        console.log("new user jioned " + roomName );

        //保存用户数据
        let room = ioRoom(roomName);
        if(!room.users){
            room.users = {};
        }

        socket.room = roomName;
        var ip      = socket.handshake.address;
        if(socket.handshake.headers['x-forwarded-for'] != null){
            ip = socket.handshake.headers['x-forwarded-for'];
        }
        data.socket_id = socket.id;
        data.client_ip = ip;
        data.addtime   = (new Date()).getTime() / 1000;
        room.users[socket.id] = data;

        var count = Object.keys(room.users).length;
        var backdata = {user_num:count,room:roomName};

        if(typeof callback === "function"){
            callback(backdata);
        }

        //发送用户数给所有人
        ios.to(socket.room).emit('online-number', backdata);
    });

    //根据用户请求发送用户列表给客户端
    socket.on('get-users', function(callback){
        if(!socket.room){
            console.log("user not jioned a room!");
            return;
        }
        let room = ioRoom(socket.room);
        callback(room.users);   
    });

    // disconnect user handling 
    socket.on('disconnect', function () {
        if(!socket.room){
            return;
        }
        let room = ioRoom(socket.room);
        if(room){
           var users = room.users;
           delete users[socket.id];
           socket.leave(socket.room);
           var count = Object.keys(users).length;
           if(count>0){
               var data  = {user_num:count,room:socket.room};
               ios.to(socket.room).emit('online-number', data);
           }
        }

    });
});

这个代码很好理解:

  • 客户端与服务器建立连接后,首先发送一个”user”消息给服务器,汇报用户信息。用户信息可以包括任何需要记录的信息,如:用户姓名、用户编号、所在的分组或应用(room),当前访问的页面等。
  • 服务器收到用户信息后记录到内存中,然后将在线用户总数发送给所有用户。
  • 客户端可以随时通过发送“get-users”请求消息查询在线用户列表。这个功能很酷,可以看到在线用户详细列表。

  1. 依赖定义文件 package.json


    package.json定义这个nodejs程序运行所依赖的组件,有个这个文件之后,可以通过npm命令来安装依赖包。

    代码:
{
  "name": "userCounter",
  "version": "1.0.0",
  "description": "a user counter nodejs app",
  "keywords": [
    "NodeJS",
    "OnlineUserCounter",
  ],
  "author": "www.ruiboyun.com",
  "homepage": "http://www.ruiboyun.com/",
  "private": "false",
  "bundleDependencies": [
    "passport.socketio"
  ],
  "dependencies": {
    "express": "3.2.*",
    "socket.io": "^1.3.5",
    "util": "^0.10.3"
  },
  "license": "MIT"
}

原文件拷贝到代码目录即可,后面使用。

部署与运行

  1. 首先正确安装nodejs,参见

    https://blog.51cto.com/livestreaming/2314592
  2. 安装依赖包

    在代码根目录(app.js和package.jso所在目录)运行命令:
npm install
  1. 修改服务端口

    在上述app.js代码中,修改web服务端口,默认是86,确保各级防火墙开放该端口:

    var port    = 86;
  2. 运行程序
node app.js

客户端

客户端代码就是网页代码啦,可以根据需要提交用户信息、显示用户数量和查询用户列表。

示例代码:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv='Content-Type' content='text/html; charset=utf-8' />
        <link href="bootstrap.min.css" rel="stylesheet" type="text/css" />
        <title></title>
    </head>
    <body>
        <a href="">
            <span class="glyphicon glyphicon-user" aria-hidden="true"></span> 在线用户 
            <span class="badge badge-danger" style="background-color: red;" id="user-count">12</span>
        </a>
    </body>
</html>
<script src="socket.io.js"></script>

<script type="text/javascript">
    var io_host   = "www.myhost.com";    //输入正确的域名或IP,就是前面启动服务的那台机器的域名或IP 
    var io_server = "http://" + io_host + ":86";   //注意端口
    var socket    = io(io_server);
    var room      = location.host; 

/**
*显示用户数量的代码
*/
    function readerUserNumber(data){
        console.log(data);
        var divuser = document.getElementById("user-count");
        if(divuser) {
            divuser.innerHTML = data.user_num; 
        }
    }

    socket.on('connect', function(){
        console.log("connected to server...");

        //提交用户信息
        var info = {};
        info.url = location.href;
        info.username = "myname";
        socket.emit("user",room,info,function(msg){
            readerUserNumber(msg);
        });

   //查询用户明细
        socket.emit("get-users",function(data){
            console.log("render by get-users...");
            console.log(data);
        });
    });

    socket.on('disconnect', function(){

    });

    socket.on('online-number', function(data){
    //用户数量更新
        console.log("online number:");
        readerUserNumber(data);
    });

</script>

效果提升

基于这个应用,还可以实现如下效果:

1)将在线用户数采样记录入库,用于网站热度分析和用户访问趋势分析

2)将用户访问历史记录如库,跟踪用户访问轨迹

3)提交用户信息时把昵称、头像等信息提交进来,显示用户列表时可以更酷

我的一个在线用户效果:

【博客大赛】100行js代码实现网站在线用户数量统计 nodejs + socket.io方案



版权声明:本文为SE_JW原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。