首页 > 解决方案 > 使用机器名时 gRPC DNS 解析失败

问题描述

我是 gRPC 的新手,并试图通过使用此处cactuaroid 的聊天服务器/客户端示例来学习它。我已修改代码以显示 WPF 应用程序中长时间运行的任务的进度。所有代码都在 .NET 5 上运行,我使用的是最新版本的 gRPC 包。

该过程在使用计算机的 IP 地址时工作正常,但在为 gRPC 客户端使用计算机名称时,我收到“DNS 解析失败”异常(计算机名称为“skylake”):

RpcException: Status(StatusCode="Unavailable", Detail="DNS 解析失败服务: skylake:6001", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1615312867.300000000","description":"解析器瞬时故障","file":"......\src\core\ext\filters\client_channel\client_channel.cc","file_line":2138,"referenced_errors":[{"created":"@ 1615312867.300000000","description":"服务的DNS解析失败:skylake:6001","file":"......\src\core\ext\filters\client_channel\resolver\dns\c_ares\dns_resolver_ares.cc ","file_line":362,"grpc_status":14,"referenced_errors":[{"created":"@1615312867.300000000","description":"C-ares 状态不是 ARES_SUCCESS qtype=AAAA name=skylake is_balancer=0: 无法联系 DNS 服务器","file":"......\src\core\ext\filters\client_channel\ resolver\dns\c_ares\grpc_ares_wrapper.cc","file_line":716,"referenced_errors":[{"created":"@1615312866.142000000","description":"C-ares 状态不是 ARES_SUCCESS qtype=A name=skylake is_balancer=0: 无法联系 DNS 服务器","file":"......\src\core\ext\filters\client_channel\resolver\dns\c_ares\grpc_ares_wrapper.cc","file_line":716} ]}]}]}")\src\core\ext\filters\client_channel\resolver\dns\c_ares\grpc_ares_wrapper.cc","file_line":716,"referenced_errors":[{"created":"@1615312866.142000000","description":"C- ares 状态不是 ARES_SUCCESS qtype=A name=skylake is_balancer=0: 无法联系 DNS 服务器","file":"......\src\core\ext\filters\client_channel\resolver\dns\c_ares\ grpc_ares_wrapper.cc","file_line":716}]}]}]}")\src\core\ext\filters\client_channel\resolver\dns\c_ares\grpc_ares_wrapper.cc","file_line":716,"referenced_errors":[{"created":"@1615312866.142000000","description":"C- ares 状态不是 ARES_SUCCESS qtype=A name=skylake is_balancer=0: 无法联系 DNS 服务器","file":"......\src\core\ext\filters\client_channel\resolver\dns\c_ares\ grpc_ares_wrapper.cc","file_line":716}]}]}]}")\src\core\ext\filters\client_channel\resolver\dns\c_ares\grpc_ares_wrapper.cc","file_line":716}]}]}]}")\src\core\ext\filters\client_channel\resolver\dns\c_ares\grpc_ares_wrapper.cc","file_line":716}]}]}]}")

我验证我可以使用 telnet skylake 6001 到达该端口。我正在本地测试,客户端和服务器都在同一台机器上。奇怪的是,gRPC 服务器似乎与计算机名称一样好。它只是有问题的客户。

服务器代码:

[Export(typeof(IService))]
    public class ProgressServiceGrpcServer : Progress.ProgressBase, IService
    {
        [Import]
        private Logger m_logger = null;

        [Import]
        private ProgressService m_progressService = null;
        private readonly Empty m_empty = new Empty();

        private const int Port = 6001;
        private readonly Grpc.Core.Server m_server;

        public ProgressServiceGrpcServer()
        {
            m_server = new Grpc.Core.Server
            {
                Services =
                {
                    Progress.BindService(this)
                        .Intercept(new IpAddressAuthenticator())
                },
                Ports =
                {
                    new ServerPort("skylake", Port, ServerCredentials.Insecure)
                }
            };
        }

        public void Start()
        {
            m_server.Start();
            m_logger.Info("Started.");
        }

        public override async Task Subscribe(ChannelName channelName, IServerStreamWriter<ProgressReport> responseStream, ServerCallContext context)
        {
            context.CancellationToken.Register(() => m_logger.Info($"{context.Host} cancels subscription."));

            try
            {
                await m_progressService.GetProgressReportsAsObservable(channelName)
                    .ToAsyncEnumerable()
      .ForEachAwaitAsync(async (x) => await responseStream.WriteAsync(x), context.CancellationToken)
                    .ConfigureAwait(false);
            }
            catch (TaskCanceledException)
            {
                m_logger.Info($"{context.Host} unsubscribed.");
            }
        }

        public override Task<Empty> Write(ProgressReport request, ServerCallContext context)
        {
            m_logger.Info($"{context.Host} {request}");
            m_progressService.Add(request);
            return Task.FromResult(m_empty);
        }
    }

客户端代码:

public class ProgressServiceClient
    {
        private readonly Progress.ProgressClient m_client =
            new Progress.ProgressClient(
                new Channel("skylake”, 6001, ChannelCredentials.Insecure));

        public async Task Write(ProgressReport progressReport)
        {
            await m_client.WriteAsync(progressReport);
        }

        public IAsyncEnumerable<ProgressReport> ProgressReports(ChannelName channelName)
        {
            var call = m_client.Subscribe(channelName);

            return call.ResponseStream
                .ToAsyncEnumerable()
                .Finally(() => call.Dispose());
        }
    }

进度写入方法:

while (inProgress)
            {
                progressServiceClient.Write(new GrpcServer.ProgressReport
                {
                    Id = Task.Id.ToString(),
                    PercentDone = percentDone,
                    TimeRemain = timeRemain
                }).Wait();

                Thread.Sleep(500);
            }

进度读取方法:

m_progressService = new ProgressServiceClient();
ChannelName channelName = new ChannelName() { Id = id };

var cts = new CancellationTokenSource();
            _ = m_progressService.ProgressReports(channelName)
                .ForEachAsync((x) =>
                {
                    Log.Debug($"id: {x.Id} progress: {x.PercentDone}");
                }, cts.Token);

            this.Dispatcher.Invoke(() =>
            {
                Application.Current.Exit += (_, __) => cts.Cancel();
                this.Unloaded += (_, __) => cts.Cancel();
            });

标签: c#.net-coregrpc

解决方案


感谢@jdweng 为我指明了正确的方向,这是通过将DNS 后缀添加到主机名(在我的例子中为skylake.lan)来解决的。我们可以通过 IPInterfaceProperties.DnsSuffix 获取 DNS 后缀。

或者,(这可能更安全)我们可以使用正确的 IP 地址而不是主机名,方法是使用类似.


推荐阅读