Ocelot在github的地址 https://github.com/TomPallister/Ocelot , 非常给力的是在课程当天完成了.NET Core 2.0的升级,升级过程请看https://github.com/TomPallister/Ocelot/issues/114 。昨天我花了半小时就把我的另外一个POC项目Nanofabric   https://github.com/geffzhang/NanoFabric 升级到了.NET Core 2.0, 这个POC项目也是我的分享的项目的原型,可以这么说.NET Core 2.0 8月份正式发布,经过3 个月时间的发展,社区生态已经都已经做好了准备,开发新项目可以采用.NET Core 2,Ocelot 是一个集成社区中众多优秀开源项目的代表。

业务的飞速发展,产生的非常多的对外的服务接口,分散在组织的各个地方需要进行统一的管理,而且我们的环境是linux和windows的混合环境,我们的目标是统一在公司的Linux环境,.NET Core对于.NET 技术团队来说是一个非常棒的技术,而且.NET Core本身的架构非常好,性能就更好了。

这里列出了Ocelot目前支持的特性:

  • Routing

    • 用户可以指定上游请求之间的映射,并将其转发到下游服务上的不同URL。
  • Service Discovery
    • Ocelot可以查看你的服务发现,并找到它应该转发下游请求的服务。它可以在这些服务之间进行负载平衡。.
  • Authentication using IdentityServer
    • 您可以将端点标记为已认证,并使用IdentityServer承载标记对您的用户进行身份验证.
  • Authorisation using Claims
    • 如果使用 bearer tokens, 可以使用 claims 标记特定 endpoints是授权的
  • Claims Transformation
    • Ocelot提供了一种语法来转换给下游请求,并将声明数据添加到标题,URL参数,其他声明等等
  • Quality of service
    • Retries, circuit breaker, timeouts etc.
  • Request / Correlation Ids
  • Caching
  • Logging
  • Custom Middleware

更详细的内容参看文档 https://github.com/TomPallister/Ocelot/wiki

上面介绍了Ocelot的功能特性,接下来我们进入介绍Ocelot 的实现原理剖析,核心是是ASP.NET Core Middleware 以及 ASP.NET Core DependencyInjection:

ASP.NET Core 传统的ASP.NET 在架构上有很大的改进,更加的模块化,下图形象的说明了他们之间区别,Application 和 Middleware 是平等的,比如ASP.NET Core MVC也是一个Middleware,通过Middleware这样的结构我们非常容易的扩展我们的应用程序。

Ocelot就是使用Middleware来完成网关的所有功能,每个小功能就是一个Middleware,具体可以看代码 https://github.com/TomPallister/Ocelot/blob/develop/src/Ocelot/Middleware/OcelotMiddlewareExtensions.cs ,Ocelot 是如何把各个Middleware串起来协同完成一个API网关的功能。 asp.net core 非常巧妙的设计,把Middleware抽象成了一个委托RequestDelegate, ASP.NET Core 的每个 Request 都会经过每个所注册的 Middleware,Response 也是逐一回传,以先进后出的方式处理每一个封包:

具体内容参考: ASP.NET Core HTTP 管道中的那些事儿 和 如何一秒钟从头构建一个 ASP.NET Core 中间件, 我们在Middleware的编程过程中需要关注HttpContext 以及管道的注册者和构建者 ApplicationBuilder。

ASP.NET Core 使用了大量的 DI (Dependency Injection) 设计,同样我们在Ocelot的设计中也使用了大量的DI设计,具体参看源码https://github.com/TomPallister/Ocelot/blob/develop/src/Ocelot/DependencyInjection/ServiceCollectionExtensions.cs

注册 Service 有分三种方式:

  • Transient  每次注入时,都重新 new 一个新的实体。
  • Scoped    每个 Request 都重新 new 一个新的实体。
  • Singleton 程序启动后会 new 一个实体。也就是运行期间只会有一个实体。

下面这张图来自https://blog.johnwu.cc/article/asp-net-core-dependency-injection.html ,形象的演示了对象生命周期。

  • A 为 Singleton
  • B 为 Scoped
  • C 为 Transient

上面介绍完了Ocelot开发的基本原理,目前Ocelot 由17 个Middleware 来完成,在每个Middleware的内部实现上还有涉及到很多业务的知识,本篇文章先不做展开,后续写具体的文章详细解析。接下来我们来说说如何自定义扩展,在我们的项目中主要在三个方面进行了扩展:

1、自定义扩展API 接口验证

Ocelot 默认支持基于IdentityServer4的认证,需要自定义认证,可以参考 https://github.com/TomPallister/Ocelot/pull/110,添加自定义的验证,但是.net core 2.0 认证部分基本上重写了。

2、自定义扩展下游通讯协议

Ocelot 默认支持Http的通讯,在我们的实际项目中有很多老的服务是RPC调用,使用的是私有的Relay通讯框架,在API网关上需要做协议转换,自动将Http的请求转换成Relay的tcp通讯。

3、自定义管理控制台

ocelot 有管理API,可以基于管理API 做自定义的管理控制台,github 有个 https://github.com/dbarkwell/Ocelot.ConfigEditor,这个项目实现了asp.net core mvc 的在线编辑路由。

首先,让我们简单了解下什么是API网关?

API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。
    API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。

其次,我们了解下Ocelot框架

Ocelot的目标是使用.NET运行微服务/面向服务架构,我们需要一个统一的入口进入我们的服务,提供监控、鉴权、负载均衡等机制,也可以通过编写中间件的形式,来扩展Ocelot的功能。  Ocelot是一堆特定顺序的中间件。

Ocelot框架内部集成了IdentityServer和Consul(服务注册发现),还引入了Polly来处理进行故障处理,关于Polly,可以在这篇博客中了解更多《已被.NET基金会认可的弹性和瞬态故障处理库Polly介绍》

Ocelot开源地址:https://github.com/TomPallister/Ocelot

接下来,我们就针对Ocelot的具体用法展开介绍。

这里使用的Ocelot版本为2.0,.Net Core版本2.0

1、搭建Asp.net Core WebApi项目,引用Ocelot.dll。

Nuget控制台,执行Ocelot安装。

1
PM>Install-Package Ocelot

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
GET https://api.nuget.org/v3/registration3-gz-semver2/ocelot/index.json
  OK https://api.nuget.org/v3/registration3-gz-semver2/ocelot/index.json 104 毫秒
  GET https://api.nuget.org/v3/registration3-gz-semver2/ocelot/page/1.5.0-unstable0134/2.0.0.json
  OK https://api.nuget.org/v3/registration3-gz-semver2/ocelot/page/1.5.0-unstable0134/2.0.0.json 108 毫秒
正在还原 J:\Demo\RichCodeBox\APIGatewayApp\APIGatewayApp.csproj 的包...
  GET https://api.nuget.org/v3-flatcontainer/ocelot/index.json
  OK https://api.nuget.org/v3-flatcontainer/ocelot/index.json 476 毫秒
  GET https://api.nuget.org/v3-flatcontainer/ocelot/2.0.0/ocelot.2.0.0.nupkg
  OK https://api.nuget.org/v3-flatcontainer/ocelot/2.0.0/ocelot.2.0.0.nupkg 125 毫秒
  GET https://api.nuget.org/v3-flatcontainer/identityserver4.accesstokenvalidation/index.json
  GET https://api.nuget.org/v3-flatcontainer/cachemanager.core/index.json
  GET https://api.nuget.org/v3-flatcontainer/cachemanager.microsoft.extensions.configuration/index.json
  GET https://api.nuget.org/v3-flatcontainer/cachemanager.microsoft.extensions.logging/index.json
  GET https://api.nuget.org/v3-flatcontainer/consul/index.json
  GET https://api.nuget.org/v3-flatcontainer/polly/index.json
  GET https://api.nuget.org/v3-flatcontainer/identityserver4/index.json
  OK https://api.nuget.org/v3-flatcontainer/identityserver4.accesstokenvalidation/index.json 133 毫秒
  GET https://api.nuget.org/v3-flatcontainer/identityserver4.accesstokenvalidation/2.1.0/identityserver4.accesstokenvalidation.2.1.0.nupkg
  OK https://api.nuget.org/v3-flatcontainer/cachemanager.microsoft.extensions.logging/index.json 286 毫秒
  OK https://api.nuget.org/v3-flatcontainer/polly/index.json 287 毫秒
  OK https://api.nuget.org/v3-flatcontainer/identityserver4.accesstokenvalidation/2.1.0/identityserver4.accesstokenvalidation.2.1.0.nupkg 160 毫秒
  GET https://api.nuget.org/v3-flatcontainer/cachemanager.microsoft.extensions.logging/1.1.1/cachemanager.microsoft.extensions.logging.1.1.1.nupkg

  2、修改Startup程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// <summary>
     ///
     /// </summary>
     /// <param name="environment"></param>
     public Startup(IHostingEnvironment environment)
     {
         var builder = new Microsoft.Extensions.Configuration.ConfigurationBuilder();
         builder.SetBasePath(environment.ContentRootPath)
                .AddJsonFile("appsettings.json"false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{environment.EnvironmentName}.json", optional: false, reloadOnChange: true)
                .AddJsonFile("configuration.json", optional: false, reloadOnChange: true)
                .AddEnvironmentVariables();
 
 
         Configuration = builder.Build();
     }

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/// <summary>
///modified:配置
/// </summary>
public IConfigurationRoot Configuration { get; }
 
/// <summary>
/// 配置服务
/// </summary>
/// <param name="services"></param>
public void ConfigureServices(IServiceCollection services)
{
    Action<ConfigurationBuilderCachePart> settings = (x) =>
    {
        x.WithMicrosoftLogging(log =>
        {
            log.AddConsole(LogLevel.Debug);
 
        }).WithDictionaryHandle();
    };
    services.AddOcelot(Configuration, settings);
    //services.AddMvc();
}
 
/// <summary>
/// 配置Ocelot
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
public async void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    //if (env.IsDevelopment())
    //{
    //    app.UseDeveloperExceptionPage();
    //}
    await app.UseOcelot();
    //app.UseMvc();
}
/// <summary>
/// 入口程序
/// </summary>
/// <param name="args"></param>
public static void Main(string[] args)
{
    IWebHostBuilder builder = new WebHostBuilder();
    builder.ConfigureServices(s =>
    {
        s.AddSingleton(builder);
    });
    builder.UseKestrel()
           .UseContentRoot(Directory.GetCurrentDirectory())
           .UseIISIntegration()
           .UseStartup<Startup>()
           .UseApplicationInsights();
    var host = builder.Build();
    host.Run();
}

  

3、配置Ocelot。

我们新建一个名为configuration的json文件,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
  "ReRoutes": [
    {
      "DownstreamPathTemplate""/api/values",
      "DownstreamScheme""http",
      "DownstreamHost""localhost",
      "DownstreamPort": 8801,
      "UpstreamPathTemplate""/api/values",
      "UpstreamHttpMethod": [ "Get" ],
      "QoSOptions": {
        "ExceptionsAllowedBeforeBreaking": 3,
        "DurationOfBreak": 10,
        "TimeoutValue": 5000
      },
      "HttpHandlerOptions": {
        "AllowAutoRedirect"false,
        "UseCookieContainer"false
      },
      "AuthenticationOptions": {
 
      }
    },
    {
      "DownstreamPathTemplate""/api/product",
      "DownstreamScheme""http",
      "DownstreamPort": 8802,
      "DownstreamHost""localhost",
      "UpstreamPathTemplate""/api/product",
      "UpstreamHttpMethod": [ "Get" ],
      "QoSOptions": {
        "ExceptionsAllowedBeforeBreaking": 3,
        "DurationOfBreak": 10,
        "TimeoutValue": 5000
      },
      "AuthenticationOptions": {
 
      }
    }
  ],
  "GlobalConfiguration": {
    "RequestIdKey""OcRequestId",
    "AdministrationPath""/admin"
  }
}

  

在这里,我们配置了两个服务,端口分别为8801和8802的。

Ocelot支持负载均衡(提供轮询、最少访问)。Ocelot大部分功能,都可以通过中间件来完成,也可以实现和重写中间件。

Ocelot原理非常简单,这里的配置文件,体现了上游请求和下游服务间的映射关系,你可以理解为,上游是客户端直接调用的URL ,下游,则是对应我们开发的服务。

4、新增两个WebApi项目,分别为APIUserServiec和APIProductService。

API服务 端口(Port)
APIUserServiec 8801
APIProductService 8802

解决方案如下:

5、配置VS启动端口:

依次类推,分别设置端口。

6、启动项目。

配置多个项目启动。

F5启动项目。

再通过API网关,访问商品服务:http://localhost:5000/api/product。

常见问题:

首次在启动API网关时,触发以下错误。

Sequence contains no matching element

根据错误详细信息,可知原因是由于系统调用AddIdentityServer方法时,触发异常。

刚开始,怀疑是配置文件configuration.json文件配置导致的,Ocelot2.0版,采用官方配置仍然触发该异常,由此排除这种可能。接下来,直接从github上克隆源代码,查看。

找到触发错误的地方,

  1. private static void AddIdentityServer(this IServiceCollection services, IIdentityServerConfiguration identityServerConfiguration, IConfigurationRoot configurationRoot)
  2. {
  3. services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
  4. services.TryAddSingleton<IHashMatcher, HashMatcher>();
  5. var identityServerBuilder = services
  6. .AddIdentityServer(o => {
  7. o.IssuerUri = "Ocelot";
  8. })
  9. .AddInMemoryApiResources(Resources(identityServerConfiguration))
  10. .AddInMemoryClients(Client(identityServerConfiguration))
  11. .AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
  12.  
  13. //todo - refactor a method so we know why this is happening
  14. var whb = services.First(x => x.ServiceType == typeof(IWebHostBuilder));//这个地方触发了错误
  15. var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance);
  16. var baseSchemeUrlAndPort = urlFinder.Find();
  17. JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
  18.  
  19. services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
  20. .AddIdentityServerAuthentication(o =>
  21. {
  22. var adminPath = configurationRoot.GetValue("GlobalConfiguration:AdministrationPath", string.Empty);
  23. o.Authority = baseSchemeUrlAndPort + adminPath;
  24. o.ApiName = identityServerConfiguration.ApiName;
  25. o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
  26. o.SupportedTokens = SupportedTokens.Both;
  27. o.ApiSecret = identityServerConfiguration.ApiSecret;
  28. });
  29.  
  30. //todo - refactor naming..
  31. if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword))
  32. {
  33. identityServerBuilder.AddDeveloperSigningCredential();
  34. }
  35. else
  36. {
  37. //todo - refactor so calls method?
  38. var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword);
  39. identityServerBuilder.AddSigningCredential(cert);
  40. }
  1. var whb = services.First(x => x.ServiceType == typeof(IWebHostBuilder));

这就代码触发了错误,是由于表达式条件不成立,导致First发生异常,这就简单了,我们修改Main函数,

把这句代码:

  1. var builder = new WebHostBuilder();

改成:

  1. IWebHostBuilder builder = new WebHostBuilder();

这样,就解决了问题,API网关启动成功。另外,官方例子https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.ManualTest/Program.cs中,写法是正确的,大家自己写的时候,注意把IWebHostBuilder换成var会引发错误。

这样,使用Ocelot框架搭建API网关的工作已经完成,尝试通过访问API网关,来访问下游的服务。

此实例源码:https://gitee.com/lichaoqiang/RichCodeBox.git

欢迎大家一起研究探讨,开启你的微服务之路。

Net Core API网关Ocelot的更多相关文章

  1. 初探.Net Core API 网关Ocelot(一)

    一.介绍 Ocelot 是基于.NetCore实现的开源的API网关,支持IdentityServer认证.Ocelot具有路由.请求聚合.服务发现.认证.鉴权.限流熔断等功能,并内置了负载均衡器与S ...

  2. Asp.Net Core API网关Ocelot

    首先,让我们简单了解下什么是API网关? API网关是一个服务器,是系统的唯一入口.从面向对象设计的角度看,它与外观模式类似.API网关封装了系统内部架构,为每个客户端提供一个定制的API.它可能还具 ...

  3. [Hei-Ocelot-Gateway ].Net Core Api网关Ocelot的开箱即用版本

    写在前面 很多neter都有在用Ocelot做Api网关,但是Ocelot又不像kong或者其他网关一样,开箱即用.它需要你单独开一个web项目来部署,这样很多同学都在做重复的事了. 这里[Hei.O ...

  4. ASP.NET Core Api网关Ocelot的中文文档

    架构图 入门 不支持 配置 路由 请求聚合 GraphQL 服务发现 微服务ServiceFabric 认证 授权 Websockets 管理 流量控制 缓存 QoS服务质量 转换Headers 转换 ...

  5. .NET Core 微服务—API网关(Ocelot) 教程 [二]

    上篇文章(.NET Core 微服务—API网关(Ocelot) 教程 [一])介绍了Ocelot 的相关介绍. 接下来就一起来看如何使用,让它运行起来. 环境准备 为了验证Ocelot 网关效果,我 ...

  6. .NET Core 微服务—API网关(Ocelot) 教程 [三]

    前言: 前一篇文章<.NET Core 微服务—API网关(Ocelot) 教程 [二]>已经让Ocelot和目录api(Api.Catalog).订单api(Api.Ordering)通 ...

  7. .NET Core开源API网关 – Ocelot中文文档

    Ocelot是一个用.NET Core实现并且开源的API网关,它功能强大,包括了:路由.请求聚合.服务发现.认证.鉴权.限流熔断.并内置了负载均衡器与Service Fabric.Butterfly ...

  8. .Net Core的API网关Ocelot使用 (一)

    1.什么是API网关 API网关是微服务架构中的唯一入口,它提供一个单独且统一的API入口用于访问内部一个或多个API.它可以具有身份验证,监控,负载均衡,缓存,请求分片与管理,静态响应处理等.API ...

  9. .NET Core 微服务—API网关(Ocelot) 教程 [一]

    前言: 最近在关注微服务,在 eShop On Containers 项目中存在一个API网关项目,引起想深入了解下它的兴趣. 一.API网关是什么 API网关是微服务架构中的唯一入口,它提供一个单独 ...

随机推荐

  1. Tornado-Form表单验证

    基本思路 用户提交表单后,验证开始.页面中会有多个域要求验证,如text input,files, checkbox.同时,根据验证字段的不同,验证方式会有很多种,例如对邮箱.IP地址.电话的验证标准 ...

  2. Node.js_express_搭建一个服务器

    原生 node 服务器 1. 导入 node.js 核心模块  / 自带模块 :   http const http = require('http'); // HTTP 库所具有的功能已经赋给了 h ...

  3. Python练手例子(2)

    7.将一个列表的数据复制到另一个列表中. 程序分析:使用列表[:]. #python3.7 #适用于简单列表(即列表中都是基本的元素) a1 = [1,2] b1 = a1[:] print(b1) ...

  4. pta编程总结

    币值转换 (20 分) 输入一个整数(位数不超过9位)代表一个人民币值(单位为元),请转换成财务要求的大写中文格式.如23108元,转换后变成“贰万叁仟壹百零捌”元.为了简化输出,用小写英文字母a-j ...

  5. Vue 前端面试题

    Vue 前端面试题 1. 说一下 Vue 的双向绑定数据的原理 vue 实现数据双向绑定主要是:采用数据劫持结合“发布者 - 订阅者”模式的方式,通过 Object.defineProperty() ...

  6. Service Mesh 数据平面 SOFAMosn

    https://mp.weixin.qq.com/s/DJ_IeDswGGFQiWqJ75pmig 开源 | Service Mesh 数据平面 SOFAMosn 深层揭秘 朵晓东 蚂蚁金服科技 20 ...

  7. Express全系列教程之(二):Express的路由以及动态路由

    一.Express路由简介 路由表示应用程序端点 (URI) 的定义以及响应客户端请求的方式.它包含一个请求方时(methods).路径(path)和路由匹配时的函数(callback); app.m ...

  8. list的基本操作实现

    有关list的相关实现,主函数没有写很多,每个部分目前没发现有问题: #include <iostream> #include <stdio.h> using namespac ...

  9. DAX/PowerBI系列 - 玩转阿里云 Alicloud Pricing

    DAX/PowerBI系列 - 玩转 阿里云主机 Ali Cloud ECS 难度: ★★☆☆☆(1星) 适用范围: ★★★☆☆(3星) 欢迎交流与骚扰 这是啥: 双十一就到了,码农门,程序猿们有没有 ...

  10. ORA-27300 ORA-27301 ORA-27302 skgpspawn3 CRS-2674

    oracle@WWJD-DB1:~> $ORACLE_HOME/bin/srvctl start database -d ndscdb PRCR-1079 : Failed to start r ...