微服务架构:使用Consul实现服务注册和服务发现
参考:
微服务注册中心原理,看这篇就够了! - Java碎碎念 - 博客园
备注:K8S自带服务注册和发现
注册中心
注册中心是什么
注册中心可以说是微服务架构中的”通讯录“,它记录了服务和服务地址的映射关系。在分布式架构中,服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址,进行调用。
功能:
- 服务注册表:
服务注册表是注册中心的核心,它用来记录各个微服务的信息,例如微服务的名称、IP、端口等。服务注册表提供查询API和管理API,查询API用于查询可用的微服务实例,管理API用于服务的注册与注销。 - 服务注册与发现:
服务注册是指微服务在启动时,将自己的信息注册到注册中心的过程。服务发现是指查询可用的微服务列表及网络地址的机制。 - 服务检查:
注册中心使用一定的机制定时检测已注册的服务,如发现某实例长时间无法访问,就会从服务注册表移除该实例
优点
- 解耦:服务消费者个服务提供者解耦,各自变化,不互相影响
- 扩展:服务消费者和服务提供者增加和删除新的服务,对于双方没有任何影响
- 中介者设计模式:这是一种多对多关系的典范
zookeeper
Consul是什么
Consul是一个用来实现分布式系统的服务发现与配置的开源工具。是由go语言开发。他主要由多个组成部分:
-
服务发现:客户端通过Consul提供服务,类似于API,MySQL,或者其他客户端可以使用Consul发现服务的提供者。使用类似DNS或者HTTP,应用程序和可以很轻松的发现他们依赖的服务。
-
检查健康:Consul客户端可以提供与给定服务相关的健康检查(Web服务器返回200 ok)或者本地节点(“内存利用率低于90%”)。这些信息可以监控集群的运行情况,并且使访问远离不健康的主机组件。
-
键值对存储:应用程序可以使用Cousul的层级键值对。
-
多数据中心:Consul有开箱及用的多数据中心。
Consul 的角色
1、server 服务端
2、client 客户端
3、agent 代理
consul agent
命令来启动。agent 可以运行在 server 状态或者 client 状态。自然的,运行在 server 状态的节点被称为 server 节点;运行在 client 状态的节点被称为 client 节点。Consul注册中心运行流程
文字描述:server服务默认就通过agent启动=》其他服务要注册服务时先调用client客户端,client客户端再通过agent启动
下载+启动
Linux安装
参考:安装步骤
window版下载地址
启动服务端:不需要安装,命令启动
下载完解压后,不需要安装,在解压后的文件夹中打开CMD命令窗口
如果要查看consul命令,输入:consul
环境变量设置:为了方便启动,最好设置环境变量,这样就不用每次都去指定文件夹启动consul了,具体参考:consul环境变量设置
开发模式启动命令(所有数据都保存在内存中):consul.exe agent -dev
生产模式启动命令(绑定默认地址和存储数据的路径):consul agent -server -bind=127.0.0.1 -bootstrap-expect 1 -data-dir d:/consul/data
在浏览器中输入查看地址(端口默认是8500,如果变更,在启动后命令窗口处能看到):http://localhost:8500
客户端启动
使用.NET启动客户端
项目中使用Consul
先参考文档:注册中心--文档
设计思路:
Consul服务器方法和Consul客户端方法都封装在MicroService.Core中,使用时是:例如在TeamService中调用Consul服务端方法进行服务注册,在AggregateService中调用Consul客户端方法来服务发现
Consul服务端和客户端封装在MicroService.Core(核心服务)中
- 包安装:NuGet包中搜索Consul且安装
- 注册服务端方法:在ConsulServiceRegistry类中服务注册
/// <summary> /// 注册服务方法 /// </summary> /// <param name="serviceNode"></param> public void Register(ServiceRegistryConfig serviceNode) { // 1、创建consul客户端连接 var consulClient = new ConsulClient(configuration => { //1.1 建立客户端和服务端连接 configuration.Address = new Uri(serviceNode.RegistryAddress); }); // 2、获取服务内部地址 // 3、创建consul服务注册对象 var registration = new AgentServiceRegistration() { ID = serviceNode.Id, Name = serviceNode.Name, Address = serviceNode.Address, Port = serviceNode.Port, Tags = serviceNode.Tags, //心跳检测 Check = new AgentServiceCheck { // 3.1、consul健康检查超时间 Timeout = TimeSpan.FromSeconds(10), // 3.2、服务停止5秒后注销服务 DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5), // 3.3、consul健康检查地址 HTTP = serviceNode.HealthCheckAddress, // 3.4 consul健康检查间隔时间 Interval = TimeSpan.FromSeconds(10), } }; // 4、注册服务 consulClient.Agent.ServiceRegister(registration).Wait(); // 5、关闭连接 consulClient.Dispose(); }
- 客户端方法:在ConsulHttpClient类中服务发现
/// <summary> /// Get方法 /// </summary> /// <typeparam name="T"></typeparam> /// param name="ServiceSchme">服务名称:(http/https)</param> /// <param name="ServiceName">服务名称</param> /// <param name="serviceLink">服务路径</param> /// <returns></returns> public async Task<T> GetAsync<T>(string Serviceshcme, string ServiceName,string serviceLink) { // 1、获取服务 IList<ServiceUrl> serviceUrls = await serviceDiscovery.Discovery(ServiceName); // 2、负载均衡服务 ServiceUrl serviceUrl = loadBalance.Select(serviceUrls); // 3、建立请求 Console.WriteLine($"请求路径:{Serviceshcme} +'://'+{serviceUrl.Url} + {serviceLink}"); HttpClient httpClient = httpClientFactory.CreateClient("mrico"); // HttpResponseMessage response = await httpClient.GetAsync(serviceUrl.Url + serviceLink); HttpResponseMessage response = await httpClient.GetAsync(Serviceshcme +"://"+serviceUrl.Url + serviceLink); // 3.1 json转换成对象 if (response.StatusCode == HttpStatusCode.OK) { string json = await response.Content.ReadAsStringAsync(); return JsonConvert.DeserializeObject<T>(json); } else { // 3.2 进行自定义异常处理,这个地方进行了降级处理 throw new Exception($"{ServiceName}服务调用错误:{response.Content.ReadAsStringAsync()}"); } }
在TeamService(团队服务)中调用Consul服务端方法
在Startup类的Configure方法中调用Consul服务端方法:
app.UseConsulRegistry() ;
在AggregateService(聚合服务)中调用Consul客户端方法
在HttpTeamServiceClient类中的GetTeams方法中调用Consul客户端方法
List<Team> teams = await consulHttpClient.GetAsync<List<Team>>(ServiceSchme, ServiceName, ServiceLink);
本文版权归作者和博客园共有,欢迎转载,但必须在文章页面给出原文链接,否则保留追究法律责任的权利。