随着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

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

当然对于这样写死配置的服务发现一点都不灵活,因此应运而生了 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中注册完成的服务:

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

完整的代码:

https://github.com/vipwan/Biwen.Microsoft.Extensions.ServiceDiscovery.Consul

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

.NET服务发现(Microsoft.Extensions.ServiceDiscovery)集成Consul的更多相关文章

  1. 服务发现Eureka、zookeeper、consul

    Spring Cloud为开发人员提供了工具,以快速构建分布式系统中的某些常见模式(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁,领导选举,分布式会话,群集状态). ...

  2. .net core grpc consul 实现服务注册 服务发现 负载均衡(二)

    在上一篇 .net core grpc 实现通信(一) 中,我们实现的grpc通信在.net core中的可行性,但要在微服务中真正使用,还缺少 服务注册,服务发现及负载均衡等,本篇我们将在 .net ...

  3. .Net Core Grpc Consul 实现服务注册 服务发现 负载均衡

    本文是基于..net core grpc consul 实现服务注册 服务发现 负载均衡(二)的,很多内容是直接复制过来的,..net core grpc consul 实现服务注册 服务发现 负载均 ...

  4. 服务发现 - consul 的介绍、部署和使用

    什么是服务发现 相关源码: spring cloud demo 微服务的框架体系中,服务发现是不能不提的一个模块.我相信了解或者熟悉微服务的童鞋应该都知道它的重要性.这里我只是简单的提一下,毕竟这不是 ...

  5. 服务发现之consul的介绍、部署和使用

    什么是服务发现 微服务的框架体系中,服务发现是不能不提的一个模块.我相信了解或者熟悉微服务的童鞋应该都知道它的重要性.这里我只是简单的提一下,毕竟这不是我们的重点.我们看下面的一幅图片:     图中 ...

  6. 服务发现 - consul 的介绍、部署和使用(转)

    什么是服务发现 相关源码: spring cloud demo 微服务的框架体系中,服务发现是不能不提的一个模块.我相信了解或者熟悉微服务的童鞋应该都知道它的重要性.这里我只是简单的提一下,毕竟这不是 ...

  7. 基于consul构建golang系统分布式服务发现机制

    原文地址-石匠的Blog: http://www.bugclosed.com/post/5 在分布式架构中,服务治理是一个重要的问题.在没有服务治理的分布式集群中,各个服务之间通过手工或者配置的方式进 ...

  8. go-micro使用Consul做服务发现的方法和原理

    go-micro v4默认使用mdns做服务发现.不过也支持采用其它的服务发现中间件,因为多年来一直使用Consul做服务发现,为了方便和其它服务集成,所以还是选择了Consul.这篇文章将介绍go- ...

  9. .NetCore Cap 注册 Consul 服务发现

    注册服务发现 需要使用Cap中的UseDiscovery方法 具体用法如下 var capConsulConfig = Configuration.GetSection("CapConsul ...

  10. 基于Docker的Consul集群实现服务发现

    服务发现 其实简单说,服务发现就是解耦服务与IP地址之间的硬绑定关系,以典型的集群为例,对于集群来说,是有多个节点的,这些节点对应多个IP(或者同一个IP的不同端口号),集群中不同节点责任是不一样的. ...

随机推荐

  1. 基于 BDD 理论的 Nebula 集成测试框架重构(下篇)

    本文首发于 Nebula Graph 公众号 NebulaGraphCommunity,Follow 看大厂图数据库技术实践. 在上篇文章中,我们介绍了 Nebula Graph 的集成测试的演进过程 ...

  2. ElasticSearch基础介绍(1)

    ## 1. Elasticsearch基本介绍 官网:https://www.elastic.co/cn Elasticsearch(简称ES)是一个基于Apache Lucene(TM)的开源搜索引 ...

  3. rust简要笔记

    第一个程序, 我们不用安装编辑器,直接在现成的网页编辑器上运行  https://play.rust-lang.org/

  4. git的 .gitignore 配置概述

    git的 .gitignore 配置概述 学习背景:自己在使用git时发现有时会上传很多无用的配置文件,或者在项目中已经包含一个本地的git仓库,导致上一级项目上传总是报错,所以学习采用gitigno ...

  5. Dashboard、Rancher与KubeSphere对比

    在容器技术和微服务架构日益盛行的今天,对于容器编排和管理平台的选择显得尤为重要.Kubernetes(K8s)作为容器编排的事实标准,其生态系统中涌现出了许多管理和监控工具.其中,Dashboard. ...

  6. 一次对requirements环境的配置

    事情是这样的,我需要跑通一个代码,因此要配置环境,但是并不能利用requirements中给的指令直接配置,于是开始找一些其他的解决方法.作为一名小白,总是绕很多弯路. 记下一些蜿蜒. 首先,摘录re ...

  7. 解决js缓存地址问题

    解决js缓存地址问题 js实现不缓存 <META HTTP-EQUIV="pragma" CONTENT="no-cache"> <META ...

  8. AOSP编译成功后关闭终端emulator命令找不到

    当我们编译好AOSP系统源码后,可以通过emulator命令打开模拟器,但是当我们关闭终端后,在次打开终端输入emulator命令,提示未找到命令: 此时我们需要重新执行下面语句 source bui ...

  9. SQLI-LABS(Less-11、12)

    Less-11(POST-Error Based-Single Quotes-String) 打开Less-11的页面,可以看到一个登录框,需要输入用户名和密码,由本关名字可知SQL语句是单引号闭合. ...

  10. 基于PyQGIS实现遥感影像下载

    1. 引言 之前的文章:QGIS中下载遥感影像的Python代码片段 - 当时明月在曾照彩云归 - 博客园 (cnblogs.com),记述了在 QGIS 的 Python Console 中使用Py ...