分布式部署可能遇到的问题
常见 nginx 反向代理方案

- 假设按照上述架构方案来
- A用户接入后connA(ws客户端) 由节点1来维护
- B用户接入后connA(ws客户端) 由节点2来维护
- 流程: A->B 发信息: A -> connA -> 分析处理 -> connB -> B
- 实际上,上述流程是没有办法通信的,因为 A找不到B在哪里
- 核心问题:系统如何将消息投递到 connB?
常用解决方案
1 ) 使用消息总线

- 优点:简单
- 去电:各个节点不知道彼此节点状态
- 例子:redis/kafka/MQ
2 ) 局域网通信协议

- 节点间通过通信协议来通信
- 优点:简单,成本低
- 缺点:不知道节点状态
- 例子:UDP
3 ) 实现调度应用

- 自己实现调度应用,保存各个客户端的状态
- 优点:可靠
- 缺点:复杂
基于局域网通信UDP协议解决
- 以上三种方案,我们选择第二种来解决
- 首先,回顾单体应用
- 开启ws接收协程recvproc/ws发送协程sendproc
- websocket收到消息->dispatch发送给dstid
- 基于UDP的分布式应用
- 开启ws接收协程recvproc/ws发送协程sendproc
- 开启udp接收协程udprecvproc/udp发送协程udpsendproc
- websocket收到消息->broadMsg广播到局域网
- udp接收到收到消息->dispatch发送给dstid
- 自己是局域网一份子,所以也能接收到消息
代码实现解决,ctrl/chat.go
package ctrl
import (
"net/http"
"github.com/gorilla/websocket"
"gopkg.in/fatih/set.v0"
"sync"
"strconv"
"log"
"encoding/json"
"net"
)
const (
CMD_SINGLE_MSG = 10
CMD_ROOM_MSG = 11
CMD_HEART = 0
)
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"` //其他和数字相关的
}
/**
消息发送结构体
1、MEDIA_TYPE_TEXT
{id:1,userid:2,dstid:3,cmd:10,media:1,content:"hello"}
2、MEDIA_TYPE_News
{id:1,userid:2,dstid:3,cmd:10,media:2,content:"标题",pic:"http://www.baidu.com/a/log,jpg",url:"http://www.a,com/dsturl","memo":"这是描述"}
3、MEDIA_TYPE_VOICE,amount单位秒
{id:1,userid:2,dstid:3,cmd:10,media:3,url:"http://www.a,com/dsturl.mp3",anount:40}
4、MEDIA_TYPE_IMG
{id:1,userid:2,dstid:3,cmd:10,media:4,url:"http://www.baidu.com/a/log,jpg"}
5、MEDIA_TYPE_REDPACKAGR //红包amount 单位分
{id:1,userid:2,dstid:3,cmd:10,media:5,url:"http://www.baidu.com/a/b/c/redpackageaddress?id=100000","amount":300,"memo":"恭喜发财"}
6、MEDIA_TYPE_EMOJ 6
{id:1,userid:2,dstid:3,cmd:10,media:6,"content":"cry"}
7、MEDIA_TYPE_Link 6
{id:1,userid:2,dstid:3,cmd:10,media:7,"url":"http://www.a,com/dsturl.html"}
7、MEDIA_TYPE_Link 6
{id:1,userid:2,dstid:3,cmd:10,media:7,"url":"http://www.a,com/dsturl.html"}
8、MEDIA_TYPE_VIDEO 8
{id:1,userid:2,dstid:3,cmd:10,media:8,pic:"http://www.baidu.com/a/log,jpg",url:"http://www.a,com/a.mp4"}
9、MEDIA_TYPE_CONTACT 9
{id:1,userid:2,dstid:3,cmd:10,media:9,"content":"10086","pic":"http://www.baidu.com/a/avatar,jpg","memo":"胡大力"}
*/
//本核心在于形成userid和Node的映射关系
type Node struct {
Conn *websocket.Conn
//并行转串行,
DataQueue chan []byte
GroupSets set.Interface
}
//映射关系表
var clientMap map[int64]*Node = make(map[int64]*Node,0)
//读写锁
var rwlocker sync.</