.NET 服务发现

https://learn.microsoft.com/en-us/dotnet/core/extensions/service-discovery?tabs=dotnet-cli

目前该包处于预览状态

在本文中,我将为你介绍如何使用 Microsoft.Extensions.ServiceDiscovery 库,服务发现是开发者使用逻辑名称而不是物理地址 ( IP 地址和端口) 来使用外部服务的方式。

入门

为了在 .NET 中使用服务发现,首先需要安装 Microsoft.Extensions.ServiceDiscovery NuGet 包,使用如下的命令

dotnet add package Microsoft.Extensions.ServiceDiscovery --prerelease

使用示例

在项目的 Program.cs 中,调用 AddServiceDiscovery() 扩展方法,以添加服务发现的功能到 Host 中,它会配置默认的服务端点解析器。

builder.Services.AddServiceDiscovery();

然后,通过在每个 IHttpClientBuilder 上调用 UseServiceDiscovery() 扩展方法来添加服务发现的支持。

builder.Services.AddHttpClient<CatalogServiceClient>(static client =>
{
client.BaseAddress = new("http://catalog");
})
.UseServiceDiscovery();

另外,你还可以通过为所有的 HttpClient 实例默认增加服务发现的支持,

builder.Services.ConfigureHttpClientDefaults(static http =>
{
// Turn on service discovery by default
http.UseServiceDiscovery();
});

通过配置来解析服务端点

AddServiceDiscovery() 扩展方法默认提供了基于配置的解析器支持。该解析器通过 .NET 的配置系统来获得端点信息。库支持通过来自 appsettings.json、环境变量、或者其它的 IConfiguration 源的配置信息。

下面的示例演示了如何为名为 catalog 的服务通过 appsettings.json 来配置服务端点:

{
"Services": {
"catalog": [
"localhost:8080",
"10.46.24.90:80",
]
}
}

上面的示例为名为 catalog 的服务配置了两个服务端点:localhost:808010.46.24.90:80。每当解析 catalog 的时候,就会选中这两个端点之一。

如果服务发现是通过 IServiceCollection 上的 AddServiceDiscoveryCore() 方法添加的,那么基于配置端点解析可以通过调用 IServiceCollection 上的 AddConfigurationServiceEndPointResolver() 扩展方法来添加。

配置

基于配置的解析器使用 ConfigurationServiceEndPointResolverOptions 类型进行配置,它支持下列配置方式:

  • SectionName: 配置包含服务端点的配置节的名称,默认为 Services
  • ApplyHostNameMetadata: 用来决定是否主机名的元数据需要用于解析端点的委托,默认该函数返回 false

如果需要使用这些配置,可以调用应用程序的 Program 文件中的 Startup 类型中 IServiceCollection 上的 Configure() 扩展方法。

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<ConfigurationServiceEndPointResolverOptions>(
static options =>
{
options.SectionName = "MyServiceEndpoints"; // Configure the logic for applying host name metadata
options.ApplyHostNameMetadata = static endpoint =>
{
// Your custom logic here. For example:
return endpoint.EndPoint is DnsEndPoint dnsEp
&& dnsEp.Host.StartsWith("internal");
};
});

上面的示例展示了配置自定义的服务配置节名称,以及提供了对于使用主机名元数据的自定义条件。

使用平台提供的服务发现机制来解析服务端点

特定的平台,诸如 Azure Container Apps,还有 Kubernetes (当正确配置之后),会提供在不需要客户端库提供的服务器发现支持的情况下提供服务发现的支持。当应用部署在这样的场景下,使用平台内置的机制会更有优势。而 Pass-through 解析器就是为此场景而设计,它支持取代解析器,比如基于配置的解析器,在不同的环境下,比如在开发者的机器上。重要的是,这种灵活性可以在不需要修改任何代码或者实现某种条件检查的情况下达到。

该 Pass-through 解析器不使用外部解析,而是通过返回诸如 DnsEndPoint 通过服务名返回的端点。

当通过调用 AddServiceDiscovery() 扩展方法使用服务发现支持的时候,Pass-through 提供器是被默认配置的。

如果服务发现是通过 IServiceCollection 上的 AddServiceDiscoveryCore() 方法添加的,那么 Pass-through 提供器可以通过调用 IServiceCollection 上的 AddPassThroughServiceEndPointResolver () 扩展方法来添加。

使用端点选择器的负载均衡

每当通过 HttpClient 管线解析一个端点的时候,就会从对应请求服务的所有已知端点中选择其一。如果存在多个端点可用,那么可能希望通过在所有端点中均衡流量。为达到此目的,可以使用定制的 端点选择器,默认情况下,端点被轮流选择。为了使用不一样的端点选择器,为 UseServiceDiscovery() 方法提供一个 IServiceEndPointSelector 实例。例如,为了从一组解析的端点中随机选择端点,可以指定 RandomServiceEndPointSelectorProvider.Instance 作为端点选择器。

builder.Services.AddHttpClient<CatalogServiceClient>(
static client => client.BaseAddress = new("http://catalog")
)
.UseServiceDiscovery(RandomServiceEndPointSelectorProvider.Instance);

NuGet 包 Microsoft.Extensions.ServiceDiscovery 包含了如下的端点选择器:

  • PickFirstServiceEndPointSelectorProvider.Instance: 选择第一个,总是选择第一个端点
  • RoundRobinServiceEndPointSelectorProvider.Instance: 轮流选择, 转圈选择端点
  • RandomServiceEndPointSelectorProvider.Instance: 随机,随机选择端点
  • PowerOfTwoChoicesServiceEndPointSelectorProvider.Instance: 二次幂选择,基于二次幂算法选择最少使用的端点来进行负载均衡。在提供的任一终结点没有 IEndPointLoadFeature 功能时,降级为随机选择终结点。

端点选择器通过 IServiceEndPointSelectorProvider 实例来创建,比如上面列出的这些选择器。Provider 的 CreateSelector() 方法在创建选择器的时候被调用,得到一个 IServiceEndPointSelector。然后当需要解析的时候,一套已知的端点被提供给 IServiceEndPointSelector 实例,使用 IServiceEndPointSelector.SetEndPoints() 方法完成。为了从集合中选择一个端点,则 IServiceEndPointSelector.GetEndPoint() 方法被调用,返回单个的 ServiceEndPoint 实例。传递给 GetEndPoint() 方法的参数 context 值,用于提供额外的对于选择器有用的上下文信息。例如,在 HttpClient 中,HttpRequestMessage 被传递进来。还没有一个 IServiceEndPointSelector 的实现检查该上下文,现在你可以忽略它,除非你在使用某个选择器,并确实使用它。

解析的顺序

当解析服务端点的时候,每个被注册的解析器按照注册的顺序被调用,并有机会修改返回给调用者的 ServiceEndPoint 集合,在 Microsoft.Extensions.ServiceDiscovery 系列包中的提供器在被调用的时候,如果已经有端点在集合中,就会跳过解析。例如,考虑一个场景,注册了如下的提供器:

  • Configuration
  • DNS SRV
  • Pass-through

当解析发生的时候,这些提供器按顺序被调用。如果 Configuration 提供器没有发现合适的端点,则 DNS SRV 进行解析,可能会增加一个或者多个端点,如果 DNS SRV 提供器增加了一个端点到集合中,那么 Pass-through 提供器就会跳过解析并立即返回。

默认实现

/// <summary>
/// Adds the core service discovery services and configures defaults.
/// </summary>
/// <param name="services">The service collection.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddServiceDiscovery(this IServiceCollection services)
{
return services.AddServiceDiscoveryCore()
.AddConfigurationServiceEndPointResolver()
.AddPassThroughServiceEndPointResolver();
}

见:https://github.com/dotnet/aspire/blob/main/src/Microsoft.Extensions.ServiceDiscovery/ServiceDiscoveryServiceCollectionExtensions.cs#L19

PassThroughServiceEndPointResolverProvider

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis;
using System.Net;
using Microsoft.Extensions.Logging; namespace Microsoft.Extensions.ServiceDiscovery.PassThrough; /// <summary>
/// Service endpoint resolver provider which passes through the provided value.
/// </summary>
internal sealed class PassThroughServiceEndPointResolverProvider(ILogger<PassThroughServiceEndPointResolver> logger) : IServiceEndPointProviderFactory
{
/// <inheritdoc/>
public bool TryCreateProvider(ServiceEndPointQuery query, [NotNullWhen(true)] out IServiceEndPointProvider? resolver)
{
var serviceName = query.OriginalString;
if (!TryCreateEndPoint(serviceName, out var endPoint))
{
// Propagate the value through regardless, leaving it to the caller to interpret it.
endPoint = new DnsEndPoint(serviceName, 0);
} resolver = new PassThroughServiceEndPointResolver(logger, serviceName, endPoint);
return true;
} private static bool TryCreateEndPoint(string serviceName, [NotNullWhen(true)] out EndPoint? serviceEndPoint)
{
if ((serviceName.Contains("://", StringComparison.Ordinal) || !Uri.TryCreate($"fakescheme://{serviceName}", default, out var uri)) && !Uri.TryCreate(serviceName, default, out uri))
{
serviceEndPoint = null;
return false;
} var uriHost = uri.Host;
var segmentSeparatorIndex = uriHost.IndexOf('.');
string host;
if (uriHost.StartsWith('_') && segmentSeparatorIndex > 1 && uriHost[^1] != '.')
{
// Skip the endpoint name, including its prefix ('_') and suffix ('.').
host = uriHost[(segmentSeparatorIndex + 1)..];
}
else
{
host = uriHost;
} var port = uri.Port > 0 ? uri.Port : 0;
if (IPAddress.TryParse(host, out var ip))
{
serviceEndPoint = new IPEndPoint(ip, port);
}
else if (!string.IsNullOrEmpty(host))
{
serviceEndPoint = new DnsEndPoint(host, port);
}
else
{
serviceEndPoint = null;
return false;
} return true;
}
}

见:https://github.com/dotnet/aspire/blob/81d1db1090c6adc05690fd9993e518d9742498fd/src/Microsoft.Extensions.ServiceDiscovery/PassThrough/PassThroughServiceEndPointResolverProvider.cs#L13

.NET 服务发现的更多相关文章

  1. Consul 服务注册与服务发现

    上一篇:Mac OS.Ubuntu 安装及使用 Consul 1. 服务注册 对 Consul 进行服务注册之前,需要先部署一个服务站点,我们可以使用 ASP.NET Core 创建 Web 应用程序 ...

  2. etcd:用于服务发现的键值存储系统

    etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现.etcd是由CoreOS开发并维护的,灵感来自于 ZooKeeper 和 Doozer,它使用Go语言编写,并通过Raft一致性算法处理 ...

  3. 我是服务的执政官-服务发现和注册工具consul简介

    服务发现和注册 我们有了两个服务.服务A的IP地址是192.168.0.1,端口9001,服务B的IP地址192.168.0.2,端口9002.我们的客户端需要调用服务A和服务B,我们只需要在配置文件 ...

  4. k8s DNS 服务发现的一个坑

    按照官当文档,以及大家的实践进行k8s dns 服务发现搭建还是比较简单的,但是会有一个因为系统默认dns 配置造成的一个问题 1. linux  默认dns 配置在 /etc/resolv.conf ...

  5. Kubernetes如何使用kube-dns实现服务发现

    大纲: •       Kubernetes中如何发现服务 •       如何发现Pod提供的服务 •       如何使用Service发现服务 •       如何使用kube-dns发现服务 ...

  6. Consul 服务发现和配置

    Service discovery and configuration made easy. Distributed, highly available, and datacenter-aware. ...

  7. 服务发现之 Etcd VS Consul

    抄自这里 *********************************************************************************************** ...

  8. SpringCloud+Consul 服务注册与服务发现

    SpringCloud+Consul 服务注册与服务发现 1. 服务注册: 在Spring.factories有一段: # Discovery Client Configuration org.spr ...

  9. 服务发现:Zookeeper vs etcd vs Consul

    [编者的话]本文对比了Zookeeper.etcd和Consul三种服务发现工具,探讨了最佳的服务发现解决方案,仅供参考. 如果使用预定义的端口,服务越多,发生冲突的可能性越大,毕竟,不可能有两个服务 ...

  10. 低功耗蓝牙4.0BLE编程-nrf51822开发(7)-SDP服务发现协议

    SDP的全称是Service Discovery Protocol,中文是服务发现协议.SDP(服务发现协议)是蓝牙协议体系中的核心协议,是蓝牙系统重要组成部分,是所有用户模式的基础.在蓝牙系统中.客 ...

随机推荐

  1. 如何关闭每次打开启动软件前的弹窗(用户账户控制)你要允许此应用.WIN11、10、7

    1.先点击任务栏内的搜索,输入"控制面板",然后点开 2.然后在右上角输入"更改用户",然后在下方点击"更改用户账户控制设置" 3.然后把& ...

  2. JVM 系列知识体系全面回顾

    经过几个月的努力,JVM 知识体系终于梳理完成了. 很早之前也和小伙伴们分享过 JVM 相关的技术知识,再次感谢大家支持和反馈. 最后再次献上 JVM系列文章合集索引,感兴趣的小伙伴可以点击查看. J ...

  3. Linux Kernel Utilization Clamping简介

    随着linux内核调度技术的不断演进,目前存在多个调度类(stop.deadline.rt.cfs.idle)以满足不同性质和要求的任务(task)的调度需求.对于用户空间来说,完全公平调度器(CFS ...

  4. 如何实现高效运维?来谈谈性能优化那些事(含直播回顾 Q&A)

    数据库性能问题,常常是困扰DBA高效运维的难题之一.如何多角度地帮助DBA,找到"数据库慢"的原因,保证系统高效.稳定.安全地运行? 2021年10月14日,云和恩墨技术顾问,拥有 ...

  5. 2021年12月墨天轮国产数据库排行榜: openGauss节节攀升拿下榜眼,GaussDB与TDSQL你争我夺各进一位

    2021年12月的国产数据库流行度排行榜已在墨天轮发布,本月共有189家数据库参与排名.为使国产数据库排名更加专业与客观,本月起,排行榜加入了三方评测.生态.专利数.论文数等新的指标.其中三方测评方面 ...

  6. iOS字符串大小写转换使用小结

    iOS开发中字符串用的比较多,追加,拆分,截取,替换,比较,大小写转换使用的频率还挺高.今天看oc技术书的时候看到关于大小写转换的地方,有一个说的是所有字母首字母大写,还是第一次看到,记录一下,以备后 ...

  7. iOS键盘通知弹框使用小结

    项目开发中文本框输入的时候经常会用到键盘弹框遮挡的问题.解决办法就是根据底部键盘弹出的高度动态的改变对应view的位置.这里以多行文本框输入为例,效果图如下. //第一步,注册监听键盘通知 [[NSN ...

  8. docker实战: vue+java+uniapp部署到阿里云服务器详解(此文没有使用宝塔面板部署),附带各种坑处理

    一.部署前准备工具以及注意事项: 项目来源:https://gitee.com/ZhongBangKeJi/crmeb_java?_from=gitee_search 官方部署文档:https://d ...

  9. 全局负载均衡、CDN内容分发的原理与实践

    CDN简介 CDN的全称是Content Delivery Network,即内容分发网络.CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡.内容分发. ...

  10. MD5文件的计算

    Windows下的命令 certutil -hashfile <文件名> <hash类型> 如: certutil -hashfile "C:\1.txt" ...