最近一段时间 因公司业务需要,需要使用.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. 面试常问的 25+ 个 Linux 命令

    作为一个Java开发人员,有些常用的Linux命令必须掌握.即时平时开发过程中不使用Linux(Unix)或者mac系统,也需要熟练掌握Linux命令.因为很多服务器上都是Linux系统.所以,要和服 ...

  2. hive简单的项目实战

    解压user.zip [root@hadoop1 test]# unzip user.zip -d /test/bigdatacase/dataset Archive: user.zip inflat ...

  3. python菜鸟教程学习5: python运算符

    算术运算符: 整除符号//:向下取接近商的整数 加+ 减- 乘* 除/ 取余% 幂** 比较运算符: 等于== 不等于!= 大于> 小于< 大于等于>= 小于等于<= 赋值运算 ...

  4. spring中的事务传播机制

    1.事务的实现思想 在spring中要想某个方法具有事务,只要在方法前加一个@Transactional注解.然后spring就会利用aop思想,在这个方法执行前开启事务, 在方法执行后选择提交事务或 ...

  5. 不一样的Flink入门教程

    前言 微信搜[Java3y]关注这个朴实无华的男人,点赞关注是对我最大的支持! 文本已收录至我的GitHub:https://github.com/ZhongFuCheng3y/3y,有300多篇原创 ...

  6. C#6,C#7,V#8,C#9 的新特性总结

    看了一下,下图的所有我都有用过,感觉越高的版本越好用. C# 6.0 特性 C# 7.0  Vs2017 C# 8.0     .net core 3.0+ C#9.0 .net5 C#的各种语法糖, ...

  7. Win搭建JAVA环境

    一:下载JDK 下载链接:https://www.oracle.com/java/technologies/javase-downloads.html 选择你的系统环境进行下载 二:安装JDK 直接运 ...

  8. 【mq读书笔记】消息消费过程(钩子 失败重试 消费偏移记录)

    在https://www.cnblogs.com/lccsblog/p/12249265.html中,PullMessageService负责对消息队列进行消息拉取,从远端服务器拉取消息后将消息存入P ...

  9. Qt5字符串编码转换学习

    目录 1.通过Python3示例探索常用字符串编码 UTF8 ANSI Unicode 小结 2.Qt5中使用QTextCodec进行编码转换 小结 1.通过Python3示例探索常用字符串编码 下面 ...

  10. 网络 华为 ensp 命令

    VLAN 端口有三种模式:access,hybrid,trunk. Access类型端口:只能属于1个VLAN,且该端口不打tag,一般用于连接计算机端口: Trunk类型端口:可以允许多个VLAN通 ...