Awesome Open Source
Awesome Open Source

概述

轻量级推送服务和实时在线监控平台,同时用于开发即时通信系统,基于node的socket.io,支持web、android、ios客户端,支持移动端离线推送,可进行分布式部署

前言

随着互联网网速的不断提升,即时消息通信的应用场景越来越多。我在参与公司多个产品的研发中,不止一次的遇到需要集成聊天功能的需求。既然是聊天就必须把消息尽快送达目标用户,做的多了就有了一套自己的经验和想法,然后借着闲暇时间一点点的就写出了这个项目。这个项目灵感来源于实际开发工作,同时又把它应用到自己的工作中。希望它能帮到你,也希望你能给我更多的反馈和改进意见,让它帮助更多的人。

img

系统概要和功能特性

  • 业务系统通过RESTful接口方式调用推送服务
  • 客户端通过socket.io协议与推送服务建立连接
  • 通过命名空间对客户端进行安全隔离和管理,不同客户端之间不能相互收发消息,如果业务系统是saas模式,命名空间相当于公司或组织的概念
  • 使用负载均衡器来负责每一个客户端的接入工作,每一个客户端随机分配给一个推送服务的后端节点,保证推送服务的负载更加平均(负载均衡器指类似nginx的服务)
  • 单个客户端和推送服务建立强关联,即一旦客户端与推送服务的某个节点建立连接,除非客户端下线否则所有数据处理和操作都有该节点完成,同时保证会话信息不丢失,可实现多机多进程部署
  • 使用redis保存系统运行时所需的数据,保证系统响应速度
  • 可实现单个用户多端连接,满足聊天系统多端登陆需求,同时接收消息,每一个客户端至少有一个默认房间,房间名: user_ + userid
  • 通过界面UI或者接口来监控,统计客户端在线信息
  • 通过界面UI模拟客户端上线/下线
  • 通过界面UI模拟推送功能
  • 模拟网络异常下推送服务已经推送消息但是客户端无法接收的情况,方便客户端编写数据同步功能
  • 通过界面UI或者接口统计消息到达率(需要客户端ack确认回执)
  • 可查询历史消息,可统计每天的推送总量,以及当前小时/当前分钟的推送总量
  • 当客户端未收到推送消息时,如果客户端为ios平台则用apns做离线推送,如果是android会保存该消息直到客户端上线
  • 监听每一个客户端接入事件并可回调业务系统,由业务系统决定客户端是否有权接入
  • 监听每一个用户的离线事件并可回调业务系统
  • 客户端房间变动事件可选择性的广播推送到对应的房间中
  • 客户端可主动推送消息,满足聊天系统中正在输入中的功能需求(正常情况的推送应有业务系统发起,保障推送服务的安全和稳定)
  • 可为每一个客户端保存一些特殊数据,比如最后一次的接入信息,客户端操作系统信息等等,同时提供一键清除僵尸客户端功能(一定时间未接入过推送服务器)
  • 推送服务本身支持cors跨域访问,方便基于推送服务来开发管理界面
  • 采用消息队列方式推送离线消息,保证系统在高并发下的稳定性
  • 服务运行时的各个参数可自行配置,比如消息默认失效时间等

快速体验

  • sudo docker run -id -p 443:443 -p 80:80 --name light-push-demo liuss/light-push:1.2.0 /mnt/data/start.sh
  • 访问管理页面: https://127.0.0.1/push-admin 登录名 admin 密码 123456

客户端调用(web)

// demo为命名空间;uuid为客户端唯一标示;userid为客户端所属的用户ID
let socket = io.connect('https://127.0.0.1:55555/demo?uuid=' + uuid + '&userid=' + userid, {
  path: '/push/socket.io/'
});

socket.on('connect', function () {
  // 客户端主动加入房间
  socket.emit('joinRoom', ['room1'], function (result) {
    console.log('joinRoom:' + JSON.stringify(result));
  });

  // 接收服务器端的推送消息
  socket.on('push', function (data) {
    console.log('push:' + JSON.stringify(data));
    // 消息确认回执
    socket.emit('ackPush', { id: data.id });
  });

  // 客户端主动离开房间
  socket.emit('leaveRoom', ['room2'], function (result) {
    console.log('leaveRoom:' + JSON.stringify(result));
  });
});

环境搭建

  • 安装 nodejs (需要超级管理员权限) 详情
curl --silent --location https://rpm.nodesource.com/setup_10.x | sudo bash -
sudo yum install -y nodejs
  • 安装pm2,如果安装中报错或者长时间没有响应 尝试通过第三方镜像安装 例如: npm install -g pm2 --registry=https://registry.npm.taobao.org
sudo npm install -g pm2

src/config.yaml 中的 redis_address 用来配置redis服务器地址 , 如果redis是集群模式,则将该配置改为数组类型

  • 安装nginx集群部署时需要,yum源
yum install nginx
  • 系统初始化之后,需要调用 /api/admin/namespace/save 接口来生成一个命名空间,客户端通过这个命名空间连接服务器;每个客户的必须有一个所属的命名空间才能连接服务器,否则服务器会拒绝客户端的所有请求

  • 集群部署nginx方案,请参考 doc/nginx.confredis配置,请参考 doc/redis.confnode单机集群pm2方案配置,请参考app.jsonpm2使用说明

  • 通过node命令启动服务,则端口配置见 src/config.yamlconnector_port : 连接服务器; logic_port : 接口服务器;如果通过pm2方式启动,则app.jsonargs-p 参数,则会覆盖 src/config.yaml 中端口配置

性能调优

详情见 doc/performance.md

客户端模拟测试

  • 执行测试:nohup node --max-old-space-size=3000 simulator.js >> test1.log 2>&1 &
  • 根据具体情况调整 test/simulator.yaml 测试配置,主要调整的参数:server,push_option_path,client_namespace,client_total,client_uuid_init
  • 配置参数 client_uuid_init 需要在每次启动测试实例时手动修改,一般为现有在线客户端数 + client_total
  • 开始测试之前需要手动修改 /etc/sysctl.conf ,增加如下配置
    fs.file-max=100000
    fs.nr_open=100000
    net.ipv4.ip_local_port_range=1024 65000
    
  • 在推出测试终端时必须通过 exit 命令退出,否则后台测试进程会被系统kill

并发测试

  • 安装测试工具 yum install httpd-tools -y
  • 执行测试见 test/ab.txt
  • 根据情况修改测试请求内容 test/ab_post

其他

接口说明和注意事项见wiki

推送服务控制台项目 地址

配套ios 演示项目 SDK

配套android SDK

QQ技术交流群 643889498


Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
Javascript (1,539,232
Docker (33,269
Node (12,914
Redis (6,655
Websocket (5,249
Nginx (5,138
Material Design (3,470
Socket Io (3,317
Yaml (2,000
Material (1,571
Koa2 (929
Push (371
Pm2 (332
Im (275
Apns (195
Related Projects