websocket
最后更新于:2022-04-02 02:50:51
[TOC]
## demo
参考 [个人github](https://github.com/idcpj/go_websocket)
参考 [个人 im 带群聊,发图片,语音](https://github.com/idcpj/go_IM)
## 推送的高并发解决方案
### 内核优化
把同一秒内要推送的弹幕,合并成一条,如每条发送10条的数,合并为10条同时发送
## 锁瓶颈-优化方案
1. 连接打散到多个集合中,每个集合有自己的锁
2. 多线程并发推送多个集合,避免锁竞争
3. 读写锁取代互斥锁,多个推送任务可以并发遍历相同集合
## 单机瓶颈
1. 维护海量长连接会花费不少内存
2. 消息推送瞬时消耗大量CPU资源
3. 消息推送瞬时带宽高达400~600MB(4-6Gbiⅰts),是主要瓶颈!
## 资源标准化
把图片,视频,音频资源先上传到服务器,在传递 url
## 群聊解决方案
1. 使用缩略图
2. 使用云服务,如阿里云
## 心跳机制
1. 每 30s 发送一次
2. 距离最近发送的消息,后的 30s [推荐]
## 降低 cpu 资源的使用
1. 降低 json 编码频次
2. 一次编码多次使用
## 降低对 io 资源的使用
1. 合并写数据库次数
2. 优化对数据库读操作
3. 能缓存的就缓存
## 群聊的 struct 表
### 方案一 [推荐]
优势
- 锁的频次低,只需要在加入时,clientMap 加锁
劣势
- 要轮训全部map
```
type Node struct {
Conn websocket.Conn //找到用户就可以发送 conn 所以不需要锁
//并行转串行,
DataQueue chan []byte
GroupSets set.Interface //设置人员所在群聊的 id 列表 使用(gopkg.in/fatih/set.v0)
}
//映射关系表, userid-node 映射表
var clientMap map[int64]Node = make(map[int64]*Node,0)
```
### 方案二
优势
- 找用户ID非常快
劣势
- 登录用户的时,需要给 clientMap 与 comMap 都加锁
- 发送信息时需要根据userid获取node,锁的频次太高
```
type Node struct {
Conn *websocket.Conn
//并行转串行,
DataQueue chan []byte
}
//映射关系表
var clientMap map[int64]*Node = make(map[int64]*Node,0)
//群中的人员userid
var comMap map[int64]set.Interface= make(map[int64]set.Interface,0)
```
## 聊天的 struct
```
type Message struct {
Id int64 `json:"id,omitempty" form:"id"` //消息ID
//谁发的
Userid int64 `json:"userid,omitempty" form:"userid"` //谁发的
//什么业务
Cmd int `json:"cmd,omitempty" form:"cmd"` //群聊还是私聊
//发给谁
Dstid int64 `json:"dstid,omitempty" form:"dstid"` //对端用户ID/群ID
//怎么展示
Media int `json:"media,omitempty" form:"media"` //消息按照什么样式展示
//内容是什么
Content string `json:"content,omitempty" form:"content"` //消息的内容
//图片是什么
Pic string `json:"pic,omitempty" form:"pic"` //预览图片
//连接是什么
Url string `json:"url,omitempty" form:"url"` //服务的URL
//简单描述
Memo string `json:"memo,omitempty" form:"memo"` //简单描述
//其他的附加数据,语音长度/红包金额
Amount int `json:"amount,omitempty" form:"amount"` //其他和数字相关的
}
const (
//点对点单聊,dstid是用户ID
CMD_SINGLE_MSG = 10
//群聊消息,dstid是群id
CMD_ROOM_MSG = 11
//心跳消息,不处理
CMD_HEART = 0
)
const (
//文本样式
MEDIA_TYPE_TEXT = 1
//新闻样式,类比图文消息
MEDIA_TYPE_News = 2
//语音样式
MEDIA_TYPE_VOICE = 3
//图片样式
MEDIA_TYPE_IMG = 4
//红包样式
MEDIA_TYPE_REDPACKAGR = 5
//emoj表情样式
MEDIA_TYPE_EMOJ = 6
//超链接样式
MEDIA_TYPE_LINK = 7
//视频样式
MEDIA_TYPE_VIDEO = 8
//名片样式
MEDIA_TYPE_CONCAT = 9
//其他自己定义,前端做相应解析即可
MEDIA_TYPE_UDEF = 100
)
type Node struct {
Conn *websocket.Conn
DataQueue chan []byte
GroupSets set.Interface //把群放入其中
}
var clientMap = make(map[int64]*Node, 0)
var rw sync.RWMutex
```
## 分布式
使用 udp + nginx ,参考 [个人 im 带群聊,发图片,语音](https://github.com/idcpj/go_IM)
';