本系列編寫目的純屬個人開發記錄 以下代碼均為demo級 如有需要 請自行優化 代碼完整包由於公司電腦加密 無法上傳整包的demo文件
consul 開發環境簡易處理
consul 下載地址 : https://www.consul.io/downloads.html
此處使用windows 64版本
為方便使用在創建一個bat文件 命令如下:
cd C:\Users\Lenovo\Desktop\
consul.exe agent -dev
第一行為進入桌面
第二行為 執行consul開發模式
運行后可在 localhost:8500 地址處看到consul 后台服務管理頁面
搭建測試API
由於 使用了consul 最新nuget包 所以 在創建的時候需要使用 .netcore 2.1
由於已經搭建了demo1 這里演示截圖會是demo2 創建方式一樣
創建一個core 2.1的API空項目
創建一個BaseController 用來保存配置信息

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; namespace DemoApi_I.Controllers { public class BaseController : ControllerBase { public static Setting Config; public BaseController(IOptions<Setting> setting) { Config = setting.Value; } } }
再添加一個健康檢查的控制器

using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; namespace DemoApi_II.Controllers { [Route("api/[controller]")] public class HealthController : BaseController { public HealthController(IOptions<Setting> setting) : base(setting) { } // GET api/values [HttpGet] public string Get() { return "ok"; } } }
為了統一顯示 可以更改默認values控制器為

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; namespace DemoApi_II.Controllers { [Route("api/[controller]")] [ApiController] public class ValuesController : BaseController { public ValuesController(IOptions<Setting> setting) : base(setting) { } // GET api/values [HttpGet] public string Get() { var aaa = Config.ServiceName; return "二號測試API"; } } }
接下來 增加 一個配置類

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace DemoApi_II { public class Setting { /// <summary> /// 端口號 /// </summary> public int Port { get; set; } /// <summary> /// 服務名稱 /// </summary> public string ServiceName { get; set; } /// <summary> /// 服務發現IP /// </summary> public string ConsulIP { get; set; } /// <summary> /// 服務發現端口號 /// </summary> public int ConsulPort { get; set; } } }
在appsettings.json 內新增節點
測試地址中只需要改端口號和服務名稱即可 會自動讀取本機ip到注入到consul中

"Setting": { "Port": "1001", "ServiceName": "demoAPi", "ConsulIP": "localhost", "ConsulPort": "8500" }
注意 需要修改為 始終復制或者較新復制
修改 Program.cs

using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace DemoApi_II { public class Program { public static string StartPort; public static void Main(string[] args) { var config = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true) .Build(); StartPort = config.GetSection("Setting")["Port"]; CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseUrls($"http://*:{StartPort}") .UseStartup<Startup>(); } }
修改startup.cs

using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ConsulRegisterHelper; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace DemoApi_II { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure<Setting>(Configuration.GetSection("Setting")); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseMvc(); app.RegisterConsul(lifetime, new ServiceEntity { IP = NetworkHelper.LocalIPAddress, Port = Convert.ToInt32(Configuration.GetSection("Setting")["Port"]), ServiceName = Configuration.GetSection("Setting")["ServiceName"], ConsulIP = Configuration.GetSection("Setting")["ConsulIP"], ConsulPort = Convert.ToInt32(Configuration.GetSection("Setting")["ConsulPort"]) }); } } }
創建自動注入consul服務的類庫
新建core2.1類庫項目 取名ConsulRegisterHelper
將以下3個類創建即可

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; namespace ConsulRegisterHelper { public class NetworkHelper { public static string LocalIPAddress { get { UnicastIPAddressInformation mostSuitableIp = null; var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (var network in networkInterfaces) { if (network.OperationalStatus != OperationalStatus.Up) continue; var properties = network.GetIPProperties(); if (properties.GatewayAddresses.Count == 0) continue; foreach (var address in properties.UnicastAddresses) { if (address.Address.AddressFamily != AddressFamily.InterNetwork) continue; if (IPAddress.IsLoopback(address.Address)) continue; return address.Address.ToString(); } } return mostSuitableIp != null ? mostSuitableIp.Address.ToString() : ""; } } public static int GetRandomAvaliablePort(int minPort = 1024, int maxPort = 65535) { Random rand = new Random(); while (true) { int port = rand.Next(minPort, maxPort); if (!IsPortInUsed(port)) { return port; } } } private static bool IsPortInUsed(int port) { IPGlobalProperties ipGlobalProps = IPGlobalProperties.GetIPGlobalProperties(); IPEndPoint[] ipsTCP = ipGlobalProps.GetActiveTcpListeners(); if (ipsTCP.Any(p => p.Port == port)) { return true; } IPEndPoint[] ipsUDP = ipGlobalProps.GetActiveUdpListeners(); if (ipsUDP.Any(p => p.Port == port)) { return true; } TcpConnectionInformation[] tcpConnInfos = ipGlobalProps.GetActiveTcpConnections(); if (tcpConnInfos.Any(conn => conn.LocalEndPoint.Port == port)) { return true; } return false; } } }

using Consul; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using System; namespace ConsulRegisterHelper { public static class AppBuilderExtensions { public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IApplicationLifetime lifetime, ServiceEntity serviceEntity) { var consulClient = new ConsulClient(x => x.Address = new Uri($"http://{serviceEntity.ConsulIP}:{serviceEntity.ConsulPort}"));//請求注冊的 Consul 地址 var httpCheck = new AgentServiceCheck() { DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服務啟動多久后注冊 Interval = TimeSpan.FromSeconds(3),//健康檢查時間間隔,或者稱為心跳間隔 HTTP = $"http://{serviceEntity.IP}:{serviceEntity.Port}{serviceEntity.HealthUrl}",//健康檢查地址 Timeout = TimeSpan.FromSeconds(3) }; // Register service with consul var registration = new AgentServiceRegistration() { Checks = new[] { httpCheck }, ID = Guid.NewGuid().ToString(), Name = serviceEntity.ServiceName, Address = serviceEntity.IP, Port = serviceEntity.Port, Tags = new[] { $"urlprefix-/{serviceEntity.ServiceName}" }//添加 urlprefix-/servicename 格式的 tag 標簽,以便 Fabio 識別 }; consulClient.Agent.ServiceRegister(registration).Wait();//服務啟動時注冊,內部實現其實就是使用 Consul API 進行注冊(HttpClient發起) lifetime.ApplicationStopping.Register(() => { consulClient.Agent.ServiceDeregister(registration.ID).Wait();//服務停止時取消注冊 }); return app; } } }

using System; using System.Collections.Generic; using System.Text; namespace ConsulRegisterHelper { public class ServiceEntity { public ServiceEntity() { HealthUrl = "/api/health"; } /// <summary> /// 服務IP /// </summary> public string IP { get; set; } /// <summary> /// 服務端口號 /// </summary> public int Port { get; set; } /// <summary> /// 服務名稱 /// </summary> public string ServiceName { get; set; } /// <summary> /// 服務發現地址 /// </summary> public string ConsulIP { get; set; } /// <summary> /// 服務發現端口號 /// </summary> public int ConsulPort { get; set; } /// <summary> /// 健康檢查地址默認為/api/health /// </summary> public string HealthUrl { get; set; } } }
在需要注入的服務Startup.cs中通過如下代碼注入:
創建bat文件方便測試使用

E: cd E:\ServerApi\OcelotGetWay\DemoApi_II\bin\Debug\netcoreapp2.1 dotnet DemoApi_II.dll
到此 測試demoapi 准備完成
參考內容:
微服務系列文章
https://www.cnblogs.com/edisonchou/p/dotnetcore_microservice_foundation_blogs_index_final.html
ocelot配置特性介紹
https://blog.csdn.net/qin_yu_2010/article/details/82749414