前言

    近期在看博客的时候或者在群里看聊天的时候,发现很多都提到了Ocelot网关的问题。我之前也研究过一点,网关本身是一种通用的解决方案,主要的工作就是拦截请求统一处理,比如认证、授权、熔断、限流、注册发现、负载均衡等等。随着服务化的不断盛行,服务拆分,负载均衡等已成为当今软件行业随处可谈的名词了,因此注册中心也随之流行了起来。Ocelot作为网关自然可以集成许多注册中心,官方文档给出了集成Eureka和Consul的解决方案,Eureka可能有的人不是很熟悉,它是Spring Cloud的核心组件之一,其功能就是服务注册发现。随着.Net Core的不断成熟,不知道为啥Consul突然成了.Net Core注册中心和配置中心的主要选择,甚至可以说是首选了,可能是因为功能比较强大,而且是基于GO开发的。Nacos作为后起之秀,功能也非常强大。那天无意中翻了一下发现网上居然没有Ocelot集成到Nacos注册中心的组件,由于我个人非常喜欢通用解决方案,于是决定自己扩展一个Ocelot.Provider.Nacos,代码已经放到了我的GitHub上https://github.com/softlgl/Ocelot.Provider.Nacos,有兴趣的可自行查阅。

概念介绍

Ocelot

    Ocelot是一个用.NET Core实现并且开源的API网关,它具备了许多强大实用的功能,包括了:路由、请求聚合、服务发现、认证、鉴权、限流熔断、并内置了负载均衡器与Service Fabric、Butterfly Tracing集成。它是由asp.net core middleware组成的一个管道。当获取请求之后会用request builder来构造一个HttpRequestMessage转发到下游的真实服务器,等下游的服务返回response之后再由一个middleware将它返回的HttpResponseMessage映射到HttpResponse上。

Nacos

    关于Nacos我之前的文章搭建一套ASP.NET Core+Nacos+Spring Cloud Gateway项目已经有过介绍了。Nacos是阿里巴巴开源的致力于服务发现、配置和管理微服务的框架。提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。一般用到的最多的就是当做配置中心和注册中心。

Ocelot.Provider.Nacos

开发环境

  • 基于.Net Core 3.1,这个是必须的,因为最新稳定版的Ocelot是在.Net Core 3.1上构建的,而我恰好选择的是这个版本的Ocelot
  • Ocelot选择的是目前最新的稳定版本 v16.0.1
  • Nacos访问组件实用的是nacos-sdk-csharp,具体引用的是
    <PackageReference Include="nacos-sdk-csharp-unofficial" Version="0.2.7" />

    它其实是有另一个nacos-sdk-csharp-unofficial.AspNetCore版本的这个是针对Asp.Net Core程序集成Nacos使用的,Ocelot也是基于Asp.Net Core搭建起来的,但是我没有选用nacos-sdk-csharp-unofficial.AspNetCore,主要是因为虽然AspNetCore版本的集成起来更方便,但是为了让它能更好的适配到Ocelot服务注册和发现上,我需要自己改造一下原本的使用方式。

集成到Ocelot

将Ocelot.Provider.Nacos的方式非常简单基本上和Ocelot.Provider.Eureka和Ocelot.Provider.Consul是一致的,首先新建一个已经集成了Ocelot的Asp.Net Core项目,这里就不演示如何搭建的了,如果有不熟悉的可以查看我的demo示例https://github.com/softlgl/Ocelot.Provider.Nacos/tree/master/demo/ApiGatewayDemo,项目搭建好之后引入Ocelot.Provider.Nacos包

<PackageReference Include="Ocelot.Provider.Nacos" Version="1.0.0" />

然后在ConfigureServices中添加注册方法AddNacosDiscovery

public void ConfigureServices(IServiceCollection services)
{
//注册服务发现
services.AddOcelot().AddNacosDiscovery();
}

代码上做这么多就可以了,其他的主要工作就在配置上了,近期新版本的Ocelot相对于之前老版本有些地方改动还是非常大的,网上很多示例都是老版本的,所以参考官方文档搭建还是比较靠谱的。接下来我们打开Ocelot的配置文件配置注册中心相关的

{
"Routes": [
{
// 用于服务发现的名称,也就是注册到nacos上的名称
"ServiceName": "productservice",
"DownstreamScheme": "http",
"DownstreamPathTemplate": "/productapi/{everything}",
"UpstreamPathTemplate": "/productapi/{everything}",
"UpstreamHttpMethod": [ "Get", "Post" ],
"LoadBalancerOptions": {
"Type": "RoundRobin"
},
// 使用服务发现
"UseServiceDiscovery": true
}
],
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
// 这里是重点
"Type": "Nacos"
}
}
}

这里只是为Ocelot配置使用配置中心,接下来我们要配置Nacos访问地址相关的,打开appsettings.json,当然你自定义的配置文件也可以,只要程序可以加载得到

"nacos": {
"ServerAddresses": [ "http://localhost:8848" ],
"DefaultTimeOut": 15000,
"Namespace": "",
"ListenInterval": 1000,
// 网关注册的服务名称
"ServiceName": "apigateway"
}

具体如何配置保留了和nacos-sdk-csharp一致的方式,可以到nacos-sdk-csharp项目源码中去查看,nacos-sdk-csharp文档不是特别详细,但是源码注释非常给力。所以直接去配置类源码里查看就好了。

在这之前如果你已经启动了Nacos,然后就可以直接运行Ocelot网关项目,启动完成后打开Nacos如果出现如图所示,说明已经注册成功你也可以直接运行我提供的demo需要启动ApiGatewayDemo和ProductApi两个项目,一个是OcelotDemo一个是服务Demo,输入网关地址和需要转发的url后可展示如下数据

自定义扩展代码

代码基本上我是参考着Ocelot.Provider.Eureka和Ocelot.Provider.Consul相关代码写的。其中入口类就一个是针对IOcelotBuilder的扩展类。

首先,将自定义的服务发现相关的服务注册进来。只展示Ocelot适配相关的,我自己写的关于服务注册发现相关的就不做展示了,有兴趣的可自行查阅

public static IOcelotBuilder AddNacosDiscovery(this IOcelotBuilder builder)
{
//添加注册自定义的NacosDiscovery相关的服务
builder.Services.AddNacosDiscovery(builder.Configuration);
//添加自定义服务发现代理
builder.Services.AddSingleton<ServiceDiscoveryFinderDelegate>(NacosProviderFactory.Get);
//根据Ocelot配置文件相关内容设置处理服务发现相关
builder.Services.AddSingleton<OcelotMiddlewareConfigurationDelegate>(NacosMiddlewareConfigurationProvider.Get);
return builder;
}

其次,将自定义的注册发现相关操作提供出来,其实主要就是获取服务地址

public static class NacosProviderFactory
{
public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) =>
{
//Nacos相关服务类
var client = provider.GetService<INacosServerManager>();
//判断类型是否为nacos
if (config.Type?.ToLower() == "nacos" && client != null)
{
//返回自定义服务提供操作,route.ServiceName是命中的Route的配置的服务发现的名称
return new Nacos(route.ServiceName, client);
}
return null;
};
} public class Nacos : IServiceDiscoveryProvider
{
private readonly INacosServerManager _client;
private readonly string _serviceName; public Nacos(string serviceName, INacosServerManager client)
{
_client = client;
_serviceName = serviceName;
} public async Task<List<Service>> Get()
{
var services = new List<Service>();
var instances = await _client.GetServerAsync(_serviceName);
if (instances != null && instances.Any())
{
//将发现的地址组装成Service集合即可
services.AddRange(instances.Select(i => new Service(i.InstanceId, new ServiceHostAndPort(i.Ip, i.Port), "", "", new List<string>())));
} return await Task.FromResult(services);
}
}

最后,根据Ocelot配置相关的信息判断是否启动Nacos相关服务

public class NacosMiddlewareConfigurationProvider
{
public static OcelotMiddlewareConfigurationDelegate Get = builder =>
{
var internalConfigRepo = builder.ApplicationServices.GetService<IInternalConfigurationRepository>();
var config = internalConfigRepo.Get();
var hostLifetime = builder.ApplicationServices.GetService<IHostApplicationLifetime>();
//判断服务注册类型是否为nacos
if (UsingNacosServiceDiscoveryProvider(config.Data))
{
//启动nacos相关服务
builder.UseNacosDiscovery(hostLifetime).GetAwaiter().GetResult();
}
return Task.CompletedTask;
}; private static bool UsingNacosServiceDiscoveryProvider(IInternalConfiguration configuration)
{
//判断配置的服务发现类型是否为nacos
return configuration?.ServiceProviderConfiguration != null && configuration.ServiceProviderConfiguration.Type?.ToLower() == "nacos";
}
}

涉及到的相关代码并不多,而且比较清晰,了解到相关规则后还是比较简单的。

开发中遇到困难

Ocelot.Provider.Nacos一共大概有十个类左右,集成到Ocleot本身相关类有四个吧,其他的类都是集成Nacos相关的。

  • 首先我找错代码了仓库,刚开始我是顺着Nuget包上的地址找到了https://github.com/ThreeMammals/Ocelot.Provider.Eureka,但是其实这是一个废弃的老仓库,它最后更改的日期是18年9月,我当时还很好奇,这么长时间居然没有改动过,但是当我仿着这个写的时候遇到了转发相关路由的问题,后来我又到了nuget上看了一下,发现Ocelot.Provider.Eureka还一直在更新,后来我想会不会把代码移植到了Ocelot仓库中,果然不出所料在https://github.com/ThreeMammals/Ocelot/tree/master/src/Ocelot/ServiceDiscovery发现了这个文件夹,后来仿着这个继续改造。
  • 后来写的差不多的时候准备调试输入具体转发路径的时候一直报UnableToFindServiceDiscoveryProviderError:Unable to find service discovery provider for type: nacos,刚开始我一直没有找到原因,明明我已经给IServiceDiscoveryProvider注册了NacosDiscovery实例它却还是说找不到,找了老长时间后来我想到了去Ocelot源码查到UnableToFindServiceDiscoveryProviderError的引用,最后在ServiceDiscoveryProviderFactory类,看到了如下代码,Ocelot的规则就是注册的Type类型要和自定义的IServiceDiscoveryProvider实现类名称一致才可以
private Response<IServiceDiscoveryProvider> GetServiceDiscoveryProvider(ServiceProviderConfiguration config, DownstreamRoute route)
{
if (config.Type?.ToLower() == "servicefabric")
{
var sfConfig = new ServiceFabricConfiguration(config.Host, config.Port, route.ServiceName);
return new OkResponse<IServiceDiscoveryProvider>(new ServiceFabricServiceDiscoveryProvider(sfConfig));
} if (_delegates != null)
{
var provider = _delegates?.Invoke(_provider, config, route);
//1.自定义的IServiceDiscoveryProvider实现类名称要和ServiceDiscoveryProvider里的Type名称一致
if (provider.GetType().Name.ToLower() == config.Type.ToLower())
{
return new OkResponse<IServiceDiscoveryProvider>(provider);
}
}
//2.否则就会返回这异常信息
return new ErrorResponse<IServiceDiscoveryProvider>(new UnableToFindServiceDiscoveryProviderError($"Unable to find service discovery provider for type: {config.Type}"));
}

后来把我的实现类名称改成了Nacos果然可以了,当时出这个异常的时候非常困惑,还好我没放弃。

总结

    总之,通过本次扩展,踩了很多坑,也加深了相应的了解,希望能为有自定义扩展注册中心的同学提供一点帮助。Ocelot本身文档非常详细,但是对于扩展这一块没有过多的介绍,只能通过查看相关源码去了解。我觉得许多东西既然开源了,需要有自定义需求的可以通过查看源码解决问题。虽然查看源码其实并不容易,如果有针对性的话,相对来说还不是很复杂。重点是遇到问题你怎么解决,能否扛得住这种无助的心情,只能靠自己,说不定坚持一下就找到思路了。

手动造轮子——为Ocelot集成Nacos注册中心的更多相关文章

  1. Spring Cloud 系列之 Alibaba Nacos 注册中心(一)

    前言 从本章节开始,我们学习 Spring Cloud Alibaba 相关微服务组件. Spring Cloud Alibaba 介绍 Spring Cloud Alibaba 致力于提供微服务开发 ...

  2. Spring Cloud Alibaba(4)---Nacos(注册中心)

    Nacos(注册中心) 有关Spring Cloud Alibaba之前写过三篇文章. Spring Cloud Alibaba(1)---入门篇 Spring Cloud Alibaba(2)--- ...

  3. SpringCloud Alibaba实战(7:nacos注册中心管理微服务)

    源码地址:https://gitee.com/fighter3/eshop-project.git 持续更新中-- 在上一节我们已经完成了Nacos Server的本地部署,这一节我们学习如何将Nac ...

  4. Nacos注册中心和配置中心流程原理

    一.Nacos注册中心 1.服务启动后---->服务注册原理 springCloud集成Nacos实现原理: 服务启动时,在spring-cloud-commons包下 spring.facto ...

  5. Spring Cloud Alibaba 使用nacos 注册中心

    ### 背景 上一文我们讲到了如何去搭建注册中心,这一次我们讲述如何使用nacos作为注册中心 ### spring-cloud-alibaba-basis 创建基础依赖 首先我们创建一个spring ...

  6. Spring Cloud 系列之 Alibaba Nacos 注册中心(二)

    本篇文章为系列文章,未读第一集的同学请猛戳这里:Spring Cloud 系列之 Alibaba Nacos 注册中心(一) 本篇文章讲解 Nacos 注册中心集群环境搭建. Nacos 集群环境搭建 ...

  7. Nacos注册中心之概要设计

    本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star. 前言 在之前的文章中分析了Nacos配置中心,配置中心的核心是配置的创建.读取.推送. 注册中 ...

  8. 5-2 Nacos注册中心

    Nacos注册中心 什么Nacos Nacos是Spring Cloud Alibaba提供的一个软件 这个软件主要具有注册中心和配置中心的功能 我们先学习它注册中心的功能 微服务中所有项目都必须注册 ...

  9. 灵感乍现!造了个与众不同的Dubbo注册中心扩展轮子

    hello大家好呀,我是小楼. 作为一名基础组件开发,服务好每一位业务开发同学是我们的义务(KPI). 客服群里经常有业务开发同学丢来一段代码.一个报错,而我们,当然要微笑服务,耐心解答. 有的问题, ...

随机推荐

  1. 使用itext asian 解决中文不显示的问题

    本人使用的itextpdf版本是5.4.3<dependency> <groupId>com.itextpdf</groupId> <artifactId&g ...

  2. Spring Boot2+Resilience4j实现容错之Bulkhead

    Resilience4j是一个轻量级.易于使用的容错库,其灵感来自Netflix Hystrix,但专为Java 8和函数式编程设计.轻量级,因为库只使用Vavr,它没有任何其他外部库依赖项.相比之下 ...

  3. python高阶-Linux基础命令集

    声明: 1)仅作为个人学习,如有冒犯,告知速删! 2)不想误导,如有错误,不吝指教! 1: 查看文件信息:ls ls常用参数: 参数 含义 -a 显示指定目录下所有子目录与文件,包括隐藏文件 -l 以 ...

  4. html+css快速入门教程(2)

    3 标签 3.1 div div 标签表示一个区块或者区域,你可以把它看成是一个容器,比如说一个 竹篮 作用:用来把网页分块 并且里面可以装任意的html元素 <div>这里是一个div容 ...

  5. plsql启动报 Using filter for all users can lead to poor perform

    首先,这个与Oracle配置无关,就是在使用pl/sql左侧树形目录时会看到非常多的和你当前工作无关的表,视图,序列等,导致打开速度慢. ​解决办法:Tools-->Object browser ...

  6. Unity ugui Anchor锚点自动适配画布中的相对位置

    本随笔参考了以下博客,在此基础上进行优化和改进: https://blog.csdn.net/qq_39640124/article/details/88284191 ugui中的Anchor预设如下 ...

  7. 119.杨辉三角II

    这道题和第118题是一样的,需要注意这道题目对行数的要求         # 定义一个列表,用来存放数据         num_list = []         for index1 in ran ...

  8. 不就是语法和长难句吗—笔记总结Day1

    CONTENTS 第一课 简单句 第二课 并列句 第三课 名词(短语)和名词性从句 第四课 定语和定语从句 第五课 状语和状语从句 第六课 英语的特殊结构 第一课 奋斗的开始——简单句 一.什么是英语 ...

  9. IndentationError: unindent does not match any outer indentation level解决策略

    [亲测有效]Nodepad++/Sublime Text3中Python脚本运行出现语法错误:IndentationError: unindent does not match any outer i ...

  10. python递归函数实现阶乘函数

    实现的效果如下: 参考www.cnblogs.com/yuanchenqi/articles/5828233.html f(5)=5*4*3*2*1=120   f(7)=7*6*5*4*3*2*1= ...