# message-pusher **Repository Path**: TenzT/message-pusher ## Basic Information - **Project Name**: message-pusher - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-09-19 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 消息推送系统 ## 需求分析与技术选型 ### 背景 - 浏览器的直播间的弹幕系统 ### 业务模型及技术挑战 - 假设一个直播间里在线人数达到100万 - 每秒发送弹幕1000条 - 单个直播间推送频率:100万 * 1000条/秒 = 10亿 ### 模式选择 - 拉模式:客户端定时轮询服务端接口 - 如果数据更新频率低,则大多数请求是无效的 - 如果在线用户数量多,则服务端的查询负载很高(相当于一个DDoS攻击了) - 拉模式是定时的,时效性不高 - *推模式:仅当数据有效时才推送 - 需要维护维护大量的在线长连接 - 数据更新后高时效 ### 协议选择 - WebSocket - 浏览器支持的socket变成,轻松维持服务端的长连接 - 基于TCP的一个你哟功能层协议 - 有丰富的类库,技术成本低 ## WebSocket协议与交互 ### 通信流程 - upgrade(client-->server): 客户端和服务端利用HTTP实现握手操作,在header里面带了一个`Upgrade:websocket`字段,表示向websocket协议转换 - switching(server-->client): 握手确认后,这时TCP连接没有断开,只是上层协议换成了websocket - message(client<-->server): websocket消息 ### 传输原理 - 协议升级后,复用原来的socket - 上层传递message即可 ## Go服务器开发 - close一个通道:从已经关闭的通道数据或者正在接收数据时,将会接收到通道值内置的零值,停止阻塞并返回。 - 为什么需要一个isClosed的标志位?服务器拉起的各个协程通过一个关闭通道来获知服务器是否关闭,但是一个通道只能被关闭一次(不可重入),因此需要设置标志位保证通道只被关闭一次。 ## 千万级弹幕系统 ### 性能瓶颈 - 内核瓶颈 - 推送量大:linux内核发送TCP的极限包频~=100万/秒 - 锁瓶颈 - 需要维护在线用户集合,通常好似一个字典结构 - 推送消息即遍历整个集合,顺序发送消息,耗时长 - 推送期间,在线用户集合是会变的 - CPU瓶颈 - json编解码非常耗费CPU资源 ### 解决方案 - 内核优化 - 减少网络小包的发送,把同一秒内的N条消息合并成一条。合并后推送次数值等于在线连接数 - 锁瓶颈 - 大变小:维护多个在线用户集,每个集合有自己的锁,将连接打散到不同的集合中,每个集合有自己的锁 - 多线程并发推送多个集合,避免锁竞争 - 读写锁取代互斥锁 - CPU瓶颈 - json编码前置,1次消息编码+100万次推送 # 揭秘分布式架构 - 单机瓶颈 - 维护海量长链接话费内存 - 消息推送瞬间消耗大量CPU资源 - 网络带宽是主要瓶颈 - 网管集群 - 将连接打散到不同机器上,这种一般堆Nginx做LB