• net core体系-API-1Ocelot-(3)简单项目实战


    首先,让我们简单了解下什么是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:DemoRichCodeBoxAPIGatewayAppAPIGatewayApp.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上克隆源代码,查看。

    找到触发错误的地方,

    复制代码
    private static void AddIdentityServer(this IServiceCollection services, IIdentityServerConfiguration identityServerConfiguration, IConfigurationRoot configurationRoot) 
            {
                services.TryAddSingleton<IIdentityServerConfiguration>(identityServerConfiguration);
                services.TryAddSingleton<IHashMatcher, HashMatcher>();
                var identityServerBuilder = services
                    .AddIdentityServer(o => {
                        o.IssuerUri = "Ocelot";
                    })
                    .AddInMemoryApiResources(Resources(identityServerConfiguration))
                    .AddInMemoryClients(Client(identityServerConfiguration))
                    .AddResourceOwnerValidator<OcelotResourceOwnerPasswordValidator>();
    
                //todo - refactor a method so we know why this is happening
                var whb = services.First(x => x.ServiceType == typeof(IWebHostBuilder));//这个地方触发了错误
                var urlFinder = new BaseUrlFinder((IWebHostBuilder)whb.ImplementationInstance);
                var baseSchemeUrlAndPort = urlFinder.Find();
                JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
    
                services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                    .AddIdentityServerAuthentication(o =>
                    {
                        var adminPath = configurationRoot.GetValue("GlobalConfiguration:AdministrationPath", string.Empty);
                        o.Authority = baseSchemeUrlAndPort + adminPath;
                        o.ApiName = identityServerConfiguration.ApiName;
                        o.RequireHttpsMetadata = identityServerConfiguration.RequireHttps;
                        o.SupportedTokens = SupportedTokens.Both;
                        o.ApiSecret = identityServerConfiguration.ApiSecret;
                    });
    
                    //todo - refactor naming..
                    if (string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificateLocation) || string.IsNullOrEmpty(identityServerConfiguration.CredentialsSigningCertificatePassword))
                    {
                        identityServerBuilder.AddDeveloperSigningCredential();
                    }
                    else
                    {
                        //todo - refactor so calls method?
                        var cert = new X509Certificate2(identityServerConfiguration.CredentialsSigningCertificateLocation, identityServerConfiguration.CredentialsSigningCertificatePassword);
                        identityServerBuilder.AddSigningCredential(cert);
                    }
    复制代码
    var whb = services.First(x => x.ServiceType == typeof(IWebHostBuilder));

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

    把这句代码:

     var builder = new WebHostBuilder();

    改成:

    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

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

    IT黑马
  • 相关阅读:
    多线程
    JS实现隔行变色及鼠标滑过变色
    通用存储过程
    三级嵌套
    网页内容,图片及连接 抓取通用类(转)
    获取文件夹中的图片
    上传图片生成缩略图指定生成图片宽高度边框颜色和填充颜色
    图片上传类
    ROW_NUMBER存储过程分页
    OPENDATASOURCE 链接远程数据库
  • 原文地址:https://www.cnblogs.com/hmit/p/10757274.html
Copyright © 2020-2023  润新知