最近一段时间 因公司业务需要,需要使用.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. rgw配置删除快速回收对象

    前言 做rgw测试的时候,经常会有删除文件的操作,而用默认的参数的时候,rgw是通过gc回收机制来处理删除对象的,这个对于生产环境是有好处的,把删除对业务系统的压力分摊到不同的时间点,但是测试的时候, ...

  2. Python_PyQt5_库

    QtQWidgets  小组件(暂无资料,但是Python中做窗口/网页时用的很多  *-*)  QtCore 模块包括了核心的非GUI功能,该模块用来对时间.文件.目录.各种数据类型.流.网址.媒体 ...

  3. 4.1 Spring源码 --- 监听器的原理

    目标: 1. 监听器如何使用 2. 监听器的原理 3. 监听器的类型 4. 多播器的概念和作用 5. 接口类型的监听器是如何注册的? 6. 注解类型的监听器和如何注册的? 7. 如果想在所有的bean ...

  4. 将多个PDF文件整合成一个文件

    pdfFactory不仅可以将单个文件创建为PDF文件进行打印,还可以将多个文件整合为一个PDF文件,同时,也可以随时删除其中的一些文件,创建新的PDF文件. 图1:pdfFactory工具界面 一. ...

  5. 使用Camtasia制作我的观影报告

    最近抖音兴起做<我的观影报告>风潮.<我的观影报告>是通过剪辑影片+旁白的方法,将自己观看过的影片安利给观众的方式.如果大家想要制作这类型的观影报告,建议使用Camtasia( ...

  6. 用大白话讲大数据HBase,老刘真的很用心(1)

    老刘今天复习HBase知识发现很多资料都没有把概念说清楚,有很多专业名词一笔带过没有解释.比如这个框架高性能.高可用,那什么是高性能高可用?怎么实现的高性能高可用?没说! 如果面试官听了你说的,会有什 ...

  7. window consul安装和运行

    consul 前言 一.consul是什么? 二.使用步骤 1.安装 2.启动 结束~~ 前言 刚开始接触spring cloud,其中就有用到consul,简单介绍一下以及怎么安装运行起来 提示:以 ...

  8. Matlab 画图2

    fplot函数 plot函数的缺点:在实际应用中,函数随着自变量的变化趋势是未知的,如果自变量的离散区间不合理,则无法反应函数的变化趋势. fplot的作用:通过自适应算法,解决上述问题. fplot ...

  9. C语言讲义——错误处理

    errno C语言不提供对错误处理的直接支持. 以返回值的形式表示是否出错. 在发生错误时,大多数的C函数调用返回1或NULL. 同时设置一个错误代码errno(全局变量),表示在函数调用期间发生了错 ...

  10. mysql GTID主从复制故障后不停机恢复同步流程

    GTID实现主从复制数据同步 GTID是一个基于原始mysql服务器生成的一个已经被成功执行的全局事务ID,它由服务器ID以及事务ID组成,这个全局事务ID不仅仅在原始服务器上唯一,在所有主从关系的m ...