本系列会分为2-3篇文章.javascript
本文的内容:html
SignalR是一个.NET Core/.NET Framework的开源实时框架. SignalR的可以使用Web Socket, Server Sent Events 和 Long Polling做为底层传输方式.java
SignalR基于这三种技术构建, 抽象于它们之上, 它让你更好的关注业务问题而不是底层传输技术问题.web
SignalR这个框架分服务器端和客户端, 服务器端支持ASP.NET Core 和 ASP.NET; 而客户端除了支持浏览器里的javascript之外, 也支持其它类型的客户端, 例如桌面应用.npm
SignalR使用的三种底层传输技术分别是Web Socket, Server Sent Events 和 Long Polling.浏览器
其中Web Socket仅支持比较现代的浏览器, Web服务器也不能太老.服务器
而Server Sent Events 状况可能好一点, 可是也存在一样的问题.websocket
因此SignalR采用了回落机制, SignalR有能力去协商支持的传输类型.app
Web Socket是最好的最有效的传输方式, 若是浏览器或Web服务器不支持它的话, 就会降级使用SSE, 实在不行就用Long Polling.负载均衡
一旦创建链接, SignalR就会开始发送keep alive消息, 来检查链接是否还正常. 若是有问题, 就会抛出异常.
由于SignalR是抽象于三种传输方式的上层, 因此不管底层采用的哪一种方式, SignalR的用法都是同样的.
SignalR默认采用这种回落机制来进行传输和链接.
可是也能够禁用回落机制, 只采用其中一种传输方式.
RPC (Remote Procedure Call). 它的优势就是能够像调用本地方法同样调用远程服务.
SignalR采用RPC范式来进行客户端与服务器端之间的通讯.
SignalR利用底层传输来让服务器能够调用客户端的方法, 反之亦然, 这些方法能够带参数, 参数也能够是复杂对象, SignalR负责序列化和反序列化.
Hub是SignalR的一个组件, 它运行在ASP.NET Core应用里. 因此它是服务器端的一个类.
Hub使用RPC接受从客户端发来的消息, 也能把消息发送给客户端. 因此它就是一个通讯用的Hub.
在ASP.NET Core里, 本身建立的Hub类须要继承于基类Hub.
在Hub类里面, 咱们就能够调用全部客户端上的方法了. 一样客户端也能够调用Hub类里的方法.
这种Hub+RPC的方式仍是很是适合实时场景的.
以前说过方法调用的时候能够传递复杂参数, SignalR能够将参数序列化和反序列化. 这些参数被序列化的格式叫作Hub 协议, 因此Hub协议就是一种用来序列化和反序列化的格式.
Hub协议的默认协议是JSON, 还支持另一个协议是MessagePack. MessagePack是二进制格式的, 它比JSON更紧凑, 并且处理起来更简单快速, 由于它是二进制的.
此外, SignalR也能够扩展使用其它协议..
随着系统的运行, 有时您可能须要进行横向扩展. 就是应用运行在多个服务器上.
这时负载均衡器会保证每一个进来的请求按照必定的逻辑分配到多是不一样的服务器上.
在使用Web Socket的时候, 没什么问题, 由于一旦Web Socket的链接创建, 就像在浏览器和那个服务器之间打开了隧道同样, 服务器是不会切换的.
可是若是使用Long Polling, 就可能有问题了, 由于使用Long Polling的状况下, 每次发送消息都是不一样的请求, 而每次请求可能会到达不一样的服务器. 不一样的服务器可能不知道前一个服务器通讯的内容, 这就会形成问题.
针对这个问题, 咱们须要使用Sticky Sessions (粘性会话).
Sticky Sessions 貌似有不少中实现方式, 可是主要是下面要介绍的这种方式.
做为第一次请求的响应的一部分, 负载均衡器会在浏览器里面设置一个Cookie, 来表示使用过这个服务器. 在后续的请求里, 负载均衡器读取Cookie, 而后把请求分配给同一个服务器.
使用空模板创建ASP.NET Core项目.
创建一个CountService:
创建一个CountHub, 继承于Hub:
在Startup里注册SignalR:
若是须要的话能够在AddSignalR()这个方法里使用lambda表达式进行一些配置.
而后在管道里使用SignalR, 使用app.UseSignalR():
这里我已经创建了一个Hub, 叫作CountHub.
该方法的参数类型是Action<HubRouteBuilder>, 而后在这里配置hub的路由.
首先创建一个Controller, 并注入IHubContext<CountHub>:
接下来咱们就可使用IHubContext<CountHub>这个对象与客户端进行实时通讯了.
下面创建一个POST Action, 客户端点击按钮以后来到这个Action, 在这里咱们使用hub为全部的客户端发送一个消息:
这里, 我调用了全部客户端上的someFunc这个方法, 参数是一个对象.
可是使用这种IHubContext<Hub>的注入方式, 咱们没法在它那取得Caller(调用该方法的客户端)这个属性.
从Hub的Context属性, 咱们能够得到用户的信息.
咱们在CountHub里override父类的一个方法OnConnectedAsync():
若是有新的链接创建了, 这个方法就会被执行.
在Hub类里, 咱们能够访问到Context属性. 从Context属性那, 咱们能够得到一个经常使用的属性叫作ConnectionId. 这个ConnectionId就是链接到Hub的这个客户端的惟一标识.
使用ConnectionId, 咱们就能够取得这个客户端, 并调用其方法, 如图中的Clients.Client(connectionId).xxx.
Hub的Clients属性表示客户端, 它有若干个方法能够选择客户端, 刚才的Client(connectionId)就是使用connectionId找到这一个客户端. 而AllExcept(connectionId)就是除了这个connectionId的客户端以外的全部客户端. 更多方法请查看文档.
SignalR还有Group分组的概念, 并且操做简单, 这里用到的是Hub的Groups属性. 向一个Group名添加第一个connectionId的时候, 分组就被创建. 移除分组内最后一个客户端的时候, 分组就被删除了. 使用Clients.Group("组名")能够调用组内客户端的方法.
SignalR会采用ASP.NET Core配置好的受权和验证体系.
用法和Controller差很少:
想要取得User对象, 须要使用Context.User, 它的类型是ClaimsPrinciple:
客户端须要安装signalr这个库. 可使用npm安装 @aspnet/signalr
可是实际上只须要signalr.js一个文件便可.
客户端代码以下:
点击按钮后先执行Controller的POST方法, POST返回的是Accepted(1), 因此id是1.
使用singalR对象的HubConnectionBuilder来构建connection. 使用返回的connection对象, 咱们能够用它的on方法来处理服务器端方法调用的响应. 响应方法的参数能够是简单类型也能够是复杂的对象.
使用connection.start()来打开链接, 使用catch()来捕获异常, 使用connection.stop() 关闭链接.
先运行一下看看效果:
能够看到使用Clients.All, 全部的客户端的方法都会被调用.
刚打开页面的时候, 咱们就尝试创建链接, 从F12能够看到一个叫作negotiate的请求被发送了:
这个请求的body以下:
能够看到客户端选择了一个connectionId, 里面还有浏览器支持的传输方式.
服务器的响应:
响应也包含着connectionId, 以及服务器支持的传输方式. 这里三种都支持. 因为我没有指定传输方式, 因此SignalR选择了最好的方式: websocket.
而在我点击按钮后, Web Socket链接才被初始化:
若是须要手动指定传输方式, 请在withUrl()方法的第二个参数指定传输方式:
.NET 客户端能够安装 Microsoft.AspNetCore.SignalR.Client 这个包来支持SignalR.
具体用法请查看官方文档, 语法和js的差很少.
须要安装 Microsoft.AspNetCore.SignalR.Protocols.MessagePack.
而后在Startup里面使用AddMessagePackProtocol()这个方法便可:
这样的话, 服务器端既支持JSON, 也支持MessagePack了.
另外.NET客户端也须要安装这个MessagePack包.
而js客户端须要安装 @aspnet/signalr-protocol-msgpack.
能够采用Redis, 须要安装 Microsoft.AspNetCore.SignalR.Redis. 这个包.
而后在Startup里面配置:
这个没试过, 请看官方文档.
SignalR就介绍这些....