简单理解:
服务多的时候,服务地址都是写固定,增加一个地址,配置一次,配置nginx或者其他配置,麻烦
有了这个就可以通过应用服务上报服务名servicename和访问地址,同一个服务名servicename可以有多个节点,多个地址,通过consul就可以只根据一个服务名servicename,找到所有这个服务的地址,省事,并可以自动检查这个服务是否掉线
一、什么是consul?
首先我们来了解什么是consul,consul是服务注册与发现的一种常用工具之一,翻阅了网上的部分资料,指把服务地址注册到consul,然后在consul中读取来消费;但在我的理解中,consul的作用是服务治理,属于可以横向伸缩的注册中心;为什么这么说呢?在以前我们用Nginx做负载时,需要把服务地址一个个手动集成进去的,如果服务多了会有什么问题呢?1.需要新增一个服务,必须要重新配置Nginx然后重启;2.感知不了哪个服务停止了;3.如果某个服务停止了,无法自动创建新的服务代替;因为微服务其中一个特点是基础设施自动化,所以明显Nginx满足不了需求,那么服务注册与发现就横空而生了(除了consul外还可以使用etcd,K8S Services等)。
二、consul有几大核心功能:
1.服务注册
其实就是把需要使用的服务注册到注册中心(consul的注册方式是已服务主动推送给consul客户端->服务端),其他客户端可以使用 Consul 发现给定服务的提供者。使用 DNS 或 HTTP,应用程序可以轻松找到它们所依赖的服务(它是通过代理注册的方式注册到注册中心,提供业务代码的解耦)
什么是服务发现?
服务发现可以让一个应用或者组件发现其运行环境以及其它应用或组件的信息。用户配置一个服务发现工具就可以将实际容器跟运行配置分离开。常见配置信息包括:ip、端口号、名称等。
当一项服务存在于多个主机节点上时,client端如何决策获取相应正确的IP和port。
在传统情况下,当出现服务存在于多个主机节点上时,都会使用静态配置的方法来实现服务信息的注册。
而当在一个复杂的系统里,需要较强的可扩展性时,服务被频繁替换时,为避免服务中断,动态的服务注册和发现就很重要。
图中,客户端的一个接口,需要调用服务A-N。客户端必须要知道所有服务的网络位置的,以往的做法是配置是配置文件中,或者有些配置在数据库中。这里就带出几个问题:
需要配置N个服务的网络位置,加大配置的复杂性
服务的网络位置变化,都需要改变每个调用者的配置
集群的情况下,难以做负载(反向代理的方式除外)
总结起来一句话:服务多了,配置很麻烦,问题多多
既然有这些问题,那么服务发现就是解决这些问题的。话说,怎么解决呢?我们再看一张图
与之前一张不同的是,加了个服务发现模块。图比较简单,这边文字描述下。服务A-N把当前自己的网络位置注册到服务发现模块(这里注册的意思就是告诉),服务发现就以K-V的方式记录下,K一般是服务名,V就是IP:PORT。服务发现模块定时的轮询查看这些服务能不能访问的了(这就是健康检查)。客户端在调用服务A-N的时候,就跑去服务发现模块问下它们的网络位置,然后再调用它们的服务。这样的方式是不是就可以解决上面的问题了呢?客户端完全不需要记录这些服务网络位置,客户端和服务端完全解耦!
2.健康检查
其实就是在程序中,建立一个接口,只返回状态使用,consul通过这个接口来检查服务是否健康,如果不健康,则通过策略去移除
3.多个数据中心
其实就是支持集群,通过3个,或5个的单数服务端的consul来支持高可用(因为单数更适合做选举算法,如果数量太多又影响性能,所以这是官网标配);
4.安全服务通信
Consul 可以为服务生成和分发 TLS 证书以建立相互 TLS 连接
三、consul安装
安装需要到官网下载安装包
小白注意:在未使用过的时候,我还以为通过nuget引用,原来是通过安装的,像mq安装那样,不需要写代码运行
安装分2种,一种是二进制,以命令行的形式安装;一种是以exe文件免安装的形式;
安装下载地址:https://developer.hashicorp.com/consul/downloads
四、运行consul服务
运行时,最可能出现的问题就是端口占用,可新增文件,修改文件端口号,然后执行命令
进入安装包的下载目录,然后运行命令:
consul agent -dev -client=0.0.0.0
window下打开 cmd.exe
然后打开浏览器,默认端口是8500,浏览:http://localhost:8500 可以打开表示安装consul成功了
五、新建应用项目webAdmin 并自动应用注册到consul
1.服务注册代码,目的是把当前服务推送到consul,并且设置健康检查
依赖组件
consul
新增注册类RegToConsul:
/// <summary>
/// 把本服务注册到Consul
/// </summary>
/// <param name="config">参数配置</param>
/// <param name="appLifetime">程序生命周期</param>
public static void RegToConsul(this IConfiguration config)
{
//Consul地址
var address = appsettingHelper.GetSettings("Consul:consulAddress");// AppHelper.ReadAppSettings("Consul", "consulAddress");
var consulClient = new ConsulClient(p => { p.Address = new Uri(address); });
//本地IP
var localIP = appsettingHelper.GetSettings("Consul:currentIp");// AppHelper.ReadAppSettings("Consul", "currentIp");
//本地服务端口
var localPort = Convert.ToInt32(appsettingHelper.GetSettings("Consul:currentPort")); //端口号从命令行参数获取(注:目前没找到直接获取本服务监听的端口的方法)
//心跳检测设置
var httpCheck = new AgentServiceCheck()
{
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(60), //心跳检测失败多久后注销
Interval = TimeSpan.FromSeconds(10), //间隔多久心跳检测一次
HTTP = $"http://{localIP}:{localPort}/webadmin/Health/Check", //心跳检查地址,本服务提供的地址
Timeout = TimeSpan.FromSeconds(5) //心跳检测超时时间
};
//服务名(这里通过命令行参数传入不同的服务名,模拟我们有不同的服务[其实只是同一个接口项目的不同运行实例])
var serviceName = appsettingHelper.GetSettings("Consul:serviceName");// AppHelper.ReadAppSettings("Consul", "serviceName");
//注册信息
var registration = new AgentServiceRegistration()
{
ID = $"{localIP}:{localPort}", //服务ID,唯一
Name = serviceName, //服务名(如果服务搭集群,它们的服务名应该是一样的,但是ID不一样)
Address = $"{localIP}", //服务地址
Port = localPort, //服务端口
Tags = new string[] { }, //服务标签,一般可以用来设置权重等本地服务特有信息
Checks = new[] { httpCheck }, //心跳检测设置
};
//向Consul注册服务
consulClient.Agent.ServiceRegister(registration).Wait();
}
新建 json配置文件 /Configs/appsettings_test.json
{
"Consul": {
"consulAddress": "http://127.0.0.1:8500",//consul的访问地址
"serviceName": "iot.webadmin",//服务名称,这个要跟Ocelot 使用时候,这个名字要一致
"currentIp": "127.0.0.1",//本地IP
"currentPort": "8070"//应用端口中
}
}
读取配置文件类appsettingHelper,一般都现成的读配置文件的代码吧?可以用自己的代码读,实在没有,用我的试试:
public static class appsettingHelper
{
static IConfigurationBuilder builder;
public static string GetSettings(string key)
{
IConfigurationBuilder builder;
//测试环境
builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(AppDomain.CurrentDomain.BaseDirectory + "/Configs/appsettings_test.json", optional: false, reloadOnChange: true);
IConfigurationRoot configuration = builder.Build();
return configuration[key];
}
}
在Program.cs中注入代码
RegisterToConsul.RegToConsul(builder.Configuration);
如图:
完成 以上步骤,已经完成应用自动注册功能
下面建一个API,用于给 consul 健康状态检测,意思是这个连接可以访问,则表示应用还活着
HealthController.cs
[Route("[controller]/[action]")]
[ApiController]
public class HealthController : Controller
{
/// <summary>
/// 键康测试
/// </summary>
/// <returns></returns>
[HttpGet]
public JsonResult Check()
{
return new JsonResult("OK");
}
}
完成后如:
运行应用起来,我的应用访问地址是:http://localhost:8070/
过十来秒,再看回上面运行中的consul,会多一个服务,那么这个注册已经完成
下一篇文章讲如何配合Ocelot网关使用