マイクロサービス入門.3


目次

  • What are we trying to solve?
  • The solution: Service Registry & Health Checks

  • Show Me the Code
  • Service Registry
  • Health Checks
  • Bibliography
  • 前回私たちは話しました.今日、私たちは、ゲートウェイとお互いに非常に密接に関連している2つのパターンについて話します:サービスレジストリと健康チェック.

    何を解決しようとしているか。


    APIゲートウェイを作成したとき、サービスの場所(ホスト+ポート)を手動で定義しなければなりませんでした.しかし、以下のシナリオを想像してください:与えられたサービスはX軸で縮小される必要があります.新しいインスタンスは異なる場所を持ち、クライアントに動的に利用できるようにしなければなりません.
    また、我々のサービスインスタンスのうちの1つがどんな点でも利用できなくなるなら、ゲートウェイはそれが利用できない間、それがそのインスタンスにルート要求をすることができないのを意識しなければなりません.

    ソリューション:サービスレジストリ&ヘルスチェック

    To solve our problems, we'll be using a combination of two patterns. First, let's discuss the Service Registry pattern.

    The Service Registry is, as the name suggests, another service on our ecosystem, that is responsible to register all the services/instances of our application, and keep track of their location. By doing that, our Gateway can, any time that a request arrives, check with the Registry and route the request accordingly. This makes it invisible to the client which instance will receive its request and, as the instances change, the Gateway can always know valid locations! This is known as Server-side discovery.


    図1.サーバ側発見
    また、サービスが互いに協力しなければならないので、彼らも、他のサービスインスタンスの位置を見つけるためにサービスレジストリにアクセスすることができます.これはクライアント側の発見として知られています.

    図2.クライアント側発見

    サービスレジストリの利点

  • すべてのサービスの/インスタンスの場所を動的に発見するAPIゲートウェイを可能にする
  • サービスレジストリの欠点

  • サービス・レジストリがインフラストラクチャの一部でない限り、それはまだインストールされなければならないもう一つの構成要素であり、構成されたEを維持しました
  • サービス登録はアプリケーションの重要な部分であるので、それは高可用性を持たなければなりません.それが利用できなくなるならば、ゲートウェイは要求をルータに与えることができません.
  • しかし、登録されたインスタンスが要求を受けることができなくなるならば、どうですか?我々は、それに何かをルートするべきではない、右?これは、我々の健康チェックパターンが遊びに来るところです.我々のサービス/インスタンスを登録するように、我々はまた、登録されたインスタンスがリクエストを受け取ることができるかどうかチェックするためにサービスレジストリに方法を提供します.インスタンスが'不健康'とみなされるならば、我々のゲートウェイはそれにどんな要請も送りません!それは素晴らしいです!

    健康チェックの利益

  • これは、サービス/インスタンスの健康を定期的にチェックする方法を作成します.
  • 健康診断の欠点

  • チェックは十分に包括的でないかもしれません、あるいは、要求は健康チェックの間で失敗するかもしれません.
  • コードを見せてください

    So, let's begin with the code needed to implement our Service Registry, service registration and health checks.

    サービスレジストリ

    We'll begin by going the Consul.io ウェブサイトとダウンロード.Consulは私たちのサービスレジストリとして機能します.このチュートリアルの目的のために、開発者モードでconsulを実行します.consulをダウンロードした後、システムパスに追加することもできます.
    consul agent --dev
    
    当社のサービスレジストリには、それです.もう走っている.あなたがアクセスしてそれをチェックすることができますhttps://localhost:8500/ .
    さて、我々のゲートウェイを調整するために移動しましょうので、それは有効なサービス/インスタンスの場所のための私達のレジストリをチェックします.我々は、オセロットを編集することから始めます.JSONファイル
    {
      "Routes": [
        "DownstreamPathTemplate": "/api/v1/{everything}",
        "DownstreamScheme": "https",
        "UpstreamPathTemplate": "/api/gateway/{everything}",
        "UpstreamHttpMethod": [ "GET", "POST", "PUT", "PATCH", "DELETE" ],
        "ServiceName": "YOUR-SERVICE-NAME"
      ],
      "GlobalConfiguration": {
        "ServiceDiscoveryProvider": {
          "Host": "localhost",
          "Port": 8500,
          "Type": "Consul"
        }
      }
    }
    
    ご覧のように、「DownStreamHorestPorts」プロパティを削除し、「ServiceName」プロパティーをルートに追加しました.次に、「ServiceDiscoveryProvider」プロパティーを追加し、ホスト名、ポート、およびサービスの種類を入力します.この設定を完了するには、プロジェクトに別のパッケージを追加する必要があります.
    dotnet add package Ocelot.Provider.Consul
    
    インストール後、「起動」csファイルをもう一度編集し、次のように追加します.
    // Startup.cs
    public void ConfigureServices(IServiceCollection services)
    {
      // ...
      services.AddOcelot().AddConsul();
      // ...
    }
    
    これはゲートウェイの設定です.さて、サービスを設定する必要があります.彼らは領事と登録する必要がありますか?それが我々が次にすることです.
    登録したいサービスについては、consulパッケージを追加しなければなりません.
    dotnet add package Consul
    
    その後、何かをする必要があります.最初に、私たちの“appsettings . json”にいくつかの必要な設定を追加する必要があります.
    "ConsulConfig": {
      "Host": "http://localhost:8500",
      "ServiceName": "YOUR-SERVICE-NAME"
      "ServiceId": "AN-UNIQUE-ID-FOR-EACH-INSTANCE"
    }
    
    重要:あなたは“ocelot . json”ファイルのルーティングで同じ“ServiceName”を使用する必要があります.また、同じサービスの複数のインスタンスを使用する場合は「サービスID」が必要です.
    その後、我々はWeb API上でサービスとしてConsul登録を追加する必要があります.
    public static IServiceCollection AddConsulConfig(
        this IServiceCollection services,
        IConfiguration configuration
    )
    {
        services.AddSingleton<IConsulClient, ConsulClient>(
            p => new ConsulClient(
            consulConfig =>
            {
                var address = configuration.GetValue<string>(
                    "ConsulConfig:Host"
                );
                consulConfig.Address = new Uri(address);
            }
            )
        );
    
        return services;
    }
    
    これはConsulクライアントインスタンスをWeb APIに追加します.さて、クライアントインスタンスを使用してConsulで登録を行う必要があります.
    public static IApplicationBuilder UseConsul(
        this IApplicationBuilder app,
        IConfiguration configuration
    )
    {
        var consulClient = app.ApplicationServices
            .GetRequiredService<IConsulClient>();
    
        var logger = app.ApplicationServices
            .GetRequiredService<ILoggerFactory>()
            .CreateLogger("AppExtensions");
    
        var lifetime = app.ApplicationServices
            .GetRequiredService<IApplicationLifetime>();
    
        if (!(app.Properties["server.Features"] is FeatureCollection features))
        {
            return app;
        }
    
        var addresses = features.Get<IServerAddressesFeature>();
        var address = addresses.Addresses.First();
    
        Console.WriteLine($"address={address}");
    
        var serviceName = configuration.GetValue<string>(
            "ConsulConfig:ServiceName"
        );
    
        var serviceId = configuration.GetValue<string>(
            "ConsulConfig:ServiceId"
        );
    
        var uri = new Uri(address);
    
        var registration = new AgentServiceRegistration()
        {
            ID = serviceId,
            Name = serviceName,
            Address = $"{uri.Host}",
            Port = uri.Port
        };
    
        logger.LogInformation("Registering with Consul");
    
        consulClient.Agent
            .ServiceDeregister(registration.ID)
            .ConfigureAwait(true);
    
        consulClient.Agent
            .ServiceRegister(registration)
            .ConfigureAwait(true);
    
        lifetime.ApplicationStopping.Register(() =>
        {
            logger.LogInformation("Unregistering from Consul");
            consulClient.Agent.ServiceDeregister(registration.ID)
                .ConfigureAwait(true);
        });
    
        return app;
    }
    
    上記のコードは、同じIDで登録された以前のインスタンスを削除し、サービス/インスタンスを登録します.最後に、シャットダウンしたときに、サービス/インスタンスをデバッグする必要があることをWeb APIに伝えます.
    そして、我々はしました!現在、我々のサービスはConsulで登録して、APIゲートウェイに利用可能になりました.健康診断を実施し始めましょう!

    健康チェック

    To add our health check, we first need to install the "AspNetCore.HealthChecks.System" package. This will add some basic health check features. If you need to check the connection to your database, you'll need another package. In my repository, I used PostgreSQL, so, in my case, I needed to add the "AspNetCore.HealthChecks.NpgSql" package.

    dotnet add package AspNetCore.HealthChecks.System
    
    dotnet add package AspNetCore.HealthChecks.NpgSql
    

    After the installation, we'll add the following code to our "ConfigureServices" method:

    // Startup.cs
    public void ConfigureServices(IServiceCollection services)
    {
      // ...
      var builder = services.AddHealthChecks();
    
      builder.AddProcessAllocatedMemoryHealthCheck(
          500 * 1024 * 1024,
          "Process Memory",
          tags: new[] { "self" }
      );
    
      builder.AddPrivateMemoryHealthCheck(
          500 * 1024 * 1024,
          "Private memory",
          tags: new[] { "self" }
      );
    
      builder.AddNpgSql(
          Configuration.GetConnectionString("DefaultConnection"),
          tags: new[] { "service" }
      );
      // ...
    }
    

    We've just added some checks for the amount of memory our Web API is consuming (the limit is 500MB for both "Process Memory" and "Private Memory"), and a check for our database connection.

    Finally, we need to create some endpoints to access this information. So, we'll add the following to our "Configure" method:

    // Startup.cs
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      app.UseHealthChecks(
        "/health",
        new HealthCheckOptions()
        {
          AllowCachingResponses = false,
          Predicate = r => r.Tags.Contains("self"),
          ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
        }
      );
    
      app.UseHealthChecks(
        "/ready",
        new HealthCheckOptions()
        {
          AllowCachingResponses = false,
          Predicate = r => r.Tags.Contains("service"),
          ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
        }
      );
      // ...
    }
    

    We've just added the endpoints "/health" and "/ready" to our Web API. Now, we need to tell our Service Registry that it should perform checks on the endpoints we've just created.

    Let's go back to our "UseConsul" method and add the following:

    var registration = new AgentServiceRegistration()
    {
        ID = serviceId,
        Name = serviceName,
        Address = $"{uri.Host}",
        Port = uri.Port,
        Checks = [
          new AgentServiceCheck()
          {
            Name = configuration.GetValue<string>("ConsulConfig:ServiceName"),
            HTTP = $"https://{uri.Host}:{uri.Port}/health",
            Interval = TimeSpan.FromSeconds(30),
          },
          new AgentServiceCheck()
          {
            Name = configuration.GetValue<string>("ConsulConfig:ServiceName"),
            HTTP = $"https://{uri.Host}:{uri.Port}/ready",
            Interval = TimeSpan.FromSeconds(30),
          },
        ],
    };
    

    That's it! Now our services can register and tell the Service Registry about their health checks. Consul will perform the checks every 30 seconds and, if the check fails, the service will be marked as 'unhealthy' and won't receive any requests until it resolve its problems.

    IMPORTANT: if your Consul instance can't perform the checks, try changing the 'https' to 'http' and the port to the 'http' port!

    The next topic of the series is . Until next time!

    参考文献

  • Microservices Patterns: Examples with Java - Chris Richardson
  • Service registry pattern - https://microservices.io/patterns/service-registry.html
  • クライアント側サービス発見パターンhttps://microservices.io/patterns/client-side-discovery.html
  • サーバ側サービス発見パターンhttps://microservices.io/patterns/server-side-discovery.html
  • Hashicorpによる領事https://www.consul.io/
  • 健康チェックhttps://microservices.io/patterns/observability/health-check-api.html
  • 健康チェック.ネットコアhttps://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0