关于ASP.NET Core WebSocket实现集群的思考

前言提到WebSocket相信大家都听说过 , 它的初衷是为了解决客户端浏览器与服务端进行双向通信,是在单个TCP连接上进行全双工通讯的协议 。在没有WebSocket之前只能通过浏览器到服务端的请求应答模式比如轮训 , 来实现服务端的变更响应到客户端,现在服务端也可以主动发送数据到客户端浏览器 。WebSocket协议和Http协议平行,都属于TCP/IP四层模型中的第四层应用层 。由于WebSocket握手阶段采用HTTP协议,所以也需要进行跨域处理 。它的协议标识是wswss对应了常规标识和安全通信协议标识 。本文重点并不是介绍WebSocket协议相关,而是提供一种基于ASP.NET Core原生WebSocket的方式实现集群的实现思路 。关于这套思路其实很早之前我就构思过了,只是之前一直没有系统的整理出来 , 本篇文章就来和大家分享一下 , 由于主要是提供一种思路,所以涉及到具体细节或者业务相关的可能没有体现出来,还望大家理解 。
实现咱们的重点关键字就是两个WebSocket集群 , 实现的框架便是基于ASP.NET Core,我也基于golang实现了一套,本文涉及到的相关源码和golang版本的实现都已上传至我的github,具体仓库地址可以转到文末自行跳转到#示例源码中查看 。既然涉及到集群 , 这里咱们就用nginx作为反向代理 , 来搭建一个集群实例 。大致的示例结构如下图所示

关于ASP.NET Core WebSocket实现集群的思考

文章插图
redis在这里扮演的角色呢 , 是用来处理Server端的消息相互传递用的,主要是使用的redis的pub/sub功能来实现的,这里便涉及到几个核心问题
  • 首先,集群状态每个用户被分发到具体的哪台服务器上是不得而知的
  • 其次,处在不同Server端的不同用户间的相互通信是需要一个传递媒介
  • 最后,针对不同的场景比如单发消息、分组消息、全部通知等要有不同的处理策略
这里需要考虑的是 , 如果需要搭建实时通信服务器的话,需要注意集群的隔离性,主要是和核心业务进行隔离,毕竟WebSocket需要保持长链接、且消息的大小需要评估 。
上面提到了redis的主要功能就是用来传递消息用的,毕竟每个server服务器是无状态的 。这当然不是必须的,任何可以进行消息分发的中间件都可以,比如消息队列rabbitmq、kafka、rocketmq、mqtt等 , 甚至只要能把要处理的消息存储起来都可以比如缓存甚至是关系型数据库等等 。这压力使用redis主要是因为操作起来简单、轻量级、灵活,让大家关注点在思路上,而不是使用中案件的代码上 。
nginx配置通过上面的图我们可以看到,我们这里构建集群示例使用的nginx,如果让nginx支持WebSocket的话 , 需要额外的配置,这个在网上有很多相关的文章介绍,这里就来列一下咱们示例的nginx配置,在配置文件nginx.conf
//上游服务器地址也就是websocket服务的真实地址upstream wsbackend {server 127.0.0.1:5001;server 127.0.0.1:5678;}server {listen5000;server_namelocalhost;location ~/chat/{//upstream地址proxy_pass http://wsbackend;proxy_connect_timeout 60s;proxy_read_timeout 3600s;proxy_send_timeout 3600s;//记得转发避免踩坑proxy_set_header Host $host;proxy_http_version 1.1;//http升级成websocket协议的头标识proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "Upgrade";}}这套配置呢 , 在搜索引擎上能收到很多,不过不妨碍我把使用的粘贴出来 。这一套亲测有效,也是我使用的配置,请放心使用 。个人认为如果是线上环境采用的负载均衡策略可以选择ip_hash的方式,保证同一个ip的客户端用户可以分发到一台WebSocket实例中去,这样的话能尽量避免使用redis的用户频道做消息传递 。好了,接下来准备开始展示具体实现的代码了 。
一对一发送首先介绍的就是一对一发送的情况,也就是我把消息发给你,聊天的时候私聊的情况 。这里呢涉及到两种情况