经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
.NET服务发现(Microsoft.Extensions.ServiceDiscovery)集成Consul
来源:cnblogs  作者:万雅虎  时间:2024/4/15 9:50:30  对本文有异议

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

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

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

  1. builder.Services.AddServiceDiscovery();
  2. builder.Services.AddHttpClient<CatalogServiceClient>(static client =>
  3. {
  4. client.BaseAddress = new("http://todo");
  5. });
  6. builder.Services.ConfigureHttpClientDefaults(static http =>
  7. {
  8. // 全局对HttpClient启用服务发现
  9. http.UseServiceDiscovery();
  10. });

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

  1. "Services": {
  2. "todo": {
  3. "http": [
  4. "http://localhost:5124"
  5. ]
  6. }
  7. }

然后使用服务发现:

  1. #region 模拟服务端的todo接口:
  2. var sampleTodos = new Todo[] {
  3. new(1, "Walk the dog"),
  4. new(2, "Do the dishes", DateOnly.FromDateTime(DateTime.Now)),
  5. new(3, "Do the laundry", DateOnly.FromDateTime(DateTime.Now.AddDays(1))),
  6. new(4, "Clean the bathroom"),
  7. new(5, "Clean the car", DateOnly.FromDateTime(DateTime.Now.AddDays(2)))
  8. };
  9. var todosApi = app.MapGroup("/todos");
  10. todosApi.MapGet("/", () => sampleTodos);
  11. todosApi.MapGet("/{id}", (int id) =>
  12. sampleTodos.FirstOrDefault(a => a.Id == id) is { } todo
  13. ? Results.Ok(todo)
  14. : Results.NotFound());
  15. #endregion
  16. public record Todo(int Id, string? Title, DateOnly? DueBy = null, bool IsComplete = false);
  17. [JsonSerializable(typeof(Todo[]))]
  18. internal partial class AppJsonSerializerContext : JsonSerializerContext
  19. {
  20. }
  21. #region 测试服务发现和负载
  22. app.MapGet("/test", async (IHttpClientFactory clientFactory) =>
  23. {
  24. //这里服务发现将自动解析配置文件中的服务
  25. var client = clientFactory.CreateClient("todo");
  26. var response = await client.GetAsync("/todos");
  27. var todos = await response.Content.ReadAsStringAsync();
  28. return Results.Content(todos, contentType: "application/json");
  29. });
  30. #endregion

运行程序后将会发现成功执行:
image

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

实现核心接口IServiceEndPointProvider

  1. internal class ConsulServiceEndPointProvider(ServiceEndPointQuery query, IConsulClient consulClient, ILogger logger)
  2. : IServiceEndPointProvider, IHostNameFeature
  3. {
  4. const string Name = "Consul";
  5. private readonly string _serviceName = query.ServiceName;
  6. private readonly IConsulClient _consulClient = consulClient;
  7. private readonly ILogger _logger = logger;
  8. public string HostName => query.ServiceName;
  9. #pragma warning disable CA1816 // Dispose 方法应调用 SuppressFinalize
  10. public ValueTask DisposeAsync() => default;
  11. public async ValueTask PopulateAsync(IServiceEndPointBuilder endPoints, CancellationToken cancellationToken)
  12. {
  13. var flag = ServiceNameParts.TryParse(_serviceName, out var serviceNameParts);
  14. var sum = 0;
  15. if (flag)
  16. {
  17. var queryResult = await _consulClient.Health.Service(serviceNameParts.Host, string.Empty, true, cancellationToken);
  18. foreach (var serviceEntry in queryResult.Response)
  19. {
  20. var address = $"{serviceEntry.Service.Address}:{serviceEntry.Service.Port}";
  21. var isEndpoint = ServiceNameParts.TryCreateEndPoint(address, out var endPoint);
  22. if (isEndpoint)
  23. {
  24. ++sum;
  25. var serviceEndPoint = ServiceEndPoint.Create(endPoint!);
  26. serviceEndPoint.Features.Set<IServiceEndPointProvider>(this);
  27. serviceEndPoint.Features.Set<IHostNameFeature>(this);
  28. endPoints.EndPoints.Add(serviceEndPoint);
  29. _logger.LogInformation($"ConsulServiceEndPointProvider Found Service {_serviceName}:{address}");
  30. }
  31. }
  32. }
  33. if (sum == 0)
  34. {
  35. _logger.LogWarning($"No ConsulServiceEndPointProvider were found for service '{_serviceName}' ('{HostName}').");
  36. }
  37. }
  38. /// <inheritdoc/>
  39. public override string ToString() => Name;
  40. }

实现 IServiceEndPointProviderFactory:

  1. internal class ConsulServiceEndPointProviderFactory(IConsulClient consulClient, ILogger<ConsulServiceEndPointProviderFactory> logger) : IServiceEndPointProviderFactory
  2. {
  3. private readonly IConsulClient _consulClient = consulClient;
  4. private readonly ILogger<ConsulServiceEndPointProviderFactory> _logger = logger;
  5. public bool TryCreateProvider(ServiceEndPointQuery query, [NotNullWhen(true)] out IServiceEndPointProvider? resolver)
  6. {
  7. resolver = new ConsulServiceEndPointProvider(query, _consulClient, _logger);
  8. return true;
  9. }
  10. }

接着扩展一下IServiceCollection

  1. public static IServiceCollection AddConsulServiceEndpointProvider(this IServiceCollection services)
  2. {
  3. services.AddServiceDiscoveryCore();
  4. services.AddSingleton<IServiceEndPointProviderFactory, ConsulServiceEndPointProviderFactory>();
  5. return services;
  6. }

最后添加一行代码 :

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

下面是Consul中注册完成的服务:
image

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

image

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

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

原文链接:https://www.cnblogs.com/vipwan/p/18129361

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号