微服务网关Ocelot
2017-09-24

微服务网关是微服务架构中的核心组件,它是客户端请求的门户,它是调用具体服务端的桥梁.下面我们将使用开源项目Ocelot(https://github.com/geffzhang/Ocelot)搭建一款轻量级服务网关,不过在此之前我们将对微服务网关做个详细介绍,以便大家更加清晰的了解什么是微服务网关。

什么是微服务网关

微服务网关类似于经典设计模式的Façade模式,它将底层的复杂细节进行屏蔽,对外提供简单而统一的调用方式,通常使用HTTP的RESTful API服务。此时,对于客户端而言,可以是PC端网页,也可以是移动设备,客户端通过HTTP方式调用微服务网关。

         微服务网关也称为服务网关(ServiceGateway)或API网关(API Gateway),下面通过一张架构图来表达服务网关与客户端以及服务端的关系。

                             

我们使用服务网关(Service Gateway)来建立客户端和服务端之间的联系。当客户端发送请求时,请求首先进入服务网关,随后服务网关将服务请求路由转发到具体的服务端。在路由的过程中,会涉及到具体的路由算法,最简单的做法是在服务网关中解析客户端请求中的路径或请求头,从而路由到具体的服务端。

         在微服务模式网站上也对服务网关进行了说明,可以通过以下地址了解API Gateway模式的具体信息。

         APIGateway:http://microservices.io/patterns/apigateway.html

         实际上,服务网关的路由过程我们称之为“反向代理”。如果大家曾经使用过Nginx,对反向代理这个概念不会陌生。说白了就是请求不会直接发送到目的地,而是通过一个中间件进行转发,这个中间件就是Nginx,它充当了反向代理的角色。

         在什么样场景下我们会考虑使用反向代理技术呢?

  • 使静态资源与动态资源分离

  • 实现AJAX跨域访问

  • 搭建统一服务网关接口

下面我们将使用Ocelet 实现一个服务网关的重要特性之一:反向代理。

 

使用Ocelot 实现一个反向代理

         Ocelot是一个使用.NET Core平台上的一个API Gateway,这个项目的目标是在.NET上面运行微服务架构,它很好的和Identity Server集成,由Identity Server负责验证授权。

         使用Ocelot 搭建一个反向代理,仅需要三步:

         第一步,使用VisualStudio 2017 创建一个ASP.NET Core空项目FitApiGateway,通过Nuget安装Ocelot

Install-PackageOcelot

第二步使用ASP.NET Core应用宿主Ocelot,在Startup.cs中配置,代码如下:

   publicclassStartup

   {

       public Startup(IHostingEnvironment env)

       {

            var builder = newConfigurationBuilder()

               .SetBasePath(env.ContentRootPath)

                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)

                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)

               .AddJsonFile("configuration.json")

                .AddEnvironmentVariables();

 

            Configuration = builder.Build();

       }

 

       // This method gets called by theruntime. Use this method to add services to the container.

       // For more information on how toconfigure your application, visithttps://go.microsoft.com/fwlink/?LinkID=398940

       publicvoid ConfigureServices(IServiceCollection services)

       {

            Action<ConfigurationBuilderCachePart> settings = (x) =>

            {

                x.WithMicrosoftLogging(log=>

                {

                    log.AddConsole(LogLevel.Debug);

                })

                .WithDictionaryHandle();

            };

 

           services.AddOcelotOutputCaching(settings);

           services.AddOcelotFileConfiguration(Configuration);

            services.AddOcelot();

       }

 

       publicIConfigurationRoot Configuration { get; }

 

       // This method gets called by theruntime. Use this method to configure the HTTP request pipeline.

       publicvoid Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactoryloggerFactory)

       {

           loggerFactory.AddConsole(Configuration.GetSection("Logging"));

            app.UseOcelot();

       }

    }

上面代码使用json配置作为Ocelot的配置,配置文件的详细说明参看https://github.com/TomPallister/Ocelot/blob/develop/configuration-explanation.txt,配置主要分两部分,路由和全局的配置:

路由是Ocelot的核心配置,Ocelot的核心功能就是将到达的Http请求转发给下游的服务,目前还只能是Http请求,将来支持其他任何传输机制。一条路由规则如下:

{

  "ReRoutes": [

   {

     "DownstreamPathTemplate": "/api/values",

     "DownstreamScheme": "http",

     "DownstreamHost": "localhost",

     "DownstreamPort": 9030,

     "UpstreamPathTemplate": "/api/values",

     "UpstreamHttpMethod": "Get",

     "FileCacheOptions": { "TtlSeconds": 15 }

   }

 ],

 

  "GlobalConfiguration": {

   "RequestIdKey": "OcRequestId",

   "ServiceDiscoveryProvider": {

     "Provider": "Consul",

     "Host": "localhost",

     "Port": 8500

   }

  }

}

DownstreamPathTemplate, DownstreamScheme,DownstreamPort 和DownstreamHost组成请求被转发的URL,UpstreamPathTemplate是Ocelot 用于识别转发请求,UpstreamHttpMethod用于识别相同的URL请求的不同操作。可以在模板里使用占位符{something},要同时出现在DownstreamPathTemplate 和UpstreamPathTemplate.。

Ocelot默认不区分大小写,可以在配置中指定区分大小写:

  "ReRouteIsCaseSensitive": true

        

RequestIdKey

除了记录访问记录外, 在处理每个用户请求过程中, 涉及业务逻辑处理和后端服务调用等, 对某些操作也需要记录相应日志和错误.对每个请求, 都生成一个唯一的requestID. 在这个请求的生命周期中, 所有打印的日志都会带上requestID信息,通过RequestI还可以构建Http请求路径,前端一个请求,可能要经过后端好几个服务,可以在http头上加上RequestId和RequestIndex,把这些服务串起来,例如 A->B->C,A服务调用B服务的时候,如果发现http head里面没有RequestId,则可以通过GuId生成一个,同时RequestIndex加1 ,B服务调用C服务端时候, 因为RequestId已经有了,就直接传递下去,同时RequestIndex加1 ,把这些信息记录到日志中,通过分析,整个调用就串起来了,通过完整的数据就可以绘制出整个服务的调用链路图。 

 

Ocelot从实现上来说就是一系列的中间件组合,在HTTP请求到达Ocelot,经过一系列的中间件的处理转发到下游的服务。

至此,一个ASP.NET Core 反向代理服务器就搭建完毕,下面我们使用Apache Bench对其性能做个简单的测试。我们模拟了1000 个用户,每次并发100个请求,以下是测试结果,机器的配置是CPU:Intel i5 2核, 4G内存的机器上测试:

D:\Workshop\ab>ab.exe -n 1000 -c 100 http://localhost:5800/api/values/

This isApacheBench, Version 2.3 <$Revision: 1757674 $>

Copyright 1996Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/

Licensed to TheApache Software Foundation, http://www.apache.org/

 

Benchmarkinglocalhost (be patient)

Completed 100requests

Completed 200requests

Completed 300requests

Completed 400requests

Completed 500requests

Completed 600requests

Completed 700requests

Completed 800requests

Completed 900requests

Completed 1000requests

Finished 1000requests

 

 

ServerSoftware:        Kestrel

ServerHostname:        localhost

ServerPort:            5800

 

DocumentPath:          /api/values/

DocumentLength:        19 bytes

 

ConcurrencyLevel:      100

Time taken fortests:   0.230 seconds

Completerequests:      1000

Failedrequests:        0

Totaltransferred:      178000 bytes

HTMLtransferred:       19000 bytes

Requests persecond:    4345.52 [#/sec] (mean)

Time perrequest:       23.012 [ms] (mean)

Time perrequest:       0.230 [ms] (mean, acrossall concurrent requests)

Transferrate:          755.37 [Kbytes/sec]received

 

Connection Times(ms)

              min  mean[+/-sd] median   max

Connect:        0   0   0.3      0      4

Processing:     5  21   9.6     19     66

Waiting:        4  18   9.8     17     65

Total:          6  21   9.6     20     66

 

Percentage ofthe requests served within a certain time (ms)

  50%    20

  66%    22

  75%    27

  80%    30

  90%    34

  95%    39

  98%    44

  99%    48

 100%    66 (longest request)

 

 可见,平均每秒请求数(吞吐率)为4345.52, 从不同分布下的请求数来看,响应时间也比较平稳, 相比直接访问接口的性能相差不大。

 

ab.exe -n 1000 -c 100http://localhost:9030/api/values/

This is ApacheBench, Version 2.3 <$Revision:1757674 $>

Copyright 1996 Adam Twiss, Zeus Technology Ltd,http://www.zeustech.net/

Licensed to The Apache Software Foundation,http://www.apache.org/

 

Benchmarking localhost (be patient)

Completed 100 requests

Completed 200 requests

Completed 300 requests

Completed 400 requests

Completed 500 requests

Completed 600 requests

Completed 700 requests

Completed 800 requests

Completed 900 requests

Completed 1000 requests

Finished 1000 requests

 

 

Server Software:        Kestrel

Server Hostname:        localhost

Server Port:            9030

 

Document Path:          /api/values/

Document Length:        19 bytes

 

Concurrency Level:      100

Time taken for tests:   0.200 seconds

Complete requests:      1000

Failed requests:        0

Total transferred:      158000 bytes

HTML transferred:       19000 bytes

Requests per second:    5011.40 [#/sec] (mean)

Time per request:       19.954 [ms] (mean)

Time per request:       0.200 [ms] (mean, across all concurrentrequests)

Transfer rate:          773.24 [Kbytes/sec] received

 

Connection Times (ms)

             min  mean[+/-sd] median   max

Connect:       0    0   0.3     0       1

Processing:    3   19   3.4    19      29

Waiting:       2   13   4.8    13      25

Total:         3   19   3.4    20      29

 

Percentage of the requests served within acertain time (ms)

 50%     20

 66%     20

 75%     21

 80%     21

 90%     22

 95%     23

 98%     25

 99%     25

 100%    29 (longest request)

 

         Asp.netcore的性能绝不亚于Nginx,但扩展性却远高于Nginx,我们可以动态的指定被代理的目标地址,而在Nginx中配置的目标地址是静态的,这一点对于我们将来实现服务发现功能及其重要,因为我们需要从ServiceRegistry中获取需要代理的微服务信息(例如IP和端口),并执行反向代理操作,调用相应的微服务REST API。

         以上实际上是服务网关的基础框架,将来我们会在此基础上进行扩展,实现一个具有反向代理和服务发现的服务网关。当然服务网关并非仅提供反向代理与服务发现特性,此外还需要具备安全认证、性能监控、数据缓存、请求分片、静态响应等众多特性,我们可以根据实际情况进行扩展。