using Grpc.Core; using Microsoft.AspNetCore.SignalR; using Microsoft.Azure.Cosmos.Linq; using Microsoft.Extensions.Primitives; using TEAMModelOS.SDK.DI; using TEAMModelOS.SDK.Extension; using TEAMModelOS.SDK; namespace HTEX.Complex.Services { public class SignalRScreenServerHub : Hub { private readonly AzureRedisFactory _azureRedis; public SignalRScreenServerHub(AzureRedisFactory azureRedis) { _azureRedis = azureRedis; } /// /// 客户连接成功时触发 /// /// public override async Task OnConnectedAsync() { var connid = Context.ConnectionId; var httpContext = Context.GetHttpContext(); if (httpContext != null) { //wss://www.winteach.cn/signalr/notify?grant_type=wechat_qrcode&scene=0a75aca57536490ba00fe62e27bb8f6c&id=U2MNiCFNPPuVcw2gUI_gRA //wss://www.winteach.cn/signalr/notify?grant_type=bookjs_api&clientid={clientid}&id=客户端自动生成的 httpContext.Request.Query.TryGetValue("grant_type", out StringValues grant_type); httpContext.Request.Query.TryGetValue("clientid", out StringValues clientid); var ip = GetIP(httpContext); await Groups.AddToGroupAsync(connid, grant_type!); if (!clientid.Equals(StringValues.Empty) && !grant_type.Equals(StringValues.Empty)) { ///连接配置,并且使用钉钉 通知。 /// var client = new SignalRClient { connid = connid, grant_type = grant_type, clientid= clientid }; await _azureRedis.GetRedisClient(8).StringSetAsync($"SignalRClient:connects:{connid}", client.ToJsonString()); switch (true) { case bool when grant_type.Equals(ScreenConstant.grant_type): ScreenClient screenClient = null; var value = await _azureRedis.GetRedisClient(8).HashGetAsync($"ScreenApi:clients", client.clientid); if (value!=default && value.HasValue) { screenClient = value.ToString().ToObject(); // 这里不强制设置free ,因为如果是重连,可能正在执行命令,需要等待执行完成 //screenClient.status="free"; screenClient.connid=connid; } else { screenClient= new ScreenClient { status=ScreenConstant.free, connid=connid, grant_type=grant_type, clientid= clientid, desc="", domain="", ip=ip, time= DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), weight= 0, duration=0, }; } //连接成功,发送消息给客户端。 await SendConnection(connid, new ConnectionMessageContent { clientid = clientid, status = ScreenConstant.free, grant_type = grant_type, content = $"连接成功" }); //先检查状态是否是在忙碌,在时间戳范围里,如果不在时间戳范围,强制free。 if (!screenClient.status!.Equals(ScreenConstant.free) &&screenClient.time + screenClient.duration < DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()) { screenClient.status = ScreenConstant.free; } if (screenClient.status!.Equals(ScreenConstant.free)) { //连接成功,马上分发任务。 //从尾部弹出元素,队列先进先出 var queueValue = await _azureRedis.GetRedisClient(8).ListRightPopAsync("PDFGen:Queue"); if (queueValue!=default && queueValue.HasValue) { PDFGenQueue genQueue = queueValue.ToString().ToObject(); await SendConnection(connid, new ScreenProcessMessageContent { clientid = clientid, status = ScreenConstant.busy, grant_type = grant_type, content =$"{queueValue.ToString()}",//从Redis中获取任务信息 }); screenClient.status = ScreenConstant.busy; screenClient.time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); //预计占用时长,并冗余5秒 screenClient.duration= genQueue.timeout+genQueue.delay+ScreenConstant.time_excess; } } await _azureRedis.GetRedisClient(8).HashSetAsync($"ScreenApi:clients", client.clientid, screenClient.ToJsonString()); break; } } else { await SendConnection(connid, new ConnectionMessageContent { clientid = string.Empty, status =ScreenConstant.error, grant_type = grant_type, content = "客户端配置错误", connid = connid, }); } } } public async override Task OnDisconnectedAsync(Exception? exception) { var connid = Context.ConnectionId; var redisData = await _azureRedis.GetRedisClient(8).StringGetAsync($"SignalRClient:connects:{connid}"); if (!redisData.IsNullOrEmpty) { await _azureRedis.GetRedisClient(8).KeyDeleteAsync($"SignalRClient:connects:{connid}"); var client = redisData.ToString().ToObject(); if (client != null) { var value = await _azureRedis.GetRedisClient(8).HashGetAsync($"ScreenApi:clients", client.clientid); if (value!=default && value.HasValue) { ScreenClient screenClient = value.ToString().ToObject() ; screenClient.status=ScreenConstant.down; screenClient.connid= string.Empty; await _azureRedis.GetRedisClient(8).HashSetAsync($"ScreenApi:clients", client.clientid, screenClient.ToJsonString()); } await Groups.RemoveFromGroupAsync(connid, client.grant_type!); ///连接配置,并且使用钉钉 离线通知。 } } } public async Task SendConnection(string connectionId, MessageBody msg) { await Clients.Client(connectionId).ReceiveConnection(msg); } public string GetIP(HttpContext httpContext) { var IpPort = httpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault(); if (string.IsNullOrEmpty(IpPort)) { IpPort = $"{httpContext.Connection.RemoteIpAddress}"; } if (IpPort.Contains("::")) { IpPort = "127.0.0.1"; } return IpPort; } } }