.net5+nacos+ocelot 配置中心和服务发现实现
最近一段时间 因公司业务需要,需要使用.net5做一套微服务的接口,使用nacos 做注册中心和配置中心,ocelot做网关。
因为ocelot 支持的是consol和eureka,如果使用nacos做服务发现,需要自己集成,以此记录下
Nacos 支持基于 DNS 和基于 RPC 的服务发现(可以作为注册中心)、动态配置服务(可以做配置中心)、动态 DNS 服务。官网地址:https://nacos.io/en-us/
ocelot 相信大家都比较熟悉,官网:https://ocelot.readthedocs.io/en/latest/index.html
环境安装:
nacos参考地址:https://blog.csdn.net/ooyhao/article/details/102744904
基于.net版本的nacos sdk:nacos-sdk-csharp-unofficial.AspNetCore (此处有坑 :Nacos.AspNetCore 已经停止更新,代码已迁移,服务注册会有问题)
SDK源码地址:https://github.com/catcherwong/nacos-sdk-csharp
配置中心:
1.在nacos添加配置

2.在.net 项目中 配置文件中 添加相关配置
1 "nacos": {
2 "ServerAddresses": [ "http://127.0.0.1:8849/" ],
3 "DefaultTimeOut": 30000,
4 "Namespace": "",
5 "ListenInterval": 30000,
6 "ServiceName": "ServiceName",
"RegisterEnabled": true,
7 "Weight": 10
8 },
9 "nacosData": {
10 "DataId": "nacosConfig",
11 "Group": "Pro"
12 }
3.在Startup.cs添加nacos sdk的DI注册
1 services.AddNacos(Configuration);
4.创建AppConfig类,定义构造函数:(从DI中获取INacosConfigClient对象)
public AppConfig(IServiceCollection _services, IConfiguration _configuration)
{
services = _services; configuration = _configuration;
var serviceProvider = services.BuildServiceProvider();
_configClient = serviceProvider.GetService<INacosConfigClient>();
}
5.添加LoadConfig方法,加载配置中心的配置
代码说明:sectionKey入参 为配置文件中的key(nacosData),responseJson为配置中心的完整配置字符串,可以是json,可以是key=value模式,根据字符串格式,转为Dictionary中,放入静态私有对象中
/// <summary>
/// 加载nacos配置中心
/// </summary>
/// <param name="sectionKey"></param>
private async Task LoadConfig(string sectionKey)
{
try
{
GetConfigRequest configRequest = configuration.GetSection(sectionKey).Get<GetConfigRequest>();
if (configRequest == null || string.IsNullOrEmpty(configRequest.DataId))
{
return;
}
var responseJson = await _configClient.GetConfigAsync(configRequest);
Console.WriteLine(responseJson);
if (string.IsNullOrEmpty(responseJson))
{
return;
}
var dic = LoadDictionary(responseJson);
if (sectionKey == commonNacosKey)
{
commonConfig = dic;
}
else
{
dicConfig = dic;
} }
catch (Exception ex)
{
throw ex;
}
}
6.添加监听:
ps:
AddListenerRequest对象为nacos sdk的监听请求对象,通过INacosConfigClient对象的AddListenerAsync方法,可以添加对nacos dataid=nacosConfig 的监听,监听的时间间隔设置可以为:ListenInterval
1 /// <summary>
2 /// 监控
3 /// </summary>
4 private void ListenerConfig()
5 {
6 AddListenerRequest request = configuration.GetSection("nacosData").Get<AddListenerRequest>();
7 request.Callbacks = new List<Action<string>>() {
8 x=>{
9 var dic = LoadDictionary(x);
10 foreach (var item in dicConfig.Keys)
11 {
12 if (dic.Keys.Any(p=>p==item))
13 {
14 if (dic[item] != dicConfig[item])
15 {
16 dicConfig[item]=dic[item].Trim();
17 }
18 }else
19 {
20 dicConfig.Remove(item);
21 }
22 }
23 foreach (var item in dic.Keys)
24 {
25 if (!dicConfig.Keys.Any(p=>p==item)){
26 dicConfig.Add(item,dic[item]);
27 }
28 }
29 }
30 };
31 var serviceProvider = services.BuildServiceProvider();
32 INacosConfigClient _configClient = serviceProvider.GetService<INacosConfigClient>();
33 _configClient.AddListenerAsync(request);
34 }
7.添加自动注入:
ps:如果需要在同一个项目中,获取多个配置,需要AppConfig对象的单列模式,这一点 自己注意下
public static IServiceCollection AddLoadConfig(this IServiceCollection _services, IConfiguration _configuration)
{
var config = new AppConfig(_services, _configuration);
config.Load();
return _services;
}
/******************************************************* 配置中心 end *************************************************************/
服务注册:
基于上面的配置信息 在自动注入中添加如下代码即可
services.AddNacosAspNetCore(conf);
启动之后 在nacos中,就可以看到此服务 如果在nacos 看到多个实列 或者 端口号和项目启动的端口号不一致,最好添加IP和port。
/***************************************************************服务注册 end***********************************************************************/
基于ocelot 的服务发现:
新建网关项目,添加ocelot和 nacos sdk 的nuget依赖
查看ocelot的官方文档就会发现,如果 能够取到nacos 中 服务的名称和服务里边可用实列的ip和port,然后把这些信息放入ocelot里边的, 就可以通过ocelot访问到这些服务接口
然后在通过自定义监听器,就可以实现想要的效果
通过上面的配置中心的配置方式,在nacos中 添加 ocelot 的模板配置
{
"Routes": [{
"DownstreamHostAndPorts": [{
"Host": "localhost",
"Port": 5000
}],
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "http",
"UpstreamHttpMethod": ["GET", "POST", "DELETE", "PUT"],
"UpstreamPathTemplate": "/OrderServer/{url}",
"LoadBalancerOptions": {
"Type": "RoundRobin"
}
}, {
"DownstreamHostAndPorts": [{
"Host": "localhost",
"Port": 5000
}],
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "http",
"UpstreamHttpMethod": ["GET", "POST", "DELETE", "PUT"],
"UpstreamPathTemplate": "/ProductServer/{url}"
}
],
"ServiceDiscoveryProvider": {
},
"GlobalConfiguration": {}
}
关于ocelot的Startup.cs的相关配置 这里就不赘述了,网上有很多。
这里的关键是,从nacos中拉取服务列表,然后根据ocelot的配置模板,生成需要的ocelot的配置信息,然后放入ocelot中
获取nacos中所有的服务列表
ps:通过INacosNamingClient对象的ListServicesAsync方法,获取nacos 的服务
/// <summary>
/// 获取所有服务
/// </summary>
/// <param name="serviceProvider"></param>
/// <param name="_servicesRequest"></param>
/// <returns></returns>
private async Task<List<ListInstancesResult>> GetServerListener(IServiceProvider serviceProvider, object _servicesRequest)
{ ListServicesRequest request = (ListServicesRequest)_servicesRequest;
try
{
var _namingClient = serviceProvider.GetService<INacosNamingClient>(); var res = await _namingClient.ListServicesAsync(request); List<ListInstancesResult> resultList = new List<ListInstancesResult>();
if (res.Count > 0)
{
List<Task<ListInstancesResult >> taskList = new List<Task<ListInstancesResult>>();
foreach (var item in res.Doms)
{
var taskItem = GetListInstancesResult(_namingClient,item);
taskList.Add(taskItem);
}
Task.WaitAll(taskList.ToArray());
foreach (var item in taskList)
{
resultList.Add(item.Result);
}
}
return resultList;
}
catch (Exception ex)
{ LoggerLocal.Error(ex.Message, ex);
return new List<ListInstancesResult>();
}
}
将nacos的服务和配置中心的ocelot模板转换为ocelot的配置对象

/// <summary>
/// nacos中的服务 转为ocelot对象的路由
/// </summary>
/// <param name="fileConfiguration"></param>
/// <param name="listInstancesResults"></param>
/// <returns></returns>
private FileConfiguration InstancesResultToFileConfiguration(FileConfigurationTemplate fileConfiguration, List<ListInstancesResult> listInstancesResults) { if (fileConfiguration.RouteTemplate == null || fileConfiguration.RouteTemplate.Count == 0)
{
throw new Exception("路由不能为空");
}
var result = new FileConfiguration() {
GlobalConfiguration = fileConfiguration.GlobalConfiguration,
Aggregates = fileConfiguration.Aggregates,
DynamicRoutes = fileConfiguration.DynamicRoutes,
Routes = new List<FileRoute>()
};
nacosServerModelList.ServerInfo = new List<ServerInfo>();
var routeList = fileConfiguration.RouteTemplate;
var defaultRoute = fileConfiguration.RouteTemplate.Find(p=>p.RoutesTemplateName=="common");
fileConfiguration.Routes = new List<FileRoute>();
foreach (var item in listInstancesResults)
{
var routeTemp = routeList.Find(p => p.ServiceName.ToLower() == item.Dom.ToLower());
if (routeTemp == null)
{
routeTemp = defaultRoute;
}
var newRouteTmp = CopyTo(routeTemp);
newRouteTmp.UpstreamPathTemplate = "/" + item.Dom + "/{url}";
newRouteTmp.DownstreamPathTemplate = "/{url}";
newRouteTmp.DownstreamHostAndPorts = new List<FileHostAndPort>();
if (item.Hosts.Count > 0)
{
foreach (var host in item.Hosts)
{
newRouteTmp.DownstreamHostAndPorts.Add(new FileHostAndPort()
{
Host = host.Ip,
Port = host.Port,
});
}
}
if (newRouteTmp.DownstreamHostAndPorts.Count > 0)
{
result.Routes.Add(newRouteTmp);
nacosServerModelList.ServerInfo.Add(new ServerInfo() { Name = item.Dom });
}
} UpdSwaggerUrlAction(serviceProvider, nacosServerModelList);
return result;
} private FileRoute CopyTo(RouteTemplate s)
{
var result = new FileRoute() {
AddClaimsToRequest=s.AddClaimsToRequest,
DangerousAcceptAnyServerCertificateValidator=s.DangerousAcceptAnyServerCertificateValidator,
DelegatingHandlers=s.DelegatingHandlers,
DownstreamHeaderTransform=s.DownstreamHeaderTransform,
DownstreamHostAndPorts=s.DownstreamHostAndPorts,
DownstreamHttpMethod=s.DownstreamHttpMethod,
DownstreamHttpVersion=s.DownstreamHttpVersion,
DownstreamPathTemplate=s.DownstreamPathTemplate,
SecurityOptions=s.SecurityOptions,
DownstreamScheme=s.DownstreamScheme,
ChangeDownstreamPathTemplate=s.ChangeDownstreamPathTemplate,
AddHeadersToRequest=s.AddHeadersToRequest,
AddQueriesToRequest=s.AddQueriesToRequest,
AuthenticationOptions=s.AuthenticationOptions,
FileCacheOptions=s.FileCacheOptions,
HttpHandlerOptions=s.HttpHandlerOptions,
Key=s.Key,
LoadBalancerOptions=s.LoadBalancerOptions,
Priority=s.Priority,
QoSOptions=s.QoSOptions,
RateLimitOptions=s.RateLimitOptions,
RequestIdKey=s.RequestIdKey,
RouteClaimsRequirement=s.RouteClaimsRequirement,
RouteIsCaseSensitive=s.RouteIsCaseSensitive,
ServiceName=s.ServiceName,
ServiceNamespace=s.ServiceNamespace,
Timeout=s.Timeout,
UpstreamHeaderTransform=s.UpstreamHeaderTransform,
UpstreamHost=s.UpstreamHost,
UpstreamHttpMethod=s.UpstreamHttpMethod,
UpstreamPathTemplate=s.UpstreamPathTemplate, };
return result;
}
将配置信息放入ocelot里边
ps:这个地方 需要看ocelot的源码,才知道这中间的对象转换逻辑
1 private void SetOcelotConfig(FileConfiguration configuration)
2 {
3
4 var internalConfigCreator = serviceProvider.GetService<IInternalConfigurationCreator>();
5 Task<Response<IInternalConfiguration>> taskResponse = internalConfigCreator.Create(configuration);
6 taskResponse.Wait();
7 IInternalConfigurationRepository internalConfigurationRepository = serviceProvider.GetService<IInternalConfigurationRepository>();
8 internalConfigurationRepository.AddOrReplace(taskResponse.Result.Data);
9 }
自定义监听器:
ps:isLoadUri 防止处理过慢,监听服务 多次监听
routesMd5:判断监听到的服务 是否需要放入ocelot
自定义监听的方式与nacos sdk中,监听配置中心的方式类似,有兴趣可以看看sdk的源码
/// <summary>
/// 获取nacos里边的所有服务信息,同时自定义服务监听
/// </summary>
/// <param name="serviceProvider"></param>
/// <returns></returns>
private async Task<List<ListInstancesResult>> GetServerList(IServiceProvider serviceProvider)
{
var request = new ListServicesRequest
{
PageNo = 1,
PageSize = 100,
};
List<ListInstancesResult> listInstancesResults = await GetServerListener(serviceProvider, request);
//return listInstancesResults;
var timeSeconds = 1000 * 10;
Timer timer = new Timer(async x =>
{
//防止重复Timer
if (isLoadUri)
{
return;
}
isLoadUri = true;
List<ListInstancesResult> listInstancesList = await GetServerListener(serviceProvider, x);
GetConfigRequest configRequest = configuration.GetSection("nacosData").Get<GetConfigRequest>(); INacosConfigClient _configClient = serviceProvider.GetService<INacosConfigClient>();
Task<string> taskResult = _configClient.GetConfigAsync(configRequest);
taskResult.Wait();
var responseJson = taskResult.Result;
if (listInstancesList.Count>0)
{
var fileConfiguration = InstancesResultToFileConfiguration(JsonConvert.DeserializeObject<FileConfigurationTemplate>(responseJson), listInstancesList);
responseJson = JsonConvert.SerializeObject(fileConfiguration);
var rMd5 = HashUtil.GetMd5(responseJson);
if (!rMd5.Equals(routesMd5))
{
SetOcelotConfig(fileConfiguration);
routesMd5 = rMd5;
}
}
isLoadUri = false;
}, request, timeSeconds, timeSeconds);
timers.Add(timer);
return listInstancesResults;
}
.net5+nacos+ocelot 配置中心和服务发现实现的更多相关文章
- Nacos配置中心和服务的注册发现
在上一篇中,我们已经把Nacos的集群搭建好了,那么既然已经搭建好了,就要在咱们的项目中去使用.Nacos既可以做配置中心,也可以做注册中心.我们先来看看在项目中如何使用Nacos做配置中心. Nac ...
- Nacos作为配置中心时,多个服务共用一个dataId的配置
写在前面 本文是对我之前一篇文章<Spring Cloud+nacos+Feign,实现注册中心及配置中心>的补充.此文章中简单写了如何将Nacos作为配置中心.在使用配置中心时,我们会遇 ...
- java架构之路-(微服务专题)feign的基本使用和nacos的配置中心
上次回归: 上次我们说了ribbon的基本使用,包括里面的内部算法,算法的细粒度配置,还有我们自己如何实现我们自己的算法,主要还是一些基本使用的知识,还不会使用ribbon的小伙伴可以回去看一下上一篇 ...
- Nacos系列:基于Nacos的配置中心
前言 在看正文之前,我想请你回顾一下自己待过的公司都是怎么管理配置的,我想应该会有以下几种方式: 1.硬编码 没有什么配置不配置的,直接写在代码里面,比如使用常量类 优势:对开发友好,开发清楚地知道代 ...
- Ocelot(三)- 服务发现
Ocelot(三)- 服务发现 作者:markjiang7m2 原文地址:https://www.cnblogs.com/markjiang7m2/p/10907856.html 源码地址:https ...
- 程序员你是如何使用Nacos作为配置中心的?
假如你使用的是spring-cloud-alibaba微服务技术栈 单个服务独有配置文件 即去除应用程序的状态,配置统一外部化管理,方便进行水平的伸缩. 集成步骤: 假如我有一个应用app-desig ...
- SpringBoot项目使用Nacos作为配置中心
前置条件:jdk.SpringBoot项目.Nacos.Linux服务器(可无) 具体版本:jdk11.SpringBoot 2.3.5.RELEASE.Nacos 2.0.3.Centos 6 目标 ...
- Ocelot中文文档-服务发现
Ocelot允许您指定服务发现提供程序,并使用它来查找Ocelot正在将请求转发给下游服务的主机和端口.目前,这仅在GlobalConfiguration部分中受支持,这意味着所有ReRoute将使用 ...
- Ocelot(十一)- 服务发现
Ocelot允许您指定服务发现提供程序,并使用它来查找Ocelot正在将请求转发给下游服务的主机和端口.目前,这仅在GlobalConfiguration部分中受支持,这意味着所有ReRoute将使用 ...
随机推荐
- MySql的远程登录问题
1.linux中先连接数据库:mysql -uroot -p(密码) 2.在mysql命令行中输入: GRANT ALL PRIVILEGES ON *.* TO '登录id'@'%' IDENTIF ...
- IDEA主题加高亮
IntelliJ Idea的黑色主题,使用就是file-->import settings 选择压缩包里的jar包,主题就被导入了,之后会提示重启,重启完就可以在设置中使用了. IDEA主题下载 ...
- 在FL Studio中通过Key Tracking来改善声音
FL Studio中的关键点跟踪(Key Tracking),是一种为MIDI添加更多动态效果的便利工具,在FL Studio中通过使用这个插件能力,我们无需担心自动化或手动调整参数等比较麻烦的问题. ...
- Java中类加载的过程
类加载过程 这里的加载过程是严格按照加载开始顺序进行的,注意是加载开始而不是加载完成.也就是有可能会有两个或几个阶段是同时进行的. 比如下面提到的验证过程中的符号引用验证是在解析阶段开始之后进行. 加 ...
- 如何获取公网IP的mac地址
如何获取远程IP的mac地址 思路分析 由于java本身没有相关的jar包进行获取,所以这里介绍从其他的方面进行入手和实践 使用的工具对比: tcpdump tshark pcap4j 都可以达到抓包 ...
- Linux 系统故障排查,这里看过来
来源:https://www.jianshu.com/p/0bbac570fa4c 导读 有时候会遇到一些疑难杂症,并且监控插件并不能一眼立马发现问题的根源.这时候就需要登录服务器进一步深入分析问题的 ...
- 【线程池】toString
java.util.concurrent.RejectedExecutionException: Task com.personal.practice.jvm.Jstacktest$1@7d605a5 ...
- Alpha冲刺-第五次冲刺笔记
Alpha冲刺-冲刺笔记 这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzzcxy/2018SE2 这个作业要求在哪里 https://edu.cnblogs. ...
- JZOJ2020年8月11日提高组反思
JZOJ2020年8月11日提高组反思 T1 看到题 啊这?! 我看错了吗??? 我理解错题了吗?? 好吧没有-- 高精度模板题,不用多说 T2 看到这种矩阵的问题 以为是前缀和搞事情 结果呢 扫描线 ...
- Codeforces Round #688(Div 2) D. Checkpoints
思路 第一步,先推导1,0,0,--,0,就是1后面跟了n-1个0的时候 所需要的期望步数 封闭式推导 \(f_n\)代表从n关开始直接通关需要的步数的期望 n为1的情况,即就只有一个1 \(f_1= ...