SignalRScreenServerHub.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. using Grpc.Core;
  2. using Microsoft.AspNetCore.SignalR;
  3. using Microsoft.Azure.Cosmos.Linq;
  4. using Microsoft.Extensions.Primitives;
  5. using TEAMModelOS.SDK.DI;
  6. using TEAMModelOS.SDK.Extension;
  7. using TEAMModelOS.SDK;
  8. using System.Web;
  9. using System.Text;
  10. using StackExchange.Redis;
  11. namespace HTEX.Complex.Services
  12. {
  13. public class SignalRScreenServerHub : Hub<IClient>
  14. {
  15. private readonly ILogger<SignalRScreenServerHub> _logger;
  16. private readonly AzureRedisFactory _azureRedis;
  17. public SignalRScreenServerHub(AzureRedisFactory azureRedis, ILogger<SignalRScreenServerHub> logger)
  18. {
  19. _logger = logger;
  20. _azureRedis = azureRedis;
  21. }
  22. /// <summary>
  23. /// 客户连接成功时触发
  24. /// </summary>
  25. /// <returns></returns>
  26. public override async Task OnConnectedAsync()
  27. {
  28. var connid = Context.ConnectionId;
  29. var httpContext = Context.GetHttpContext();
  30. if (httpContext != null)
  31. {
  32. //wss://www.winteach.cn/signalr/notify?grant_type=wechat_qrcode&scene=0a75aca57536490ba00fe62e27bb8f6c&id=U2MNiCFNPPuVcw2gUI_gRA
  33. //wss://www.winteach.cn/signalr/notify?grant_type=bookjs_api&clientid={clientid}&id=客户端自动生成的
  34. httpContext.Request.Query.TryGetValue("grant_type", out StringValues grant_type);
  35. httpContext.Request.Query.TryGetValue("clientid", out StringValues clientid);
  36. httpContext.Request.Query.TryGetValue("device", out StringValues _device);
  37. await Groups.AddToGroupAsync(connid, grant_type!);
  38. if (!clientid.Equals(StringValues.Empty) && !grant_type.Equals(StringValues.Empty)) {
  39. ///连接配置,并且使用钉钉 通知。
  40. ///
  41. var client = new SignalRClient
  42. {
  43. connid = connid,
  44. grant_type = grant_type,
  45. clientid= clientid
  46. };
  47. await _azureRedis.GetRedisClient(8).HashSetAsync($"SignalRClient:connects", connid, client.ToJsonString());
  48. ClientDevice device = HttpUtility.UrlDecode(_device, Encoding.Unicode).ToObject<ClientDevice>();
  49. switch (true)
  50. {
  51. case bool when grant_type.Equals(ScreenConstant.grant_type):
  52. ScreenClient screenClient ;
  53. var value = await _azureRedis.GetRedisClient(8).HashGetAsync($"ScreenApi:clients", client.clientid);
  54. if (value!=default && value.HasValue)
  55. {
  56. screenClient = value.ToString().ToObject<ScreenClient>();
  57. // 这里不强制设置free ,因为如果是重连,可能正在执行任务,需要等待执行完成
  58. //screenClient.status="free";
  59. //先检查状态是否是在忙碌,在时间戳范围里,如果不在时间戳范围,强制free。
  60. if (!screenClient.status!.Equals(ScreenConstant.free) && screenClient.last_time + screenClient.timeout+ screenClient.delay + ScreenConstant.time_excess < DateTimeOffset.UtcNow.ToUnixTimeMilliseconds())
  61. {
  62. screenClient.status = ScreenConstant.free;
  63. }
  64. }
  65. else
  66. {
  67. screenClient = new ScreenClient
  68. {
  69. status = ScreenConstant.free,
  70. };
  71. }
  72. screenClient.connid=connid;
  73. screenClient.grant_type = grant_type;
  74. screenClient.clientid = clientid;
  75. screenClient.os = device.os;
  76. screenClient.port = device.port;
  77. screenClient.name = device.name;
  78. screenClient.region = device.region;
  79. screenClient.remote = device.remote;
  80. screenClient.networks = device.networks;
  81. screenClient.screenUrl = device.screenUrl;
  82. screenClient.delay = device.delay;
  83. screenClient.timeout = device.timeout;
  84. screenClient.last_time= DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  85. //连接成功,发送消息给客户端。
  86. await SendConnection(connid, new ConnectionMessageContent
  87. {
  88. connid=connid,
  89. clientid = clientid,
  90. status = screenClient.status,
  91. grant_type = grant_type,
  92. message_type= MessageType.conn_success,
  93. content = $"连接成功"
  94. });
  95. _logger.LogInformation($"客户端连接成功=>{screenClient.name},{clientid}:\n{screenClient.ToJsonString()}");
  96. if (screenClient.status!.Equals(ScreenConstant.free)) {
  97. _logger.LogInformation($"客户端当前空闲=>{screenClient.name},{clientid},分发任务......");
  98. //连接成功,马上分发任务。
  99. //从尾部弹出元素,队列先进先出
  100. var queueValue = await _azureRedis.GetRedisClient(8).ListRightPopAsync("PDFGen:Queue");
  101. if (queueValue!=default && queueValue.HasValue)
  102. {
  103. PDFGenQueue genQueue = queueValue.ToString().ToObject<PDFGenQueue>();
  104. await SendMessage(connid, new ScreenProcessMessageContent
  105. {
  106. clientid = clientid,
  107. status = ScreenConstant.busy,
  108. grant_type = grant_type,
  109. content =$"{queueValue.ToString()}",//从Redis中获取任务信息
  110. });
  111. screenClient.status = ScreenConstant.busy;
  112. screenClient.last_time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  113. }
  114. else {
  115. _logger.LogInformation($"客户端当前空闲=>{screenClient.name},{clientid},暂无任务可领取的任务......");
  116. }
  117. }
  118. await _azureRedis.GetRedisClient(8).HashSetAsync($"ScreenApi:clients", client.clientid, screenClient.ToJsonString());
  119. break;
  120. }
  121. }
  122. else
  123. {
  124. await SendConnection(connid, new ConnectionMessageContent
  125. {
  126. clientid = string.Empty,
  127. status =ScreenConstant.error,
  128. grant_type = grant_type,
  129. message_type= MessageType.conn_error,
  130. content = "客户端配置错误",
  131. connid = connid,
  132. });
  133. }
  134. }
  135. }
  136. public async override Task OnDisconnectedAsync(Exception? exception)
  137. {
  138. var connid = Context.ConnectionId;
  139. var redisData = await _azureRedis.GetRedisClient(8).HashGetAsync($"SignalRClient:connects", connid);
  140. _logger.LogInformation($"客户端断开连接=>{connid} ");
  141. ///连接配置,并且使用钉钉 离线通知。
  142. if (!redisData.IsNullOrEmpty)
  143. {
  144. var client = redisData.ToString().ToObject<SignalRClient>();
  145. await _azureRedis.GetRedisClient(8).HashDeleteAsync($"SignalRClient:connects", connid);
  146. if (client != null)
  147. {
  148. await Groups.RemoveFromGroupAsync(connid, client.grant_type!);
  149. var value = await _azureRedis.GetRedisClient(8).HashGetAsync($"ScreenApi:clients", client.clientid);
  150. if (value!=default && value.HasValue)
  151. {
  152. ScreenClient screenClient = value.ToString().ToObject<ScreenClient>() ;
  153. _logger.LogInformation($"客户端断开连接=>{connid},{screenClient.name},{screenClient.clientid} ");
  154. long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
  155. // 判断是否过期
  156. if (screenClient.status!.Equals(ScreenConstant.busy ) && screenClient.last_time+screenClient.timeout+screenClient.delay+ ScreenConstant.time_excess <=now)
  157. {
  158. screenClient.status=ScreenConstant.down;
  159. screenClient.connid= string.Empty;
  160. await _azureRedis.GetRedisClient(8).HashSetAsync($"ScreenApi:clients", client.clientid, screenClient.ToJsonString());
  161. }
  162. }
  163. }
  164. }
  165. }
  166. public async Task SendConnection(string connectionId, MessageBody msg)
  167. {
  168. await Clients.Client(connectionId).ReceiveConnection(msg);
  169. }
  170. public async Task SendMessage(string connectionId, MessageBody msg)
  171. {
  172. await Clients.Client(connectionId).ReceiveMessage(msg);
  173. }
  174. public async Task SendDisConnection(string connectionId, MessageBody msg)
  175. {
  176. await Clients.Client(connectionId).ReceiveDisConnection(msg);
  177. }
  178. }
  179. }