千万级WebSocket消息推送
===
### 技术复杂度分析
* 在线人数: 100万
* 发送消息: 1000条/秒
* 推送频率: 100万 \* 1000条/秒 = 10亿条/秒
* N个业务推送频率: N \* 10亿条/秒
### [](https://github.com/dollarkillerx/Tens-of-thousands-of-messages-pushed#%E6%8E%A8%E6%A8%A1%E5%BC%8F%E4%B8%8E%E6%8B%89%E6%A8%A1%E5%BC%8F)推模式与拉模式
* 拉模式
* 数据更新频率低,大多数请求是无效的
* 在线用户数多,服务端查询负载高
* 定时轮询拉取,无法满足时效性要求
* 推模式
* 仅在数据更新时才需要推送
* 需要维护大量的在线长连接
* 数据更新立即推送,高时效性
* 基于WebSocket协议
* 浏览器支持socket编程,轻松维持服务端长连接
* 基于TCP可靠传输之上的协议,无需关系通讯细节
* 提供高度抽象编程接口,业务才发成本较低
### [](https://github.com/dollarkillerx/Tens-of-thousands-of-messages-pushed#%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8A%80%E6%9C%AF%E9%80%89%E9%A1%B9)服务端技术选项
* NodeJS
* 单线程模型,推送性能有效
* C/C++
* TCP通讯,WebSocket协议实现成本高
* Go
* 多线程,基于协程模型并发
* 成熟的WebSocket标准库,不需造轮子
### [](https://github.com/dollarkillerx/Tens-of-thousands-of-messages-pushed#%E6%8A%80%E6%9C%AF%E9%9A%BE%E7%82%B9)技术难点
* 3个性能瓶颈
* 内核瓶颈
* 推送量大: 100万在线\*10条/秒 = 1000万条/秒
* linux内核发送TCP的极限包频率 约为 100万/秒
* 锁瓶颈
* 需要维护在线用户集合(100万在线),通常是一个字典结构
* 推送消息即遍历整个集合,顺序发送消息,耗时极长
* 推送期间,客户端仍旧正常上/下线,所以集合需要上锁
* CPU瓶颈
* 浏览器与客户端通常采取json格式通讯
* json编码非常耗费CPU资源
* 向100万在线推送1次,则需100万次json encode
### [](https://github.com/dollarkillerx/Tens-of-thousands-of-messages-pushed#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88)解决方案
* 内核瓶颈
* 优化原理
* 减少网络小包的发送
* 优化方案
* 将同一s内的n条消息,合并成1条消息
* 合并后,每秒推送次数只等于在线连接数
* 锁瓶颈
* 优化原理
* 大拆小
* 优化方案
* 连接打散到多个集合中,每一个集合有自己的锁
* 多协程并发推送多个集合,避免锁的竞争
* 读写锁取代互斥锁,多个推送可以并发遍历相同集合
* CPU瓶颈
* 优化原理
* 减少重复计算
* 优化方案
* json编码前置,1次消息编码 + 100 万次推送
* 消息合并前置,n条消息合并后只编码一次
* 单机架构
![](https://box.kancloud.cn/9b94db454dfd032b2f5a8b6b84b583d3_529x433.png)
* 分布式架构
* 单机瓶颈
* 维护海量长连接会话费不少内存
* 消息推送瞬间消耗大量CPU资源
* 消息推送瞬时带宽高达400~600MB(4-6Gbits),主要瓶颈
* 网管集群
* 每一个消息进来 分配给不同的网管
* 这时就会出现问题,返回消息怎么知道是那个网管的呢?(一般广播解决)
* 逻辑集群
* 基于HTTP/2协议想gateway集群分发消息
* HTTP/2支持链接复用,用于PRC性能更佳
* 基于HTTP/1协议对外提供推送API
* HTTP/1更加普及,对业务方更加友好
OK
![](https://box.kancloud.cn/afabefff539b4d6d97d8182ac8f2b422_917x301.png)
[https://github.com/dollarkillerx/Tens-of-thousands-of-messages-pushed](https://github.com/dollarkillerx/Tens-of-thousands-of-messages-pushed)
- 初认GOlang Web
- 关于环境配置
- 路由
- 路由进阶与目录架构
- 静态文件服务器
- 自定义Middleware
- 与MySQL起舞
- 主从模式概念
- 部署主从集群
- 分库分表
- 补充:事务
- 补充:常用SQL示例
- Template使用
- 一些小的,但是要知道的东西
- 调度任务
- 流控算法
- 鉴权
- JWT鉴权前置知识:加密解密
- session
- 文件上传与下载
- 带缓存读写拷贝io
- 参考
- 写好的文件上传
- 文件下载
- 拓展:秒传功能实现
- 扩展:分块上传和断点续传
- 扩展:分块上传
- 扩展:断点续传
- 扩展:分布式存储
- 部署ceph集群
- cephAuth
- go操作ceph集群
- 扩展:云存储
- go操作oss
- 补充:xorm框架
- 命令小结
- 补充:xorm框架高级部分
- 补充
- MongoDB
- 基础概念
- 简简单单NoSql
- 操作集合(Collection)
- 操作文档(Document)
- 带条件的文档 db.find
- 复杂条件抽文档 db.find
- redis
- redis操作
- go操作redis
- (新增)配置鉴权
- 密码学
- 文件校验算法
- 未来课程的安排
- RPC实践
- 爬虫
- 正则表达式
- 爬取手机号
- 爬取邮箱
- 爬取超链接
- 爬取身份证号
- 并发爬图片
- 扩展:CICD
- GO实现自动化部署系统
- 国际化支持
- 并发带来问题的解决
- GOWEB小知识
- Sync包讲解
- sync.Pool
- 千万级WebSocket消息推送
- 微服务入门:开篇
- 路由通讯
- RabbitMQ
- RabbitMQ工作原理和转发模式
- Dcoker 下 RabbitMQ and Ui
- Go操作RabbitMQ
- 初步微服务
- go-micro
- 补充:consul
- 快速入门
- 补充:consul超时
- 微服务架构
- 微服务架构全景图
- 服务注册和发现
- raft协议基本概念
- raft协议leader选举详解
- raft协议日志复制详解
- raft协议safefy详解
- rpc调用个服务监控
- etcd
- 命令行使用
- Golang操作etcd
- GO操作etcd OP方式 (分布式锁基础)
- etcd 分布式集群乐观锁
- (新增)鉴权
- 服务注册
- 服务发现原理
- 选项设计模式介绍
- 基于插件的注册组建
- 课前知识
- etcd注册开发1
- ffmpeg
- 2.0新的启航
- 高可用Mysql
- mysql逻辑架构
- 常见的MySQL高可用方案
- 索引
- MYSQL调优
- 什么影响了MYSQL的性能
- Mysql 服务器参数配置
- Go深入并发
- 基本同步原语
- 扩展同步原语
- 原子操作
- M P G 模型
- 简单的消息总线
- GoMicro入门
- GO任务池编写
- GO依赖注入
- 一些补充
- golang defer在什么时候执行
- 分布式理论篇(面试吹牛必备)
- CAP理论
- Raft协议
- 保证注册中心的可靠性
- 链路追踪
- 怎么实现强一致性