.Net Core 微服务学习(二): 服务治理发现(Consul)
Consul([ˈkɒnsl],康搜)是注册中心,服务提供者、服务消费者等都要注册到Consul中,这样就可以实现服务提供者、服务消费者的隔离。
除了Consul之外,还有Eureka、Zookeeper等类似软件。
COnsul主要功能如下
1.服务注册与发现
2.服务负载均衡
3.健康检查
用DNS举例来理解Consul。consul是存储服务名称与IP和端口对应关系的服务器。
假设:我有3台用于发帖的服务器,他们的IP和端口分别是
127.0.0.1:5726
127.0.0.1:5727
127.0.0.1:5728
那么这三台服务器就在Consul中注册,那么Consul就知道了这三台服务器的IP可端口了。当我们要发帖,想调用发帖服务的时候,就像Consul要,Consul会告诉我们,哪些服务器提供了发帖服务,然后我们自己选择一个发帖服务器就可以了。(是不是特别简单,这样就不需要我们记住有那些发帖服务器的IP地址和端口了)
Consul还提供一个健康检查的功能
假如有三台发帖的服务器都在Consul进行注册了发帖服务,假如有一台服务器挂了怎么办? 所有Consul就提供了一个服务器的健康检查功能,他会每隔一段时间向这三台服务器发送心跳包,比如每隔10秒钟就向这三台服务器请求一次,通过这样来检查这三台服务器是否还活着
一:Consul服务器安装与启动
https://www.consul.io/downloads.html
因为我的电脑是windows64位的,因为仅仅是测试,所有就下了一个windows版本的Consul
下载下来后,在运行中输入cmd 命令,然后在cmd中输入命令,执行cd命令切换到consul文件所在的盘符及文件夹
然后执行 consul.exe agent -dev
【这是开发环境测试,所有运行一台服务器就够了,因为开发环境无所谓稳定不稳定,但是如果是生产环境就要建集群,要至少一台Server,多台Agent】如果觉得搭建环境麻烦的话就去阿里云去买Consul服务吧,阿里云都给你配置好了,直接用就行
开发环境中consul重启后数据就会丢失。
consul的监控页面http://127.0.0.1:8500/
consult主要做三件事:提供服务到ip地址的注册;提供服务到ip地址列表的查询;对提供服务方的健康检查(HealthCheck);
二:.Net Core连接consul
在nuget管理器中执行:Install-Package Consul
三、 Rest服务的准备
1>创建一个Asp.Net Core Web应用程序(这个应用程序主要提供发送信息的服务,所有我取名叫MsgServer)
在程序中添加一个API控制器(这个控制器主要用于做服务器的健康检查,所有我取名叫HealthController)
using Microsoft.AspNetCore.Mvc; namespace MsgServer.Controllers { [Produces("application/json")] [Route("api/Health")] public class HealthController : Controller { //这个控制器主要用于对这个服务的健康检查,如果返回OK表示服务正常,没有挂掉 [HttpGet] public IActionResult Get() { Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "health check"); return Content("ok"); } } }
四、 服务注册到Consul 与 服务从Consul中注销
Program.cs文件
namespace MsgServer { public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) { //程序通过selfhost方式启动,可以设置这个args参数值。比如:dotnet test.dll --ip 127.0.0.1 --port 8080 //args就会接收到ip,port这些值。如果直接以IIS Express方式启动,这里好像是接收不到args参数的 var config = new ConfigurationBuilder().AddCommandLine(args).Build(); string ip = config["ip"]; string port = config["port"]; return WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() //.UseUrls("http://127.0.0.1:8080") //如果要在之类指定了URL地址,那么它的优先权最高 .UseUrls($"http://{ip}:{port}") .Build(); } } }
Startup.cs文件
using Consul; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace MsgServer { 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.AddMvc(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime appLifetime) { if (env.IsDevelopment()) { app.UseBrowserLink(); app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); string ip = Configuration["ip"]; string port = Configuration["port"]; string serviceName = "MsgService";//服务名称 string serviceId = serviceName + Guid.NewGuid();//服务编号 using (var consulClient = new ConsulClient(consulConfig)) { AgentServiceRegistration ars = new AgentServiceRegistration(); ars.Address = ip; //服务器的IP地址(必须能被调用者访问的IP地址,所有正式环境就不能用127.0.0.1) ars.Port = Convert.ToInt32(port); //服务器的端口号,必须能被调用者访问的的端口 ars.Name = serviceName;//服务器名称(同一个服务,服务名称都是一致的) ars.ID = serviceId; //服务器的编号(如果有5个发帖服务器,那么它们的服务器编号都是唯一的) ars.Check = new AgentServiceCheck() //健康检查 { DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5), //发现服务器挂了后,多久时间注销掉这个服务器 HTTP = $"http://{ip}:{port}/api/Health",//健康检查调用的控制器地址 Interval = TimeSpan.FromSeconds(10),//多久多一次健康检查(心跳检查) Timeout = TimeSpan.FromSeconds(5) }; consulClient.Agent.ServiceRegister(ars).Wait();//注意:Consult客户端的所有方法几乎都是异步方法,但是都没按照规范加上Async后缀,所以容易误导。记得调用后要Wait()或者await }; //服务器正常退出的时候的注册事件,这个注册事件就是从Consul注销服务(它是调用的Register方法进行事件注册的) //它需要通过方法参数注入IApplicationLifetime对象,从这个对象中可以知道,服务器是否退出 appLifetime.ApplicationStopped.Register(() => { using (var consulClient = new ConsulClient(consulConfig)) { Console.WriteLine("应用退出,开始从consul注销"); consulClient.Agent.ServiceDeregister(serviceId).Wait();//即注销服务编号为serviceId的服务器 } }); //appLifetime.ApplicationStarted.Register(() => { });//服务启动的注册事件 //appLifetime.ApplicationStopping.Register(() => { }); //服务正在启动中的注册事件 } private void consulConfig(ConsulClientConfiguration c) { c.Address = new Uri("http://127.0.0.1:8500"); //Consul服务的地址,Consul默认端口号就是8500 c.Datacenter = "dc1"; } } }
启动项目
此处采用 命令行启动的方式 我们只有一套代码 要启动多个实例
命令如下
dotnet MicroService.dll --urls="http://*:5726" --ip="127.0.0.1" --port=5726
此处指定ip 和端口 启动服务实例
服务的调用
1>创建一个控制台项目(获取其他项目)我取名叫ClientProject
2>在项目中安装Consul
using Consul; using System; namespace ClientProject { class Program { static void Main(string[] args) { using (var consulClient = new ConsulClient(r => r.Address = new Uri("http://127.0.0.1:8500"))) { //获取到在Consul中注册的所有服务 var services = consulClient.Agent.Services().Result.Response; foreach (var service in services.Values) { //我们获取到所有的服务器后,自己决定要连接那台服务器(我们可以随机的选择一条服务器,或则采用轮询机制,或其他的方式,总之是达到负载均衡的效果) Console.WriteLine($"id={service.ID},name={service.Service},ip={service.Address},port={service.Port}"); } Console.ReadKey(); } } } }