C#控制台应用调用独立方法时出现Dapr消息发布异常
在Kubernetes Cron Job中使用Dapr Sidecar时独立方法调用失败的解决方案
问题场景
将C#控制台应用作为Kubernetes Cron Job运行,通过Dapr Sidecar向Azure Service Bus队列发布消息时出现异常:Main方法内直接发布消息可正常执行,但调用独立方法PublishMessage时触发DaprException,报错连接被拒绝。
代码示例
static async Task Main(string[] args) { using (var client = new DaprClientBuilder().Build()) { string topicName = "topic1"; await client.PublishEventAsync("pubsub", topicName, "testdapr2"); Console.WriteLine("Message published successfully."); } await PublishMessage(); } private static async Task PublishMessage() { Console.WriteLine("Getting Error"); using (var client = new DaprClientBuilder().Build()) { string topicName = "topic1"; await client.PublishEventAsync("pubsub", topicName, "testdapr2"); Console.WriteLine("Message published successfully."); } }
报错信息
DaprException: Publish operation failed: the Dapr endpoint indicated a failure. See InnerException for details. ---> Grpc.Core.RpcException: Status(StatusCode="Unavailable", Detail="Error connecting to subchannel.", DebugException="System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it.") ---> System.Net.Sockets.SocketException (10061): No connection could be made because the target machine actively refused it. at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token) at System.Net.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|281_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken) at Grpc.Net.Client.Balancer.Internal.SocketConnectivitySubchannelTransport.TryConnectAsync(ConnectContext context) --- End of inner exception stack trace --- at Grpc.Net.Client.Balancer.Internal.ConnectionManager.PickAsync(PickContext context, Boolean waitForReady, CancellationToken cancellationToken) at Grpc.Net.Client.Balancer.Internal.BalancerHttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at Grpc.Net.Client.Internal.GrpcCall`2.RunCall(HttpRequestMessage request, Nullable`1 timeout) at Dapr.Client.DaprClientGrpc.MakePublishRequest(String pubsubName, String topicName, ByteString content, Dictionary`2 metadata, String dataContentType, CancellationToken cancellationToken) --- End of inner exception stack trace ---
问题分析
核心原因是Kubernetes Cron Job的生命周期与Dapr Sidecar的绑定逻辑:
- 当Main方法中第一个
using块释放Dapr客户端后,控制台应用主进程的状态变化会被Dapr的注入机制感知到,Sidecar容器可能被提前终止。 - 后续调用
PublishMessage方法创建新客户端时,Sidecar已经停止,导致无法建立连接,触发连接拒绝的Socket异常。
解决方案
1. 复用Dapr客户端实例
避免在每个方法中重复创建Dapr客户端,改为在应用启动时创建单例实例,整个执行周期内复用,确保客户端始终能连接到存活的Sidecar。
修改后的代码:
// 全局复用的Dapr客户端实例 private static readonly DaprClient _daprClient = new DaprClientBuilder().Build(); static async Task Main(string[] args) { try { string topicName = "topic1"; await _daprClient.PublishEventAsync("pubsub", topicName, "testdapr2"); Console.WriteLine("Message published successfully from Main."); await PublishMessage(); } finally { // 应用结束时释放客户端资源 (_daprClient as IDisposable)?.Dispose(); } } private static async Task PublishMessage() { Console.WriteLine("Publishing message from separate method"); string topicName = "topic1"; await _daprClient.PublishEventAsync("pubsub", topicName, "testdapr2"); Console.WriteLine("Message published successfully from separate method."); }
2. 调整Sidecar终止等待时长
在Kubernetes Cron Job的Pod模板中添加Dapr注解,延长Sidecar的终止等待时间,确保主进程完成所有异步操作后,Sidecar才会终止。
示例YAML配置片段:
apiVersion: batch/v1 kind: CronJob metadata: name: dapr-cron-job spec: schedule: "*/5 * * * *" jobTemplate: spec: template: metadata: annotations: dapr.io/enabled: "true" dapr.io/app-id: "cron-app" dapr.io/app-port: "80" # 设置Sidecar终止等待30秒 dapr.io/termination-grace-period: "30" spec: containers: - name: cron-app image: your-app-image:latest restartPolicy: OnFailure
3. 确保主进程等待所有异步任务完成
虽然代码中使用了await,但控制台应用可能存在异步任务未完全执行就开始退出的情况。可以在Main方法末尾添加额外的等待逻辑,或者使用Task.WhenAll确保所有任务完成:
static async Task Main(string[] args) { try { var mainPublishTask = _daprClient.PublishEventAsync("pubsub", "topic1", "testdapr2"); var separatePublishTask = PublishMessage(); // 等待所有发布任务完成 await Task.WhenAll(mainPublishTask, separatePublishTask); Console.WriteLine("All messages published successfully."); } finally { (_daprClient as IDisposable)?.Dispose(); } }
内容的提问来源于stack exchange,提问作者Piyush
相关产品推荐
相关产品推荐

