在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
Consul服务发现的使用方法:
本文的重点不是描述上述过程, 只是准备分享一下自己编写的服务注册代码, 虽然网上已经有不少类似的文章, 个人觉得自己的代码要智能一点
下面就是实现代码, 需要nuget Consul using Consul; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.Linq; public static class RegisterCansulExtension { public static void RegisterToConsul(this IApplicationBuilder app, IConfiguration configuration, IApplicationLifetime lifetime) { lifetime.ApplicationStarted.Register(() => { string serviceName = configuration.GetValue<string>("serviceName"); string serviceIP = configuration.GetValue<string>("serviceIP"); string consulClientUrl = configuration.GetValue<string>("consulClientUrl"); string healthCheckRelativeUrl = configuration.GetValue<string>("healthCheckRelativeUrl"); int healthCheckIntervalInSecond = configuration.GetValue<int>("healthCheckIntervalInSecond"); ICollection<string> listenUrls = app.ServerFeatures.Get<IServerAddressesFeature>().Addresses; if (string.IsNullOrWhiteSpace(serviceName)) { throw new Exception("Please use --serviceName=yourServiceName to set serviceName"); } if (string.IsNullOrEmpty(consulClientUrl)) { consulClientUrl = "http://127.0.0.1:8500"; } if (string.IsNullOrWhiteSpace(healthCheckRelativeUrl)) { healthCheckRelativeUrl = "health"; } healthCheckRelativeUrl = healthCheckRelativeUrl.TrimStart('/'); if (healthCheckIntervalInSecond <= 0) { healthCheckIntervalInSecond = 1; } string protocol; int servicePort = 0; if (!TryGetServiceUrl(listenUrls, out protocol, ref serviceIP, out servicePort, out var errorMsg)) { throw new Exception(errorMsg); } var consulClient = new ConsulClient(ConsulClientConfiguration => ConsulClientConfiguration.Address = new Uri(consulClientUrl)); var httpCheck = new AgentServiceCheck() { DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(10),//服务启动多久后注册 Interval = TimeSpan.FromSeconds(healthCheckIntervalInSecond), HTTP = $"{protocol}://{serviceIP}:{servicePort}/{healthCheckRelativeUrl}", Timeout = TimeSpan.FromSeconds(2) }; // 生成注册请求 var registration = new AgentServiceRegistration() { Checks = new[] { httpCheck }, ID = Guid.NewGuid().ToString(), Name = serviceName, Address = serviceIP, Port = servicePort, Meta = new Dictionary<string, string>() { ["Protocol"] = protocol }, Tags = new[] { $"{protocol}" } }; consulClient.Agent.ServiceRegister(registration).Wait(); //服务停止时, 主动发出注销 lifetime.ApplicationStopping.Register(() => { try { consulClient.Agent.ServiceDeregister(registration.ID).Wait(); } catch { } }); }); } private static bool TryGetServiceUrl(ICollection<string> listenUrls, out string protocol, ref string serviceIP, out int port, out string errorMsg) { protocol = null; port = 0; errorMsg = null; if (!string.IsNullOrWhiteSpace(serviceIP)) // 如果提供了对外服务的IP, 只需要检测是否在listenUrls里面即可 { foreach (var listenUrl in listenUrls) { Uri uri = new Uri(listenUrl); protocol = uri.Scheme; var ipAddress = uri.Host; port = uri.Port; if (ipAddress == serviceIP || ipAddress == "0.0.0.0" || ipAddress == "[::]") { return true; } } errorMsg = $"The serviceIP that you provide is not in urls={string.Join(',', listenUrls)}"; return false; } else // 没有提供对外服务的IP, 需要查找本机所有的可用IP, 看看有没有在 listenUrls 里面的 { var allIPAddressOfCurrentMachine = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces() .Select(p => p.GetIPProperties()) .SelectMany(p => p.UnicastAddresses) // 这里排除了 127.0.0.1 loopback 地址 .Where(p => p.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork && !System.Net.IPAddress.IsLoopback(p.Address)) .Select(p => p.Address.ToString()).ToArray(); var uris = listenUrls.Select(listenUrl => new Uri(listenUrl)).ToArray(); // 本机所有可用IP与listenUrls进行匹配, 如果listenUrl是"0.0.0.0"或"[::]", 则任意IP都符合匹配 var matches = allIPAddressOfCurrentMachine.SelectMany(ip => uris.Where(uri => ip == uri.Host || uri.Host == "0.0.0.0" || uri.Host == "[::]") .Select(uri => new { Protocol = uri.Scheme, ServiceIP = ip, Port = uri.Port }) ).ToList(); if (matches.Count == 0) { errorMsg = $"This machine has IP address=[{string.Join(',', allIPAddressOfCurrentMachine)}], urls={string.Join(',', listenUrls)}, none match."; return false; } else if (matches.Count == 1) { protocol = matches[0].Protocol; serviceIP = matches[0].ServiceIP; port = matches[0].Port; return true; } else { errorMsg = $"Please use --serviceIP=yourChosenIP to specify one IP, which one provide service: {string.Join(",", matches)}."; return false; } } } } 使用时可以提供5个参数, 只有serviceName是必须要由参数提供的, serviceIP会自动尽可能匹配出来, consulClientUrl默认是http://127.0.0.1:8500, healthCheckRelativeUrl默认是 /health, healthCheckIntervalInSecond默认是1秒
使用方法 public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddHealthChecks(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // consul的http check只需要返回是200, 就认为服务可用; 这里必须把Degraded改为503 app.UseHealthChecks("/Health", new HealthCheckOptions() { ResultStatusCodes = { [Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus.Healthy] = StatusCodes.Status200OK, [Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus.Degraded] = StatusCodes.Status503ServiceUnavailable, [Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable } }); app.UseMvc(); app.RegisterToConsul(Configuration, lifetime); } } 调试的时候, 也不能使用默认的 http://localhost:5000了
后记: 这段代码也是很久以前写的了, 后来发现Ocelot是个功能很丰富的网关, 帮我们做了很大部分工作, 实施微服务的基础功能就不再写了. 后来又学习了Kubernetes, 感觉这才是微服务的正道, 但Kubernetes更复杂, 对运维有更高的要求, 幸好各大云服务商都提供了Kubernetes的基础设施, 这样只需要开发人员提升开发方式即可 现在Service Mesh不断发展, 但还没形成统一的解决方案, 其中Consul也支持Mesh, 有时间再写吧
End
|
请发表评论