.NET服务发现(Microsoft.Extensions.ServiceDiscovery)集成Consul

本文介绍了Microsoft.Extensions.ServiceDiscovery在preview5版本中的应用,重点讲解了如何使用默认提供者以及如何扩展Consul服务发现,包括创建自定义Provider和集成到应用程序中,以实现服务的动态发现和负载均衡。
摘要由CSDN通过智能技术生成

随着Aspire发布preview5的发布,Microsoft.Extensions.ServiceDiscovery随之更新,

服务注册发现这个属于老掉牙的话题解决什么问题就不赘述了,这里主要讲讲Microsoft.Extensions.ServiceDiscovery(preview5)以及如何扩展其他的中间件的发现集成 .

Microsoft.Extensions.ServiceDiscovery官方默认提供的Config,DNS,YARP三种Provider,使用也比较简单 :

builder.Services.AddServiceDiscovery();

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

builder.Services.ConfigureHttpClientDefaults(static http =>
{
    // 全局对HttpClient启用服务发现
    http.UseServiceDiscovery();
});

然后 appsettings.json 为名为 todo 的服务配置终结点:

"Services": {
    "todo": {
      "http": [
        "http://localhost:5124"
      ]
    }
  }

然后使用服务发现:

#region 模拟服务端的todo接口: 
var sampleTodos = new Todo[] {
    new(1, "Walk the dog"),
    new(2, "Do the dishes", DateOnly.FromDateTime(DateTime.Now)),
    new(3, "Do the laundry", DateOnly.FromDateTime(DateTime.Now.AddDays(1))),
    new(4, "Clean the bathroom"),
    new(5, "Clean the car", DateOnly.FromDateTime(DateTime.Now.AddDays(2)))
};

var todosApi = app.MapGroup("/todos");
todosApi.MapGet("/", () => sampleTodos);
todosApi.MapGet("/{id}", (int id) =>
    sampleTodos.FirstOrDefault(a => a.Id == id) is { } todo
        ? Results.Ok(todo)
        : Results.NotFound());
#endregion

public record Todo(int Id, string? Title, DateOnly? DueBy = null, bool IsComplete = false);

[JsonSerializable(typeof(Todo[]))]
internal partial class AppJsonSerializerContext : JsonSerializerContext
{
}

#region 测试服务发现和负载

app.MapGet("/test", async (IHttpClientFactory clientFactory) =>
{
    //这里服务发现将自动解析配置文件中的服务
    var client = clientFactory.CreateClient("todo");
    var response = await client.GetAsync("/todos");
    var todos = await response.Content.ReadAsStringAsync();
    return Results.Content(todos, contentType: "application/json");
});

#endregion

运行程序后将会发现成功执行:
71c17636dcdce4533b7a216edbedb401.png

当然对于这样写死配置的服务发现一点都不灵活,因此应运而生了 YARP和DNS这些Provider, 目前服务注册发现使用Consul的还是挺多的,当然还有很多其他的轮子就不赘述了,这里我们来扩展一个Consul的服务发现Provider :

实现核心接口IServiceEndPointProvider

internal class ConsulServiceEndPointProvider(ServiceEndPointQuery query, IConsulClient consulClient, ILogger logger)
        : IServiceEndPointProvider, IHostNameFeature
    {
        const string Name = "Consul";
        private readonly string _serviceName = query.ServiceName;
        private readonly IConsulClient _consulClient = consulClient;
        private readonly ILogger _logger = logger;

        public string HostName => query.ServiceName;

#pragma warning disable CA1816 // Dispose 方法应调用 SuppressFinalize
        public ValueTask DisposeAsync() => default;

        public async ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationToken cancellationToken)
        {
            var flag = ServiceNameParts.TryParse(_serviceName, out var serviceNameParts);
            var sum = 0;
            if (flag)
            {
                var queryResult = await _consulClient.Health.Service(serviceNameParts.Host, string.Empty, true, cancellationToken);
                foreach (var serviceEntry in queryResult.Response)
                {
                    var address = $"{serviceEntry.Service.Address}:{serviceEntry.Service.Port}";
                    var isEndpoint = ServiceNameParts.TryCreateEndPoint(address, out var endPoint);
                    if (isEndpoint)
                    {
                        ++sum;
                        var serviceEndPoint = ServiceEndPoint.Create(endPoint!);
                        serviceEndPoint.Features.Set<IServiceEndPointProvider>(this);
                        serviceEndPoint.Features.Set<IHostNameFeature>(this);
                        endPoints.EndPoints.Add(serviceEndPoint);
                        _logger.LogInformation($"ConsulServiceEndPointProvider Found Service {_serviceName}:{address}");
                    }
                }
            }

            if (sum == 0)
            {
                _logger.LogWarning($"No ConsulServiceEndPointProvider were found for service '{_serviceName}' ('{HostName}').");
            }
        }

        /// <inheritdoc/>
        public override string ToString() => Name;
    }

实现 IServiceEndPointProviderFactory:

internal class ConsulServiceEndPointProviderFactory(IConsulClient consulClient, ILogger<ConsulServiceEndPointProviderFactory> logger) : IServiceEndPointProviderFactory
    {
        private readonly IConsulClient _consulClient = consulClient;
        private readonly ILogger<ConsulServiceEndPointProviderFactory> _logger = logger;

        public bool TryCreateProvider(ServiceEndPointQuery query, [NotNullWhen(true)] out IServiceEndPointProvider? resolver)
        {
            resolver = new ConsulServiceEndPointProvider(query, _consulClient, _logger);
            return true;
        }
    }

接着扩展一下IServiceCollection

public static IServiceCollection AddConsulServiceEndpointProvider(this IServiceCollection services)
{
	services.AddServiceDiscoveryCore();
	services.AddSingleton<IServiceEndPointProviderFactory, ConsulServiceEndPointProviderFactory>();
	return services;
}

最后添加一行代码 :

// 使用Microsoft.Extensions.ServiceDiscovery实现负载均衡
builder.Services.AddServiceDiscovery()
    .AddConfigurationServiceEndPointResolver() //config
    .AddConsulServiceEndpointProvider(); //consul

下面是Consul中注册完成的服务:
fe1c0c5ff10ac56ff4afd71dc7d3e02b.png

然后我们请求 ./test 调用服务,观察调试日志,成功了!

86b84f7494fb4e5f830b2d15a5f47754.png

完整的代码:
https://github.com/vipwan/Biwen.Microsoft.Extensions.ServiceDiscovery.Consul

当然你也可以直接使用nuget引用 Biwen.Microsoft.Extensions.ServiceDiscovery.Consul 我已经发布到了nuget上 , 最后因为Aspire还在不停的迭代所以Biwen.Microsoft.Extensions.ServiceDiscovery.Consul后面还会存在一些变化, 前面的几个早期版本我都做了适配以最新的为准

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,针对你的问题,我可以给出以下回答: 在 .NET Framework 4.7.2 中,可以使用 Microsoft.Extensions.Configuration 来管理应用程序的配置信息。这个框架提供了一些类,可以从多种不同的配置源(如 JSON、XML、命令行参数等)中读取配置信息,并将其统一封装成一个 IConfiguration 对象,供应用程序使用。 下面是一个简单的示例,演示如何使用 Microsoft.Extensions.Configuration 读取一个名为 appsettings.json 的配置文件: 1. 首先,在项目中添加 Microsoft.Extensions.Configuration 和 Microsoft.Extensions.Configuration.Json 包。 2. 在 appsettings.json 文件中定义需要读取的配置项,例如: ``` { "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } } } ``` 3. 在代码中读取配置项,例如: ``` using System.Configuration; using Microsoft.Extensions.Configuration; ... var builder = new ConfigurationBuilder() .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); IConfigurationRoot configuration = builder.Build(); var connectionString = configuration.GetConnectionString("DefaultConnection"); var logLevel = configuration["Logging:LogLevel:Default"]; ``` 这里的代码首先创建了一个 ConfigurationBuilder 对象,并通过 AddJsonFile 方法指定要读取的配置文件的路径和名称。然后,调用 Build 方法创建了一个 IConfigurationRoot 对象,该对象表示了整个配置文件的内容。最后,可以通过 GetConnectionString 和索引器等方法获取需要的配置项。 希望这个回答对你有所帮助。如果你还有其他问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值