需求提出

公司的在线培训平台,需要增加一个新功能:实时统计当前在线的用户数量并在终端界面上显示,需要的时候可以查询当前在线的用户的明细。
有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方案相关推荐

  1. 力争群雄:2012年度IT博客大赛100强脱颖而出

    2012年度IT博客大赛于11月20日圆满结束.这一所谓的"海选"阶段为期33天,引无数网友和博主翘首以待,来源包括51CTO.独立个人博客.其他博客服务托管商,以及今年评选新增加 ...

  2. 原生JS:100行js代码带你实现【像素鸟】小游戏(完整代码+素材图片)

    系列文章目录 JS:经典小游戏:像素鸟 JS:经典小游戏:贪吃蛇 JS:经典小游戏:扫雷 目录 系列文章目录 像素鸟 1.游戏介绍 2.代码分析 3.代码实现 3.1 随机生成水管 3.2 当水管超过 ...

  3. 100行JS代码实现❤坦克大战js小游戏源码 HTML5坦克大战游戏代码(HTML+CSS+JavaScript )

    坦克大战js小游戏源码 HTML5坦克大战游戏代码(HTML+CSS+JavaScript ) HTML5坦克大战网页小游戏,完美还原小霸王学习机效果,以坦克战斗及保卫基地为主题,属于策略型类游戏. ...

  4. 2021FME博客大赛 —— 面向海量地貌数据的FME在线质检研究

    作者:王慧 摘要:在浙江省新型基础测绘资源库建设中,地貌数据存在高曲矛盾.水曲矛盾.坎曲矛盾.等高线跳跃等典型质量问题,针对该类质量问题存在隐蔽.质检自动化程度低.质检模式受限于数据量等特点,研究提出 ...

  5. 几行JS代码防止网站在QQ和微信被举报

    1 <div> 2 <script>3 // 跳转提示4 5 if (is_weixn_qq()) {;6 7 window.location.href = 'https:// ...

  6. 博客园 页面定制CSS代码

    更新时间:2019-02-18 花了一点时间修改自己的博客背景设置,现在分享一下代码.希望对大家有帮助.我的比较简单,主要代码模板来源于另外一个博主,然后我在基础上进行了部分修改.原博主的连接:htt ...

  7. “2012年度IT博客大赛”获奖感言--梦想、学习、坚持、自信、淡定

    今年有幸参加"2012年度IT博客大赛",并且进入了前十强,大赛组委会让前十强选手写一下获奖感言.自开博以来,已经有一年半的时间,刚好籍此机会回顾一下写博历程.首先要感谢<老 ...

  8. 基于 abp vNext 和 .NET Core 开发博客项目 - 数据访问和代码优先

    基于 abp vNext 和 .NET Core 开发博客项目 - 数据访问和代码优先 转载于:https://github.com/Meowv/Blog 本篇主要使用Entity Framework ...

  9. 登顶 GitHub 趋势榜,标星1.8k:200 行 JS 代码让画面人物瞬间消失!

    整理 | 夕颜 出品 | CSDN(ID:CSDNnews) 今天,一个名为 Real-Time-Person-Removal(实时人物去除)项目在GitHub上火了,登上近日GitHub Trend ...

最新文章

  1. CUDA 7 流并发性优化
  2. 测试适合眉形的软件_软件测试的自我修养:正向思维与逆向思维
  3. Go 语言编程 — 错误处理
  4. 群友:事务中的异常不也抛出了,为什么没catch到而回滚?
  5. android 自定义apk名,Android Studio多渠道打包、自定义打包APK名称
  6. 【机器视觉】 while算子
  7. 开发Eureka Server
  8. java用easyexcel实现读取excell表格内容
  9. 麦克风失灵_iPhone7Plus手机麦克风失灵怎么办?请看解决方案
  10. 回望2017:一个前端从业者砥砺前行的一年
  11. 劲爆ORACLE优化,你不必是专家
  12. Brex联合创始人:可能将加密货币纳入资产负债表
  13. linux tab 缩进,codemirror TAB 缩进问题记录
  14. html编写个人博客_云开发平台开箱,3分钟零基础搭建个人Hexo博客
  15. java excel换行_Java 导出excel进行换行的案例
  16. 通信原理-确定信号分析
  17. 面试官灵魂三问:什么是SOA?什么是微服务?SOA和微服务有什么区别?
  18. 兔子繁殖为例 c语言,用斐波那契数列解答兔子的繁殖
  19. JVM--Jit学习
  20. GAL 高压缩版辅助工具

热门文章

  1. 用户注册与登陆(验证和数据库)
  2. 数组常见异常 学习笔记
  3. AIX5.3安装oracle10g
  4. yum 快速安装 LAMP
  5. PyTorch载入图片后ToTensor解读(含PIL和OpenCV读取图片对比)
  6. npm -S -D -g i 有什么区别
  7. VueJS样式绑定:v-bind
  8. Portal for ArcGIS 资源承载数据类型
  9. AWR Report and session_cached_cursor
  10. 合成谬误与公地悲剧(为何设置产品总监职位及核算名义成本)