最近一段时间 因公司业务需要,需要使用.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 配置中心和服务发现实现的更多相关文章

  1. Nacos配置中心和服务的注册发现

    在上一篇中,我们已经把Nacos的集群搭建好了,那么既然已经搭建好了,就要在咱们的项目中去使用.Nacos既可以做配置中心,也可以做注册中心.我们先来看看在项目中如何使用Nacos做配置中心. Nac ...

  2. Nacos作为配置中心时,多个服务共用一个dataId的配置

    写在前面 本文是对我之前一篇文章<Spring Cloud+nacos+Feign,实现注册中心及配置中心>的补充.此文章中简单写了如何将Nacos作为配置中心.在使用配置中心时,我们会遇 ...

  3. java架构之路-(微服务专题)feign的基本使用和nacos的配置中心

    上次回归: 上次我们说了ribbon的基本使用,包括里面的内部算法,算法的细粒度配置,还有我们自己如何实现我们自己的算法,主要还是一些基本使用的知识,还不会使用ribbon的小伙伴可以回去看一下上一篇 ...

  4. Nacos系列:基于Nacos的配置中心

    前言 在看正文之前,我想请你回顾一下自己待过的公司都是怎么管理配置的,我想应该会有以下几种方式: 1.硬编码 没有什么配置不配置的,直接写在代码里面,比如使用常量类 优势:对开发友好,开发清楚地知道代 ...

  5. Ocelot(三)- 服务发现

    Ocelot(三)- 服务发现 作者:markjiang7m2 原文地址:https://www.cnblogs.com/markjiang7m2/p/10907856.html 源码地址:https ...

  6. 程序员你是如何使用Nacos作为配置中心的?

    假如你使用的是spring-cloud-alibaba微服务技术栈 单个服务独有配置文件 即去除应用程序的状态,配置统一外部化管理,方便进行水平的伸缩. 集成步骤: 假如我有一个应用app-desig ...

  7. SpringBoot项目使用Nacos作为配置中心

    前置条件:jdk.SpringBoot项目.Nacos.Linux服务器(可无) 具体版本:jdk11.SpringBoot 2.3.5.RELEASE.Nacos 2.0.3.Centos 6 目标 ...

  8. Ocelot中文文档-服务发现

    Ocelot允许您指定服务发现提供程序,并使用它来查找Ocelot正在将请求转发给下游服务的主机和端口.目前,这仅在GlobalConfiguration部分中受支持,这意味着所有ReRoute将使用 ...

  9. Ocelot(十一)- 服务发现

    Ocelot允许您指定服务发现提供程序,并使用它来查找Ocelot正在将请求转发给下游服务的主机和端口.目前,这仅在GlobalConfiguration部分中受支持,这意味着所有ReRoute将使用 ...

随机推荐

  1. 使用Ganglia监控系统监控集群(debian)

    ganglia是一个集群监控软件,底层使用RRDTool获得数据. Ganglia分为ganglia-monitor和gmetad两部分,前者运行在集群每个节点上(被监控机器)收集RRDTool产生的 ...

  2. 从 3.1 到 5.0 —— OpenReservation 更新记

    OpenReservation 从 asp.net core 3.1 到 5.0 Intro OpenReservation 是一个开源的预约系统,最初的版本是我们学校的活动室预约系统,现在正逐步变成 ...

  3. 详解Java锁的升级与对比(1)——锁的分类与细节(结合部分源码)

    前言 之前只是对Java各种锁都有所认识,但没有一个统一的整理及总结,且没有对"锁升级"这一概念的加深理解,今天趁着周末好好整理下之前记过的笔记,并归纳为此博文,主要参考资源为&l ...

  4. Fastjson远程代码执行漏洞复现

    fastjson漏洞简介 Fastjson是一个Java库,可用于将Java对象转换为其JSON表示形式.它还可以用于将JSON字符串转换为等效的Java对象,fastjson爆出多个反序列化远程命令 ...

  5. php7的Opcache getshell

    OPcache基础 OPcache是一种通过解析的PHP脚本预编译的字节码存放在共享内存中来避免每次加载和解析PHP脚本的开销,解析器可以直接从共享内存读取已经缓存的字节码,从而大大提高了PHP的执行 ...

  6. PHP反序列化漏洞-CVE-2016-7124(绕过__wakeup)复现

    前言 最近电脑也不知怎么了时不时断网而且我竟然找不出原因!!!很诡异....  其他设备电脑都OK唯独我的电脑 时好时坏 我仿佛摸清了我电脑断网的时间段所以作息时间都改变了  今天12点多断网刷了会手 ...

  7. Pinpoint 更改agentid 和 agent name 长度限制(Pinpoint系列二)

    本文基于 Pinpoint 2.1.0 版本 本文的内容为了更改 ID 和 Name 长度限制,因为有使用容器或者是服务名称确实比较长,所以根据业务场景,我们需要更改源码来实现这个. 具体更改,参考 ...

  8. 在FL Studio中如何制作和优化你的人声和弦(Vocal Chords)

    人声和弦在Future Bass.Melodic Dubstep等类型的电子音乐中被常用.与一般的和弦相同,其主要起到为主旋律做铺垫的效果,但是人声和弦加入了人声的因素,可以使得和弦更有趣,更有电子音 ...

  9. jQuery 第五章 实例方法 详解内置队列queue() dequeue() 方法

    .queue() .dequeue() .clearQueue() ------------------------------------------------------------------ ...

  10. TIOBE 11月指数:C语言居首,稳居宝座,Python直逼第二!

    官方网址:https://www.tiobe.com/tiobe-index/   ​ 这是自近20年前TIOBE指数开始以来,Java和C第一次不再占据前两位.C仍然是第一位的,但是现在第二个位置是 ...