Consul是一个服务网格(微服务间的 TCP/IP,负责服务之间的网络调用、限流、熔断和监控)解决方案,它是一个一个分布式的,高度可用的系统,并且开发使用都很简便。它提供了一个功能齐全的控制平面,主要特色是:服务发现、健康检查、键值存储、安全服务通讯、多数据中心。除了 Consul 以外,还有 Eureka、Zookeeper 等相似软件。web
咱们这里是直接在Windows上开发,因此对应下载Windows
版本的。下载地址:浏览器
下载完成后实际就是consul.exe
,咱们在下载位置运行cmd命令网络
consul agent -dev
而后咱们打开浏览器,输入http://localhost:8500/app
能够看到Consul已经启动了,可是除了他本身外尚未其余服务注册进来。异步
咱们建立一个Api项目,好比订单服务OrderService
。分布式
安装Consul
微服务
建立完成后就是默认的项目结构,咱们添加一个健康检查的Controller。健康检查的意思是Consul会根据咱们的配置定时的去请求健康检查接口,判断当前服务是否是可用。避免提供挂掉的服务给消费者,固然间隔时间也会有,须要配合后面的熔断、降级使用。测试
using System; using Microsoft.AspNetCore.Mvc; namespace Consul.OrderService.Controllers { [ApiController] [Route("[controller]")] public class HealthController : ControllerBase { public IActionResult Get() { Console.WriteLine("调用了健康检查" + DateTime.Now); return Ok("OK"); } } }
调用时咱们输出下当前时间,能够看到Consul有没有调用健康检查。
由于这里须要指定Api的地址,因此咱们配置一下获取配置。
public static IHostBuilder CreateHostBuilder(string[] args) { var config = new ConfigurationBuilder().AddCommandLine(args).Build(); string ip = config["ip"]; string port = config["port"]; return Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>() .UseUrls($"http://{ip}:{port}"); }); }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,IHostApplicationLifetime applicationLifetime) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); string ip = Configuration["ip"]; int port = Convert.ToInt32(Configuration["port"]); string serveiceName = "OrderService"; string serviceId = serveiceName + Guid.NewGuid(); using (var consulClient = new ConsulClient(ConsulConfig)) { AgentServiceRegistration agentServiceRegistration = new AgentServiceRegistration(); //服务编号,不能重复,用 Guid 最简单 agentServiceRegistration.ID = serviceId; //服务的名字 agentServiceRegistration.Name = serveiceName; //服务提供者的能被消费者访问的 ip 地址(能够被其余应用访问的 地址,本地测试能够用 127.0.0.1,机房环境中必定要写本身的内网 ip 地址) agentServiceRegistration.Address = ip; // 服务提供者的能被消费者访问的端口 agentServiceRegistration.Port = port; agentServiceRegistration.Check = new AgentServiceCheck() { //服务中止多久 后反注册(注销) DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5), //健康检查时间间隔,或者称为心跳 间隔 Interval = TimeSpan.FromSeconds(10), //健康检查地址 HTTP = $"http://{ip}:{port}/health", Timeout = TimeSpan.FromSeconds(5) }; //注册服务到 Consul consulClient.Agent.ServiceRegister(agentServiceRegistration).Wait();//Consult 客户端的全部方法几乎都是异步方法,可是都没按照规范加上 Async 后缀,因此容易误导。记得调用后要 Wait()或者 await } //程序正常退出的时候从 Consul 注销服务 ////要经过方法参数注入 IHostApplicationLifetime applicationLifetime.ApplicationStopped.Register(() => { using (var consulClient = new ConsulClient(ConsulConfig)) { Console.WriteLine($"应用退出,开始从consul注销{serveiceName}"); consulClient.Agent.ServiceDeregister(serviceId).Wait(); } }); } private void ConsulConfig(ConsulClientConfiguration configuration) { configuration.Address = new Uri("http://127.0.0.1:8500/"); configuration.Datacenter = "dc1"; }
咱们生成下Api项目,用命令启动
dotnet Consul.OrderService.dll --ip localhost --port 5000
打开Consul看一下,已经能够看到OrderService
已经注册好了。并且是有健康检查的。
咱们建立一个控制台程序,并安装Consul
。
修改下Program.cs
class Program { static void Main(string[] args) { using (var consulClient = new ConsulClient(c => { c.Address = new Uri("http://127.0.0.1:8500"); })) { var services = consulClient.Agent.Services().Result.Response; foreach (var agentService in services.Values) { Console.WriteLine($"id={agentService.ID},name={agentService.Service},ip={agentService.Address},port={agentService.Port}"); } } Console.ReadKey(); } }
因为刚才咱们只开了一个OrderService
,如今咱们多打开几个。
dotnet Consul.OrderService.dll --ip localhost --port 5001 dotnet Consul.OrderService.dll --ip localhost --port 5002 dotnet Consul.OrderService.dll --ip localhost --port 5003
咱们能够看到4个服务都有健康检查,而且Consul也能够看到。
咱们启动下控制台应用程序
能够看到咱们注册的4个服务都是能够获取到的,那么咱们随便请求一个试一下。
static void Main(string[] args) { using (var consulClient = new ConsulClient(c => { c.Address = new Uri("http://127.0.0.1:8500"); })) { var services = consulClient.Agent.Services().Result.Response.Values.Where(s => s.Service.Equals("OrderService", StringComparison.OrdinalIgnoreCase)); if (!services.Any()) { Console.WriteLine("找不到服务的实例"); } else { //随机获取 var service = services.ElementAt(Environment.TickCount % services.Count()); using (HttpClient client=new HttpClient()) { var result= client.GetAsync(new Uri($"http://{service.Address}:{service.Port}/WeatherForecast")).Result; Console.WriteLine(result.Content.ReadAsStringAsync().Result); } } } Console.ReadKey(); }