.NET服务发现(Microsoft.Extensions.ServiceDiscovery)集成Consul
随着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的更多相关文章
- 服务发现Eureka、zookeeper、consul
Spring Cloud为开发人员提供了工具,以快速构建分布式系统中的某些常见模式(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁,领导选举,分布式会话,群集状态). ...
- .net core grpc consul 实现服务注册 服务发现 负载均衡(二)
在上一篇 .net core grpc 实现通信(一) 中,我们实现的grpc通信在.net core中的可行性,但要在微服务中真正使用,还缺少 服务注册,服务发现及负载均衡等,本篇我们将在 .net ...
- .Net Core Grpc Consul 实现服务注册 服务发现 负载均衡
本文是基于..net core grpc consul 实现服务注册 服务发现 负载均衡(二)的,很多内容是直接复制过来的,..net core grpc consul 实现服务注册 服务发现 负载均 ...
- 服务发现 - consul 的介绍、部署和使用
什么是服务发现 相关源码: spring cloud demo 微服务的框架体系中,服务发现是不能不提的一个模块.我相信了解或者熟悉微服务的童鞋应该都知道它的重要性.这里我只是简单的提一下,毕竟这不是 ...
- 服务发现之consul的介绍、部署和使用
什么是服务发现 微服务的框架体系中,服务发现是不能不提的一个模块.我相信了解或者熟悉微服务的童鞋应该都知道它的重要性.这里我只是简单的提一下,毕竟这不是我们的重点.我们看下面的一幅图片: 图中 ...
- 服务发现 - consul 的介绍、部署和使用(转)
什么是服务发现 相关源码: spring cloud demo 微服务的框架体系中,服务发现是不能不提的一个模块.我相信了解或者熟悉微服务的童鞋应该都知道它的重要性.这里我只是简单的提一下,毕竟这不是 ...
- 基于consul构建golang系统分布式服务发现机制
原文地址-石匠的Blog: http://www.bugclosed.com/post/5 在分布式架构中,服务治理是一个重要的问题.在没有服务治理的分布式集群中,各个服务之间通过手工或者配置的方式进 ...
- go-micro使用Consul做服务发现的方法和原理
go-micro v4默认使用mdns做服务发现.不过也支持采用其它的服务发现中间件,因为多年来一直使用Consul做服务发现,为了方便和其它服务集成,所以还是选择了Consul.这篇文章将介绍go- ...
- .NetCore Cap 注册 Consul 服务发现
注册服务发现 需要使用Cap中的UseDiscovery方法 具体用法如下 var capConsulConfig = Configuration.GetSection("CapConsul ...
- 基于Docker的Consul集群实现服务发现
服务发现 其实简单说,服务发现就是解耦服务与IP地址之间的硬绑定关系,以典型的集群为例,对于集群来说,是有多个节点的,这些节点对应多个IP(或者同一个IP的不同端口号),集群中不同节点责任是不一样的. ...
随机推荐
- [Rocky Linux] 使用btrfs
使用btrfs rocky本身并没有btrfs的相关管理工具,所以需要自己安装,但是遗憾的发现它的源中啥也没有.只能考虑自己安装. 相关说明 btrfs Wiki (kernel.org) 可以从中得 ...
- Java 常见的两个错误 -------1.栈溢出 java.lang.StackOverflowError 2.堆溢出 java.lang.OutOfMemoryError /OOM
1 package com.bytezero.exception; 2 3 /** 4 * 5 * @Description Error 6 * @author Bytezero·zhenglei! ...
- Java 客户信息管理软件 (面向对象 封装 总结)
1 package com.bytezero.cim.bean; 2 3 /** 4 * 5 * @Description Customer为实体对象,用来封装客户信息 6 * @author Byt ...
- CPNtools协议建模安全分析---实例(三)
对于复杂的系统的建模或者协议的建模,各种颜色集的定义以及变量的声明很重要,要区分明确,对于函数行业进程的定义更加复杂.CPN对协议的描述只适合简单逻辑性的协议分析,如果协议包括复杂的算法,那么CPN就 ...
- Oracle中表字段有使用Oracle关键字的一定要趁早改!!!
一.问题由来 现在进行项目改造,数据库需要迁移,由原来的使用GBase数据库改为使用Oracle数据库,今天测试人员在测试时后台报了一个异常. 把SQL语句单独复制出来进行查询,还是报错,仔细分析原因 ...
- c语言运算符优先级实例解析
壹: 对于优先级:算术运算符 > 关系运算符 > 逻辑运算符 > 赋值运算符.逻辑运算符中"逻辑非 !"除外.这是程序员总结出来的最快的学习方式. 可在实战 ...
- Global AI Bootcamp 成都站 圆满结束!
3月10日星期天下午2点「Global AI Bootcamp 2024 - 成都站」,在成都银泰中心蔚来汽车会议区圆满结束了! 本次活动共计吸引了约50名IT行业从业者线下参与,他们分别来自成都各行 ...
- autowired循环调用
下面这段代码,表示那你的bean进行了循环调用. org.springframework.beans.factory.BeanCreationException: Error creating bea ...
- 使用TS封装操作MongoDB数据库的工具方法
使用TS封装操作MongoDB数据库的工具方法 前言 在做毕业设计过程中采用了MongoDb存储应用的日志信息,总结了一些CRUD方法与大家分享一下,最终使用效果可跳转到业务调用示例这一小节查看 关于 ...
- 记录--Uni-app接入腾讯人脸核身
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 人脸核身功能有多种接入方式,其中包含微信H5.微信小程序.APP.独立H5.PC端.API接入6种方式. 我们的产品是使用uni-ap ...