C\#HttpClient 는 Consult 발견 서 비 스 를 어떻게 사용 합 니까?

Overt.Core.Grpc 를 사용 하여 GRPC 의 사용 을 WCF 처럼 개조 하 였 으 며,성능 테스트 도 매우 좋 으 니,여러분 이 사용 하 시 는 것 을 매우 추천 합 니 다.
그러나 기 존 프로젝트 는 대부분 http 요청 이 었 습 니 다.GRPC 로 개조 하면 작업량 이 많 았 습 니 다.그래서 Steeltoe.Discovery 를 찾 았 습 니 다.Startup 에서 HttpClient 에 Delegating Handler 를 추가 하고 요청 url 의 host 와 port 를 동적 으로 변경 하여 http 요청 을 consul 이 발견 한 서비스 사례 를 가리 키 면 서비스의 동적 발견 을 실현 할 수 있 습 니 다.
성능 테스트 를 통 해 Steeltoe.Discovery 는 Overt.Core.Grpc 의 20%밖 에 되 지 않 아 받 아들 이기 가 매우 어 려 웠 습 니 다.그래서 자신 은 consul 기반 서비스 발견 도 구 를 실 현 했 습 니 다.네,이름 이 너무 어렵 습 니 다.ConsultDiscovery.HttpClient 로 잠 정적 으로 정 하 세 요.
기능 은 매우 간단 하 다.
  • webapi 는 json 에서 설정 정 보 를 읽 습 니 다.ConsultDiscovery Options;
  • 자신 이 서비스 라면 consul 에 등록 하고 건강 검진 Url 을 설치한다.
  • ConsultDiscovery.HttpClient 에 consul client 가 정시 에 모든 서비스의 url 방문 주 소 를 새로 고 칩 니 다.
  • 핵심 을 비교 하 는 두 종류
    
    using Consul;
    using Microsoft.Extensions.Options;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    
    namespace ConsulDiscovery.HttpClient
    {
      public class DiscoveryClient : IDisposable
      {
        private readonly ConsulDiscoveryOptions consulDiscoveryOptions;
        private readonly Timer timer;
        private readonly ConsulClient consulClient;
        private readonly string serviceIdInConsul;
    
        public Dictionary<string, List<string>> AllServices { get; private set; } = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
    
    
        public DiscoveryClient(IOptions<ConsulDiscoveryOptions> options)
        {
          consulDiscoveryOptions = options.Value;
          consulClient = new ConsulClient(x => x.Address = new Uri($"http://{consulDiscoveryOptions.ConsulServerSetting.IP}:{consulDiscoveryOptions.ConsulServerSetting.Port}"));
          timer = new Timer(Refresh);
    
          if (consulDiscoveryOptions.ServiceRegisterSetting != null)
          {
            serviceIdInConsul = Guid.NewGuid().ToString();
          }
        }
    
        public void Start()
        {
          var checkErrorMsg = CheckParams();
          if (checkErrorMsg != null)
          {
            throw new ArgumentException(checkErrorMsg);
          }
          RegisterToConsul();
          timer.Change(0, consulDiscoveryOptions.ConsulServerSetting.RefreshIntervalInMilliseconds);
        }
    
        public void Stop()
        {
          Dispose();
        }
    
        private string CheckParams()
        {
          if (string.IsNullOrWhiteSpace(consulDiscoveryOptions.ConsulServerSetting.IP))
          {
            return "Consul      ConsulDiscoveryOptions.ConsulServerSetting.IP     ";
          }
    
          if (consulDiscoveryOptions.ServiceRegisterSetting != null)
          {
            var registerSetting = consulDiscoveryOptions.ServiceRegisterSetting;
            if (string.IsNullOrWhiteSpace(registerSetting.ServiceName))
            {
              return "     ConsulDiscoveryOptions.ServiceRegisterSetting.ServiceName     ";
            }
            if (string.IsNullOrWhiteSpace(registerSetting.ServiceIP))
            {
              return "     ConsulDiscoveryOptions.ServiceRegisterSetting.ServiceIP     ";
            }
          }
          return null;
        }
    
        private void RegisterToConsul()
        {
          if (string.IsNullOrEmpty(serviceIdInConsul))
          {
            return;
          }
    
          var registerSetting = consulDiscoveryOptions.ServiceRegisterSetting;
          var httpCheck = new AgentServiceCheck()
          {
            HTTP = $"{registerSetting.ServiceScheme}{Uri.SchemeDelimiter}{registerSetting.ServiceIP}:{registerSetting.ServicePort}/{registerSetting.HealthCheckRelativeUrl.TrimStart('/')}",
            Interval = TimeSpan.FromMilliseconds(registerSetting.HealthCheckIntervalInMilliseconds),
            Timeout = TimeSpan.FromMilliseconds(registerSetting.HealthCheckTimeOutInMilliseconds),
            DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(10),
          };
          var registration = new AgentServiceRegistration()
          {
            ID = serviceIdInConsul,
            Name = registerSetting.ServiceName,
            Address = registerSetting.ServiceIP,
            Port = registerSetting.ServicePort,
            Check = httpCheck,
            Meta = new Dictionary<string, string>() { ["scheme"] = registerSetting.ServiceScheme },
          };
          consulClient.Agent.ServiceRegister(registration).Wait();
        }
    
        private void DeregisterFromConsul()
        {
          if (string.IsNullOrEmpty(serviceIdInConsul))
          {
            return;
          }
          try
          {
            consulClient.Agent.ServiceDeregister(serviceIdInConsul).Wait();
          }
          catch
          { }
        }
    
        private void Refresh(object state)
        {
          Dictionary<string, AgentService>.ValueCollection serversInConsul;
          try
          {
            serversInConsul = consulClient.Agent.Services().Result.Response.Values;
          }
          catch // (Exception ex)
          {
            //     consul  ,         .               
            //              consul,                 ,                ,                ?     ,               
            return;
          }
    
          // 1.       
          // 2.            ,        Id         
          var tempServices = new Dictionary<string, HashSet<string>>();
          bool needReregisterToConsul = true;
          foreach (var service in serversInConsul)
          {
            var serviceName = service.Service;
            if (!service.Meta.TryGetValue("scheme", out var serviceScheme))
            {
              serviceScheme = Uri.UriSchemeHttp;
            }
            var serviceHost = $"{serviceScheme}{Uri.SchemeDelimiter}{service.Address}:{service.Port}";
            if (!tempServices.TryGetValue(serviceName, out var serviceHosts))
            {
              serviceHosts = new HashSet<string>();
              tempServices[serviceName] = serviceHosts;
            }
            serviceHosts.Add(serviceHost);
    
            if (needReregisterToConsul && !string.IsNullOrEmpty(serviceIdInConsul) && serviceIdInConsul == service.ID)
            {
              needReregisterToConsul = false;
            }
          }
    
          if (needReregisterToConsul)
          {
            RegisterToConsul();
          }
    
          var tempAllServices = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
          foreach (var item in tempServices)
          {
            tempAllServices[item.Key] = item.Value.ToList();
          }
          AllServices = tempAllServices;
        }
    
    
        public void Dispose()
        {
          DeregisterFromConsul();
          consulClient.Dispose();
          timer.Dispose();
        }
      }
    }
    
    using System;
    using System.Net.Http;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsulDiscovery.HttpClient
    {
      public class DiscoveryHttpMessageHandler : DelegatingHandler
      {
        private static readonly Random random = new Random((int)DateTime.Now.Ticks);
    
        private readonly DiscoveryClient discoveryClient;
    
        public DiscoveryHttpMessageHandler(DiscoveryClient discoveryClient)
        {
          this.discoveryClient = discoveryClient;
        }
    
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
          if (discoveryClient.AllServices.TryGetValue(request.RequestUri.Host, out var serviceHosts))
          {
            if (serviceHosts.Count > 0)
            {
              var index = random.Next(serviceHosts.Count);
              request.RequestUri = new Uri(new Uri(serviceHosts[index]), request.RequestUri.PathAndQuery);
            }
          }
          return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
        }
      }
    }
    사용 방법
    간단 하기 위해 서 나 는 새로 만 든 WebApi 에 하 나 를 추가 했다. Hello Controller,Say Hello 서비스 서 비 스 를 제공 하고 자신 을 Consul 에 등록 합 니 다.
    우리 가 이 WebApi 의/Weather Forecast 를 방문 할 때,Get()방법 은http://SayHelloService/Hello/NetCore에 접근 합 니 다.이것 은 원 격 호출 에 해당 합 니 다.다만 이 WebApi 의/Hello/NetCore 를 호출 합 니 다.
      1. apptsettings.json 증가
    
    "ConsulDiscoveryOptions": {
      "ConsulServerSetting": {
       "IP": "127.0.0.1", //   
       "Port": 8500, //   
       "RefreshIntervalInMilliseconds": 1000
      },
      "ServiceRegisterSetting": {
       "ServiceName": "SayHelloService", //   
       "ServiceIP": "127.0.0.1", //   
       "ServicePort": 5000, //   
       "ServiceScheme": "http", //    http    https,   http, 
       "HealthCheckRelativeUrl": "/HealthCheck",
       "HealthCheckIntervalInMilliseconds": 500,
       "HealthCheckTimeOutInMilliseconds": 2000
      }
     }
    2.Startup.cs 수정
    
    using ConsulDiscovery.HttpClient;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using System;
    
    namespace WebApplication1
    {
      public class Startup
      {
        public Startup(IConfiguration configuration)
        {
          Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        public void ConfigureServices(IServiceCollection services)
        {
          services.AddControllers();
    
          //    ConsulDiscovery     
          services.AddConsulDiscovery(Configuration);
          //    SayHelloService  HttpClient
          services.AddHttpClient("SayHelloService", c =>
            {
              c.BaseAddress = new Uri("http://SayHelloService");
            })
            .AddHttpMessageHandler<DiscoveryHttpMessageHandler>();
        }
    
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime lifetime)
        {
          if (env.IsDevelopment())
          {
            app.UseDeveloperExceptionPage();
          }
    
          app.UseRouting();
    
          app.UseAuthorization();
    
          app.UseEndpoints(endpoints =>
          {
            endpoints.MapControllers();
          });
    
          //    ConsulDiscovery
          app.StartConsulDiscovery(lifetime);
        }
      }
    }
    3.HelloController 추가
    
    using Microsoft.AspNetCore.Mvc;
    
    namespace WebApplication1.Controllers
    {
      [ApiController]
      [Route("[controller]")]
      public class HelloController : ControllerBase
      {
        [HttpGet]
        [Route("{name}")]
        public string Get(string name)
        {
          return $"Hello {name}";
        }
      }
    }
    4.일기 예보 수정
    
    using Microsoft.AspNetCore.Mvc;
    using System.Net.Http;
    using System.Threading.Tasks;
    
    namespace WebApplication1.Controllers
    {
      [ApiController]
      [Route("[controller]")]
      public class WeatherForecastController : ControllerBase
      {
        private readonly IHttpClientFactory httpClientFactory;
    
        public WeatherForecastController(IHttpClientFactory httpClientFactory)
        {
          this.httpClientFactory = httpClientFactory;
        }
    
        [HttpGet]
        public async Task<string> Get()
        {
          var httpClient = httpClientFactory.CreateClient("SayHelloService");
          var result = await httpClient.GetStringAsync("Hello/NetCore");
          return $"WeatherForecast return:      {result}";
        }
      }
    }
    5.consul 시작
    
    consul agent -dev
    6.웹 애플 리 케 이 션 1 을 시작 하고 접근  http://localhost:5000/weatherforecast

    이상 의 예 는 도착 할 수 있다  https://github.com/zhouandke/ConsulDiscovery.HttpClient  다운로드 consul 시작:     consul agent -dev  End
    이상 은 C\#HttpClient 가 Consul 발견 서 비 스 를 어떻게 사용 하 는 지 에 대한 상세 한 내용 입 니 다.C\#HttpClient 가 Consul 발견 서 비 스 를 사용 하 는 지 에 관 한 자 료 는 저희 의 다른 관련 글 을 주목 하 세 요!

    좋은 웹페이지 즐겨찾기