本系列编写目的纯属个人开发记录 以下代码均为demo级 如有需要 请自行优化 代码完整包由于公司电脑加密 无法上传整包的demo文件
consul 开发环境简易处理
consul 下载地址 : https://www.consul.io/downloads.html
此处使用windows 64版本
为方便使用在创建一个bat文件 命令如下:
cd C:UsersLenovoDesktop
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:ServerApiOcelotGetWayDemoApi_IIinDebug etcoreapp2.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