四时宝库

程序员的知识宝库

搭建SignalR服务(signal服务器搭建)

在项目里面引入了websocket,想着建立长连接后,前后端便能做到双工通信,这样执行时间比较长的操作,就不用担心超时了,还可以加上比较顺滑的滚动条效果。

当实现了传统的websocket方式后,想着继续试试之前用到的signalR,这样开始了部署signalR服务的过程,部署了signalR方式,紧接着开始了即时通信的改版优化,一发不可收拾,截止目前为止,即时通信算告一段落,基础功能也就有了,支持私聊、群组聊、自定义群组,发送图片、视频、文字等信息。

这一过程完全搭载在自己的NAS上面,采用自带的k3s部署,由提交代码,jekins构建、自动生成docker、上传到阿里云仓库,最后部署k3s,整整一套全流程,有时间后续慢慢介绍,在自建的工作台上面实现即时通信,只是试试工作台的产出效率如何,就当一个实验品,还有快速搭建工具类的网页,支持手机端采集照片,快速搭建门户网站等等,现在先说一下部署的signalR服务吧。

场景

在前端发起建立websocket请求,并与signalR服务连接,采用网关的方式去建立连接。其他的方法通过管理平台的边界服务进行管理

需要的服务

前端、网关、SignalR、管理平台(已经搭建好的,需要权限认证)

搭建网关

搭建网关(需要部署一个网关服务),使用app.UseWebSockets();监听websocket,修改配置文件,如下

{
    "DownstreamPathTemplate": "/{catchAll}",
    "DownstreamScheme": "ws",
    "DownstreamHostAndPorts": [
        {
            "Host": "ansheng-signalr-nodeport.ansheng",
            "Port": "80"
        }
    ],
    "UpstreamPathTemplate": "/gateway/{catchAll}",
    "UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE", "OPTIONS" ]

 }

搭建signalR服务

  1. 配置服务(ConfigureServices)里面添加SignalR服务
services.AddSignalR();
  1. 为了获取UserId,使用UserId进行发送、接收消息,添加IUserIdProvider实现类UserIdProvider
 public class UserIdProvider : IUserIdProvider
    {
        public string GetUserId(Microsoft.AspNetCore.SignalR.HubConnectionContext connection)
        {
            return connection.GetHttpContext().Request.Query["userid"];
        }
    }
  1. 进行依赖注入
services.TryAddScoped(typeof(IUserIdProvider), typeof(UserIdProvider));
  1. 配置端口入口
app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<MsgHub>("msghub");
            });
  1. 实现MsgHub类
public class MsgHub : Hub
    {
        private IHttpContextAccessor _httpContextAccessor;
        private IUserIdProvider UserIdProvider;
        public MsgHub(IHttpContextAccessor httpContextAccessor, IUserIdProvider userIdProvider)
        {
            _httpContextAccessor = httpContextAccessor;
            UserIdProvider = userIdProvider;
        }
        public async Task ConnectServer(string ConnectionId)
        {
            await Clients.User(Context.UserIdentifier).SendAsync("Connect", "已成功连接服务");
        }

        public async Task SendMessage(string message)
        {
            await Clients.User(Context.UserIdentifier).SendAsync("ReceiveMessage", message);
        }
    }

注意这里的Context.UserIdentifier当我们实现了IUserIdProvider的GetUserId方法后,这里获取的就是前端请求传递的userid。

设置前端

现在就需要前端发起websocket请求,并建立连接

在SignalRService.ts里面实现InitSignalR方法,代码里面的ApiGatewayServiceUrl就是配置的网关地址

InitSignalR(Id:string){
    const connection = new signalR.HubConnectionBuilder()
    .withUrl(`${environment.ApiGatewayServiceUrl}msghub?userid=${Id}`)
    .configureLogging(signalR.LogLevel.Information)
    .build();
    connection.on('connect', Connect);
    connection.on('receivemessage', ShowReceiveMessage);

    async function start() {
      try {
        Object.defineProperty(WebSocket, 'OPEN', { value: 1, });
        await connection.start();
        connection.send('ConnectServer', connection.connectionId);
        console.log("SignalR Connected.");
      } catch (err) {
        console.log(err);
        setTimeout(start, 5000);
      }
    };
    connection.onclose(async () => {
      await start();
    });
    start();

    function Connect(msg: string) {
      console.dir(msg);
    }
    function ShowReceiveMessage(msg:string){
      console.log(msg);
    }
  }

注意:这里的Object.defineProperty(WebSocket, 'OPEN', { value: 1, });一定是显式的明确webSocket是打开状态,因为当前依赖的signalR组件依赖这个状态值,不指明的话,会报如下错误

signalr.js:2273 Unhandled Promise rejection: WebSocket is not in the OPEN state ; Zone: <root> ; Task: null ; Value: WebSocket is not in the OPEN state undefined

大概意思是

这个错误消息通常表示尝试在WebSocket连接尚未打开(即,它正在CONNECTING状态,或者已经被CLOSING或CLOSED)时发送消息。

对于WebSocket API来说,只有当WebSocket实例的readyState属性是WebSocket.OPEN(值为1)时,你才可以通过WebSocket实例安全地发送消息。其他所有状态都可能导致INVALID_STATE_ERR异常。

最后配置

在lucky里面配置转发,将暴露的外网域名地址转发到内网(网关的节点地址),这样实现外网访问通过网关与signalR服务建立连接。当建立连接后,后续的用户添加群组,退出群组等操作,均通过调用边界服务接口(为了控制权限)实现。

注意:这里的反向代理设置时,一定加上跨域,并且指明允许跨域的地址。

为什么单独部署一个SignalR服务,这样是希望消息总线有单独配置资源的服务,如果应用在实际的生产环境,方便资源的追加,有效支持即时通信服务的正常运行。

当前的实现并没有实现对用户的认证,这个可以在后期加上。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接