123 lines
5.1 KiB
C#
123 lines
5.1 KiB
C#
|
|
using Grpc.Core;
|
|||
|
|
using Grpc.Net.Client;
|
|||
|
|
using Microsoft.Extensions.Hosting;
|
|||
|
|
using Microsoft.Extensions.Logging;
|
|||
|
|
using Newtonsoft.Json.Linq;
|
|||
|
|
using SHH.CameraSdk;
|
|||
|
|
using SHH.Contracts;
|
|||
|
|
using SHH.Contracts.Grpc; // 引用 Proto 生成的命名空间
|
|||
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Threading;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
|
|||
|
|
namespace SHH.CameraService
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// gRPC 指令接收后台服务
|
|||
|
|
/// 负责:1. 逻辑注册 2. 维持指令长连接 3. 指令分发
|
|||
|
|
/// </summary>
|
|||
|
|
public class GrpcCommandReceiverWorker : BackgroundService
|
|||
|
|
{
|
|||
|
|
private readonly ILogger<GrpcCommandReceiverWorker> _logger;
|
|||
|
|
private readonly ServiceConfig _config;
|
|||
|
|
private readonly IEnumerable<ICommandHandler> _handlers; // 自动注入所有指令处理器
|
|||
|
|
|
|||
|
|
public GrpcCommandReceiverWorker(
|
|||
|
|
ILogger<GrpcCommandReceiverWorker> logger,
|
|||
|
|
ServiceConfig config,
|
|||
|
|
IEnumerable<ICommandHandler> handlers)
|
|||
|
|
{
|
|||
|
|
_logger = logger;
|
|||
|
|
_config = config;
|
|||
|
|
_handlers = handlers;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|||
|
|
{
|
|||
|
|
// 给 SDK 和数据库留出几秒钟的加载时间
|
|||
|
|
_logger.LogInformation("[gRPC Bus] 后台 Worker 准备就绪,3秒后发起连接...");
|
|||
|
|
await Task.Delay(3000, stoppingToken);
|
|||
|
|
|
|||
|
|
while (!stoppingToken.IsCancellationRequested)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 1. 地址预处理 (将 127.0.0.1 强制转换为 localhost 解决 Unimplemented 问题)
|
|||
|
|
var ep = _config.CommandEndpoints.First();
|
|||
|
|
string targetUrl = ep.Uri.Replace("tcp://", "http://").Replace("127.0.0.1", "localhost");
|
|||
|
|
|
|||
|
|
using var channel = GrpcChannel.ForAddress(targetUrl);
|
|||
|
|
var client = new GatewayProvider.GatewayProviderClient(channel);
|
|||
|
|
|
|||
|
|
// --- 第一步:发起逻辑注册 (Unary) ---
|
|||
|
|
_logger.LogInformation("[gRPC Bus] 正在发起逻辑注册: {Url}", targetUrl);
|
|||
|
|
var regResp = await client.RegisterInstanceAsync(new RegisterRequest
|
|||
|
|
{
|
|||
|
|
InstanceId = _config.AppId,
|
|||
|
|
Version = "2.0.0-grpc",
|
|||
|
|
ServerIp = "127.0.0.1",
|
|||
|
|
StartTimeTicks = DateTime.Now.Ticks
|
|||
|
|
}, cancellationToken: stoppingToken);
|
|||
|
|
|
|||
|
|
if (regResp.Success)
|
|||
|
|
{
|
|||
|
|
_logger.LogInformation("[gRPC Bus] 逻辑注册成功。正在开启长连接指令通道...");
|
|||
|
|
|
|||
|
|
// --- 第二步:开启物理指令流 (Server Streaming) ---
|
|||
|
|
using var call = client.OpenCommandChannel(new CommandStreamRequest
|
|||
|
|
{
|
|||
|
|
InstanceId = _config.AppId
|
|||
|
|
}, cancellationToken: stoppingToken);
|
|||
|
|
|
|||
|
|
// --- 第三步:阻塞式监听服务端推送 ---
|
|||
|
|
// 只要服务端通过 responseStream.WriteAsync 发消息,这里就会命中
|
|||
|
|
while (await call.ResponseStream.MoveNext(stoppingToken))
|
|||
|
|
{
|
|||
|
|
var protoMsg = call.ResponseStream.Current;
|
|||
|
|
_logger.LogInformation("[gRPC Bus] 收到远程指令: {CmdCode}", protoMsg.CmdCode);
|
|||
|
|
|
|||
|
|
// 异步分发,不阻塞接收循环
|
|||
|
|
_ = DispatchCommandAsync(protoMsg);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (OperationCanceledException) { break; }
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError("[gRPC Bus] 链路异常,5秒后重试: {Msg}", ex.Message);
|
|||
|
|
await Task.Delay(5000, stoppingToken);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 指令分发逻辑
|
|||
|
|
/// </summary>
|
|||
|
|
private async Task DispatchCommandAsync(CommandPayloadProto msg)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
// 1. 寻找匹配的处理器 (SyncCameraHandler / RemoveCameraHandler)
|
|||
|
|
var handler = _handlers.FirstOrDefault(h => h.ActionName == msg.CmdCode);
|
|||
|
|
|
|||
|
|
if (handler != null)
|
|||
|
|
{
|
|||
|
|
// 2. 将 Proto 的参数转为 JToken,保持与原有处理器兼容
|
|||
|
|
var jsonParams = JToken.Parse(msg.JsonParams);
|
|||
|
|
await handler.ExecuteAsync(jsonParams);
|
|||
|
|
_logger.LogInformation("[gRPC Bus] 指令 {CmdCode} 执行完成", msg.CmdCode);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
_logger.LogWarning("[gRPC Bus] 未找到处理 {CmdCode} 的处理器", msg.CmdCode);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_logger.LogError(ex, "[gRPC Bus] 指令执行失败: {CmdCode}", msg.CmdCode);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|