SignalR+Redis做横向扩展(集群)

时间:2024-05-21 13:09:02

问题背景:当SignalR应用部署在一台服务器的时候,随着用户量的增加,服务器的承载能力肯定会达到瓶颈,这时候就需要考虑多部署几台服务器来缓解压力

解决办法:多台服务器部署SignalR应用,一台服务器部署Redis服务

SignalR+Redis做横向扩展(集群)

如上图,一台SignalR服务器对应一个用户群,SignalR应用服务器和Redis服务器之间采用发布-订阅模式。当用户群1的用户向用户群2的用户发送消息的时候,消息先到达SignalR应用服务器1,SignalR1服务器向Redis服务器发布消息,其他服务器订阅了Redis服务器的消息,会接受到SignalR服务器1的消息,SignalR服务器2发现是自己用户消息,因此接受消息并将消息发送给用户群2里面的用户

实现:

在vs2017新建一个web应用,在nuget上安装Microsoft.AspNetCore.SignalR.Redis 和Microsoft.AspNetCore.SignalR.StackExchangeRedis两个应用组件

在Start.cs代码文件里面新增SignalR服务并配置Redis服务

services.AddSignalR()
                .AddRedis("127.0.0.1:6379", opitons =>
            {
                opitons.Configuration.ChannelPrefix = "myapp2";  //同一个SignalR应用配置相同的Redis频道前缀(应为要订阅相同频道的Redis消息)
            })
            ;

编写Hub

 public class ChatHubs : Hub
    {
        public override Task OnConnectedAsync()
        {
            Clients.Client(this.Context.ConnectionId).SendAsync("ReceiveConnectionID", this.Context.ConnectionId);
            return base.OnConnectedAsync();
        }

        public override Task OnDisconnectedAsync(Exception exception)
        {
            return base.OnDisconnectedAsync(exception);
        }

        public async Task SendMessage(string connectionID,string user, string message)  //给某一个人发送消息
        {
            //await Clients.All.SendAsync("ReceiveMessage", user, message);            
            await Clients.Client(connectionID).SendAsync("ReceiveMessage", user, message);
        }
        
    }

发布应用到两个文件夹,使用dotnet命令分别运行两个应用将网站跑起来,两个网站分别绑定到不同的端口,比如我这里分别绑定到5001和5002两个端口

SignalR+Redis做横向扩展(集群)

SignalR+Redis做横向扩展(集群)

在5001这个端口下面的应用的ConnectionID填写5002端口下面的那个ConnectionID值,这样就可以实现5001端口应用向5002端口应用发送消息

这样只要知道需要接受消息的那个人的ConnectionID,就可以向那个人发送消息,即便这收发消息的两个人不是连在同一个服务器上也可以进行通信

这里只是讲讲自己对SignrR+Redis方面的扩展的理解,真正的扩展还需要考虑用户和SignalR服务器的之间转发问题,因为用户和SignalR服务器之间是通过ConnectionID关联的,所以当有多个服务器的时候,就需要考虑怎么保持ConnectionID和服务器的对应关系,这里可以使用Nginx实现用户分发问题,利用Nginx的ip绑定策略实现ip固定访问服务器的功能。

这里附上通过ip绑定策略配置Nginx负载配置

修改Nginx配置文件nginx.conf

upstream myweb{ 
        ip_hash;
        server localhost:5001; 
        server localhost:5002; 
        keepalive 1000; 
    }

server {
        listen       81;
        server_name  localhost;
        location / {
            #root   html;
            index  index.html index.htm;
            proxy_pass http://myweb;
            #Nginx配置websocket
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";    
        }
    }

这样在浏览器访问 http://localhost:81地址,同一个ip用户请求将只会被转发到一台固定的服务器