.NET Core SignalR Redis底板详解(二)
接上文。
上文说到Clients.All.SendAsync实际上是调用AllClientProxy的SendCoreAsync方法。其实主要是调用IClientProxy的SendCoreAsync。在上文说到的HubClients类里。就有很多IClientProxy的实现类。比如刚刚说到的All其实是AllClientProxy对象。
public IClientProxy AllExcept(IReadOnlyList<string> excludedConnectionIds) { return new AllClientsExceptProxy<THub>(_lifetimeManager, excludedConnectionIds); } public IClientProxy Client(string connectionId) { return new SingleClientProxy<THub>(_lifetimeManager, connectionId); } public IClientProxy Group(string groupName) { return new GroupProxy<THub>(_lifetimeManager, groupName); } public IClientProxy GroupExcept(string groupName, IReadOnlyList<string> excludedConnectionIds) { return new GroupExceptProxy<THub>(_lifetimeManager, groupName, excludedConnectionIds); } public IClientProxy Clients(IReadOnlyList<string> connectionIds) { return new MultipleClientProxy<THub>(_lifetimeManager, connectionIds); } public IClientProxy Groups(IReadOnlyList<string> groupNames) { return new MultipleGroupProxy<THub>(_lifetimeManager, groupNames); } public IClientProxy User(string userId) { return new UserProxy<THub>(_lifetimeManager, userId); } public IClientProxy Users(IReadOnlyList<string> userIds) { return new MultipleUserProxy<THub>(_lifetimeManager, userIds); }
这些实现类其实里面都是千篇一律的构造函数和SendCoreAsync。真正起作用的还是HubLifetimeManager对象。上文中说了。在AdRedis的时候就已经把RedisHubLifetimeManager注入进去了。所以这里其实真正调用的还是RedisHubLifetimeManager中的SendAllAsync方法。
public override Task SendAllAsync(string methodName, object[] args, CancellationToken cancellationToken = default) { var message = _protocol.WriteInvocation(methodName, args); return PublishAsync(_channels.All, message); }
在WriteInvocation方法中完成广播的发送。(这段代码太晦涩。我没看懂,只是猜个大概)。在PublishAsync实现的是Redis的推送。
那么其他服务器是怎么收到广播的呢?
同样是在RedisHubLifetimeManager中。重写了OnConnectedAsync方法。
public override async Task OnConnectedAsync(HubConnectionContext connection) { await EnsureRedisServerConnection(); var feature = new RedisFeature(); connection.Features.Set<IRedisFeature>(feature); var connectionTask = Task.CompletedTask; var userTask = Task.CompletedTask; _connections.Add(connection); connectionTask = SubscribeToConnection(connection); if (!string.IsNullOrEmpty(connection.UserIdentifier)) { userTask = SubscribeToUser(connection); } await Task.WhenAll(connectionTask, userTask); }
在这里通过SubscribeToConnection方法完成了Redis的订阅。这个OnConnectedAsync方法在HubConnectionHandler中的OnConnectedAsync中被调用。从名字就可以看出来HubConnectionHandler是处理HubConnection的。而HubConnectionHandler的调用则是在我们一开始UseHub的时候。
public static IConnectionBuilder UseHub<THub>(this IConnectionBuilder connectionBuilder) where THub : Hub { var marker = connectionBuilder.ApplicationServices.GetService(typeof(SignalRCoreMarkerService)); if (marker == null) { throw new InvalidOperationException("Unable to find the required services. Please add all the required services by calling " + "'IServiceCollection.AddSignalR' inside the call to 'ConfigureServices(...)' in the application startup code."); } return connectionBuilder.UseConnectionHandler<HubConnectionHandler<THub>>(); }
至此。整个广播流程就讲解完毕了。
这里说一下为什么前言中的方法不好。因为用接口暴露的形式其实是走Http协议。而Redis的订阅与发布是走RESP协议。Http是超文本传输协议在子房间多的情况下会额外用掉很多带宽与IIS的处理资源。而且用Redis的订阅发布模式,Channel的方式与Signalr的Group等其他方式的思路其实是相同的。
最后贴一下微软的官方说明。
设置 ASP.NET Core SignalR 横向扩展 Redis 底板
https://docs.microsoft.com/zh-cn/aspnet/core/signalr/redis-backplane?view=aspnetcore-2.2
教程:ASP.NET Core SignalR 入门
原文地址:https://www.cnblogs.com/dbdn/p/11390928.html
- Visual Studio 2008 每日提示(三十六)
- zephir-(6)运算符
- zephir-(12)php函数和异常处理
- phalapi-进阶篇7(使用缓存以及用redis拓展解决实际问题)
- 5个不为人知的Java API使用技巧
- zephir-(3)你的第一个PHP拓展
- zephir-(7)数组
- phalapi-进阶篇5(数据库读写分离以及多库使用)
- phalapi-进阶篇4(notrom进阶以及事务操作)
- 自动机器学习:利用遗传算法优化递归神经网络
- zephir-(8)类和对象1
- zephir-(4)基本语法
- zephir-(2)安装和初体验
- phalcon-入门篇8(Model层基础使用2)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Js中Symbol对象
- 责任链模式
- Android Studio升级到3.0后遇到的坑
- Android原生项目集成React Native的方法
- Android编程之光线传感器用法详解
- Android Studio 3.0 新功能全面解析和旧项目适配问题
- Android开发中使用外部应用获取SD卡状态的方法
- Android编程使用光线传感器获取光线强弱的方法【LightSensorManager封装类】
- Android开发中的重力传感器用法实例详解
- 腾讯云TKE-Ingress案例: Nginx-Ingress 实现grpc转发
- 机器人软件中间层 yarp-Yet Another Robot Platforms
- 3分钟短文:Laravel写个命令行,你就是下一个Geek!
- Android开发多年每天Crud不清楚自己的技术?来刷刷大厂的高端技术面试题就知道了
- MySQL案例:count(*)效率优化
- MUI进行APP混合开发实现下拉刷新和上拉加载 原创