您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關(guān)如何在C# 中使用Consul 發(fā)現(xiàn)服務(wù),文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。
webapi 從json中讀取配置信息 ConsulDiscoveryOptions;
如果自己是一個服務(wù), 則將自己注冊到consul中并設(shè)置健康檢查Url;
ConsulDiscovery.HttpClient 內(nèi)有一個consul client 定時刷新所有服務(wù)的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服務(wù)器地址 ConsulDiscoveryOptions.ConsulServerSetting.IP 不能為空"; } if (consulDiscoveryOptions.ServiceRegisterSetting != null) { var registerSetting = consulDiscoveryOptions.ServiceRegisterSetting; if (string.IsNullOrWhiteSpace(registerSetting.ServiceName)) { return "服務(wù)名稱 ConsulDiscoveryOptions.ServiceRegisterSetting.ServiceName 不能為空"; } if (string.IsNullOrWhiteSpace(registerSetting.ServiceIP)) { return "服務(wù)地址 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出錯, 則不更新服務(wù)列表. 繼續(xù)使用以前獲取到的服務(wù)列表 // 但是如果很長時間都不能連接consul, 服務(wù)列表里的一些實例已經(jīng)不可用了, 還一直提供這樣舊的列表也不合理, 所以要不要在這里實現(xiàn) 健康檢查? 這樣的話, 就得把檢查地址變成不能設(shè)置的 return; } // 1. 更新服務(wù)列表 // 2. 如果這個程序提供了服務(wù), 還要檢測 服務(wù)Id 是否在服務(wù)列表里 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 增加了一個 HelloController, 提供 SayHelloService 服務(wù), 并把自己注冊到Consul.
當我們訪問這個WebApi的 /WeatherForecast 時, 其Get()方法會訪問 http://SayHelloService/Hello/NetCore, 這就相當于一次遠程調(diào)用, 只是調(diào)用的就是這個WebApi的/Hello/NetCore
1. appsettings.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 相關(guān)配置 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. 修改WeatherForecast
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. 啟動 WebApplication1 并訪問 http://localhost:5000/weatherforecast
關(guān)于如何在C# 中使用Consul 發(fā)現(xiàn)服務(wù)就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。