- A+
但在微服务架构中,每个微服务通常有多个实例,每个实例具有不同的位置,而且实例会动态变化,比如在负载发生变化时服务会进行扩容或缩容,或者某个实例所在的VM/Container故障后发生迁移,都会导致服务实例地址的变化。因此使用微服务架构开发的应用,必须通过服务注册和发现技术解决此问题。
1. 简单概述
服务注册
服务要被使用,就需要对外提供服务的位置信息,这个位置信息通常是一个IP地址+端口。在服务只有单个实例并且地址不会动态变化的情况下,服务的位置在使用端可以通过配置文件甚至代码等方式固定死。但在位置信息会动态发生变化的情况下,服务实例就需要将这个地址注册到一个注册中心。
服务的所有实例在自己可以对外提供服务后,将位置注册到一个ServiceRegistry服务。这个服务具有固定的位置或域名,负责保存所有服务实例的位置信息。
在使用ServiceRegistry时,服务实例要在无法提供服务时取消注册。ServiceRegistry需要通过心跳等方式核查出无法提供服务的实例,并将实例自动取消注册。
服务注册有Self Registration和3rd Party Registration两种方式。Self Registration需要由每个服务实例自己实现服务的注册和取消注册的代码,3rd Party Registration则由第三方的Registrar完成服务的注册和取消注册。
ServiceRegistry通常会使用如etcd、zookeeper、consul,nacos 等具备分布式一致性的key/value数据库存储服务的注册信息,并提供服务实例的变更通知。
注册流程
如上图,Service-Center中服务发现流程大致有以下几个步骤:
-
1 服务提供者向Service-Center注册服务信息
-
2 服务提供者发送心跳,维持在Service-Center中的“UP”状态
-
3 服务消费者向Service-Center注册服务信息
-
4 服务消费者从Service-Center发现服务提供者信息
-
5 服务消费者向服务提供者发送请求,并获取通讯结果
Self Registration
优点
-
服务实例能够更好的掌握注册时机,仅在真正可提供服务时才注册到ServiceRegistry
缺点:
-
所有服务都需要实现注册和取消注册代码,实现复杂且与ServiceRegistry有耦合;
-
服务的实例很容易在不能提供服务时忘记取消注册;
3rd Party Registration
优点:
-
服务实例不需要负责服务的注册和取消注册,实现简单
-
第三方的服务注册机制通常会提供健康检查机制判断注册的服务实例是否可用
缺点:
-
第三方的软件通常也需要进行部署和管理,增加了复杂度
-
第三方的服务注册机制通常无法了解服务实例的实际状态
服务发现
我们把每个服务的机器实例注册到了注册中心上之后,接下来,我们如何去发现我们需要调用的服务的信息呢?这就是服务发现了。
客户端要使用服务必须通过服务发现技术获取服务的位置信息。服务发现包括Client-Side的服务发现和Server-Side的服务发现两种方式。
Client-Side Discovery
Client-Side Discovery
在Client-Side Discovery的设计中,服务实例的发现由Client进行,发现的方式可以是主动到ServiceRegistry查询,也可以由ServiceRegistry通知到Client。在使用Client-Side Discovery时,Client会发现服务的所有实例,并根据LB策略选择一个实例发起请求。
Server-Side Discovery
在Server-Side Discovery设计中,在Client和所有的服务实例间增加LoadBalance,Client只需要访问LoadBalance,由LoadBalance负责服务的发现和负载均衡。
方案对比
不论是Client-Side还是Server-Side的服务发现,执行发现的组件(Client/LoadBalance)通常都需要引入本地缓存,并通过核查保证与ServiceRegistry的一致性。引入缓存可以避免对ServiceRegistry的频繁交互,能够提升性能。
Client-Side Discovery相对于Server-Side Discovery有更少的跳数,性能更优。但所有类型的客户端都需要实现服务发现与LB算法,客户端的复杂度高,且与ServiceRegistry耦合。
Server-Side Discovery设计中,客户端只需要看到LoadBalance,复杂度低;如果是基于公有云提供服务,则公有云提供商通常会提供现成的服务端LoadBalance。但相对Client-Side Discovery增加了一跳,对性能有一定影响;同时LoadBalance的开发、部署、运维带来了额外的复杂度;
总结
在服务实例的位置会发生动态变化的微服务架构中,需要引入服务注册和发现技术。
服务注册和发现需要一个位置固定或提供了固定域名的ServiceRegistry服务,服务发布端通过Self-Registration或3rd Party Registration将所有的服务实例注册到ServiceRegistry,Client或LoadBalance则通过其获取服务实例的位置以执行负载均衡和发送服务请求。
ServiceRegistry和LoadBalance是系统中的关键服务,如果这两个服务出现了异常,通常会导致系统不可用,因此它们必须具备极高的可用性。
2. Consul
中文文档:Consul 中文文档 - Consul 中文文档 (gitbook.io)
做服务发现的框架常用的有 zookeeper, eureka, consul,nacos 。 那么consul是啥?consul就是提供服务发现的工具。
Consul
是HashiCorp公司推出的开源工具,Consul由Go语言开发,部署起来非常容易,只需要极少的可执行程序和配置文件,具有绿色、轻量级的特点。Consul
是分布式
的、高可用
的、 可横向扩展
的用于实现分布式系统的服务发现与配置。
1. Consul具有哪些特点
-
服务发现(Service Discovery):
Consul
提供了通过DNS或者HTTP接口的方式来注册服务和发现服务。一些外部的服务通过Consul很容易的找到它所依赖的服务。 -
健康检查(Health Checking):Consul的Client可以提供任意数量的健康检查,既可以与给定的服务相关联(“webserver是否返回200 OK”),也可以与本地节点相关联(“内存利用率是否低于90%”)。操作员可以使用这些信息来监视集群的健康状况,服务发现组件可以使用这些信息将流量从不健康的主机路由出去。
-
Key/Value存储:应用程序可以根据自己的需要使用Consul提供的Key/Value存储。 Consul提供了简单易用的HTTP接口,结合其他工具可以实现动态配置、功能标记、领袖选举等等功能。
-
安全服务通信:Consul可以为服务生成和分发TLS证书,以建立相互的TLS连接。意图可用于定义允许哪些服务通信。服务分割可以很容易地进行管理,其目的是可以实时更改的,而不是使用复杂的网络拓扑和静态防火墙规则。
-
多数据中心: Consul支持开箱即用的多数据中心。这意味着Consul的用户不必担心建立额外的抽象层来发展到多个区域。
2. Consul 架构图
我们来分析一下这张图,并描述一下每一个部分。首先,我们可以看到有两个数据中心,分别标注为 "DATACENTER1"和 "DATACENTER2"。Consul对多个数据中心有天然非常好的支持,并希望这是常见的情况。
在每个数据中心内,我们有Client和Server的混合。预计会有3到5台Server。这是在权衡故障场景下可用性和性能之间取得平衡的结果,因为随着机器的增加,共识的速度会逐渐变慢。然而,Client的数量没有限制,它们可以轻松地扩展到数千或数万。
所有在数据中心的代理都会参与一个Gossip协议。这意味着有一个Gossip池,其中包含了某个数据中心的所有Agent。这有几个目的:
-
第一,客户端不需要配置Server的地址,发现工作是自动完成的。
-
第二,检测代理故障的工作不放在Server上,而是分布式的。这使得故障检测的扩展性比原生的心跳方案要强得多。同时,它还为节点提供了故障检测,如果代理无法到达,那么该节点可能已经发生了故障。
-
第三,它被用作消息层,当发生重要事件(如Leader 选举)时进行通知。
每个数据中心的Server都是单一Raft对等集的一部分。这意味着它们共同选出一个单一的Leader,一个被选中的Server,它有额外的职责。Leader负责处理所有查询和事务。事务也必须复制到所有参与共识协议的分片。由于这一要求,当None-Leader Server收到RPC请求时,它会将其转发给集群Leader。
Server Agent还作为WAN(广域网) Gossip Pool的一部分进行操作。这个池子与LAN(局域网)池不同,因为它是针对互联网的较高延迟进行优化的,WAN池只包含其他Consul 数据中心的Sever Agent。这个池的目的是让数据中心以低接触的方式发现彼此。让一个新的数据中心上线就像加入现有的WAN Gossip 池一样简单。因为服务器都在这个池中运行,所以还可以实现跨数据中心的请求。当一台Server收到一个不同数据中心的请求时,它会将其转发到正确数据中心的随机Server。然后该Servevr可能会转发到本地Leader。
这导致数据中心之间的耦合度很低,但由于故障检测、连接缓存和多路复用,跨数据中心的请求相对快速可靠。
一般情况下,不同的Consul数据中心之间不会复制数据。当对另一个数据中心的资源进行请求时,本地Consul服务器会将该资源的RPC请求转发给远程Consul服务器,并返回结果。如果远程数据中心不可用,那么这些资源也将不可用,但这不会以其他方式影响本地数据中心。
在一些特殊情况下,可以复制有限的数据子集,比如使用Consul内置的ACL复制功能,或者使用consul-replicate等外部工具。 在某些地方,Client Agent可能会从Server上缓存数据,使其在本地可用,以提高性能和可靠性。例如, 包括连接证书和它允许Client代理对入站连接请求做出本地决定,而无需往返Server的场景。一些API端点还支持可选的结果缓存。这有助于可靠性,因为即使与服务器的连接中断或服务器暂时不可用,本地Agent仍然可以继续从缓存中响应一些查询,如服务发现或Connect授权。
3. 安装Consul
安装完 Consul 后应该运行 agent,它可以运行在服务端(server)或者客户端(client)模式下。每个数据中心(datacenter)都至少有一个 server,推荐一个集群(cluster)至少有 3 到 5 个 server。因为在故障情况下数据丢失是不可避免的,所以强烈建议不要单机部署。
其他 agent 运行在 client 模式,它是一个轻量级进程,提供服务注册、健康检查和服务器见的查询转发。集群的所有节点都应运行一个 agent。
windows 安装
https://www.consul.io/downloads
-
选择windows x64 版本(64bit)
-
进入下载好的文件夹中,打开powershell ,执行命令,启动服务端代理
consul.exe agent -dev
-
浏览器输入:http://IP:8500/出现ConsulWeb界面就表示成功了
-dev 表示开发模式,生产环境下不建议使用,因为它不会保存任何状态
-server 生产环境下可使用
linux 安装
-
yum 安装 consul 环境
sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo sudo yum -y install consul
-
安装完事后,执行命令查询是否成功:
-
[root@yushangxue ~]# consul version Consul v1.10.3 Revision c976ffd2d Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)
执行命令,启动服务端
consul agent -dev
-
浏览器输入:http://IP:8500/出现ConsulWeb界面就表示成功了
Docker 安装
拉取Consul镜像
$ docker pull consul # 默认拉取latest $ docker pull consul:1.15.4 # 拉取指定版本
安装并运行
-
docker run -d -p 8500:8500 --restart=always --name=consul consul agent -server -bootstrap -ui -node=1 -client='0.0.0.0'
agent: 表示启动 Agent 进程。
-
server:表示启动 Consul Server 模式
-
client:表示启动 Consul Cilent 模式。
-
bootstrap:表示这个节点是 Server-Leader ,每个数据中心只能运行一台服务器。技术角度上讲 Leader 是通过 Raft 算法选举的,但是集群第一次启动时需要一个引导 Leader,在引导群集后,建议不要使用此标志。
-
ui:表示启动 Web UI 管理器,默认开放端口 8500,所以上面使用 Docker 命令把 8500 端口对外开放。
-
node:节点的名称,集群中必须是唯一的,默认是该节点的主机名。
-
client:consul服务侦听地址,这个地址提供HTTP、DNS、RPC等服务,默认是127.0.0.1所以不对外提供服务,如果你要对外提供服务改成0.0.0.0
-
join:表示加入到某一个集群中去。 如:-json=192.168.0.11。
4 .Net 6 中如何使用
-
安装包:
Consul.AspNetCore 1.6.10.8
-
application.json
"Consul": { "ConsulHost": "http://localhost:8500", "ServiceName": "NetCloud.Consul.ServiceA", "ServiceIP": "localhost", "ServicePort" : 5009 }
添加Consul服务
-
public class ConsulOption { public string ConsulHost { get; set; } public string ServiceName { get; set; } public string ServiceIP { get; set; } public int ServicePort { get; set; } } public static class ServiceBuilderExtension { public static IServiceCollection AddConsulService(this IServiceCollection services,IConfiguration configuration) { var consulSection = configuration.GetSection("Consul"); services.Configure<ConsulOption>(consulSection); var consulOption = consulSection.Get<ConsulOption>(); services.AddConsul(p => { p.Address = new Uri(consulOption.ConsulHost); }); services.AddConsulServiceRegistration(p => { p.Address = consulOption.ServiceIP;// 服务绑定IP p.ID = Guid.NewGuid().ToString();// 唯一服务Id p.Name = consulOption.ServiceName; // 服务名 p.Port = consulOption.ServicePort; // 服务端口号 p.Check = new() { DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5), Interval = TimeSpan.FromSeconds(10), // 健康检查时间间隔 HTTP = $"http://{p.Address}:{p.Port}/healthCheck/GetHealthCheck", // 健康检查地址 Timeout = TimeSpan.FromSeconds(5) // 超时时间 }; }); return services; } } Program.cs 代码 builder.Services.AddConsulService(builder.Configuration); 创建健康检查控制器: [Route("[controller]")] [ApiController] public class HealthCheckController : ControllerBase { [HttpGet] public ActionResult GetHealthCheck() { Console.WriteLine($"进行心跳检测:{DateTime.Now}"); return Ok("连接正常"); } }
-
-
citical 状态:表示健康检测失败,如果在
DeregisterCriticalServiceAfter
时间还是检测失败,则服务会消失大家在本地测试时,一定要确保Consul 服务器能访问你的健康检测地址。例如我,Consul装在云服务器上,在我本机启动时由云服务器无法访问我本机,所以服务健康检测一直失败。把项目部署至云服务器上就好了
-
passing 健康检测成功
获取服务
ServiceB
调用 ServiceA
接口
public class HomeController:ControllerBase { private readonly IConsulClient _consulClient; public HomeController(IConsulClient consulClient) { _consulClient = consulClient; } [HttpGet] public IActionResult TestConsul() { var services = _consulClient.Catalog.Service("NetCloud.Consul.ServiceB").Result.Response; var instance = services[new Random().Next(services.Length)]; using HttpClient client = new(); string result = client.GetStringAsync($"http://{instance.ServiceAddress}:{instance.ServicePort}").Result; return Ok($"服务B结果:{result},服务A结果:测试成功"); } }
3. Nacos
官网: https://nacos.io/zh-cn/index.html
Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
Nacos 的关键特性包括:
-
服务发现和服务健康监测
Nacos 支持基于 DNS 和基于 RPC 的服务发现。服务提供者使用 原生SDK、OpenAPI、或一个独立的Agent TODO注册 Service 后,服务消费者可以使用DNS TODO 或HTTP&API查找和发现服务。
Nacos 提供对服务的实时的健康检查,阻止向不健康的主机或服务实例发送请求。Nacos 支持传输层 (PING 或 TCP)和应用层 (如 HTTP、MySQL、用户自定义)的健康检查。 对于复杂的云环境和网络拓扑环境中(如 VPC、边缘网络等)服务的健康检查,Nacos 提供了 agent 上报模式和服务端主动检测2种健康检查模式。Nacos 还提供了统一的健康检查仪表盘,帮助您根据健康状态管理服务的可用性及流量。
-
动态配置服务
动态配置服务可以让您以中心化、外部化和动态化的方式管理所有环境的应用配置和服务配置。
动态配置消除了配置变更时重新部署应用和服务的需要,让配置管理变得更加高效和敏捷。
配置中心化管理让实现无状态服务变得更简单,让服务按需弹性扩展变得更容易。
Nacos 提供了一个简洁易用的UI (控制台样例 Demo) 帮助您管理所有的服务和应用的配置。Nacos 还提供包括配置版本跟踪、金丝雀发布、一键回滚配置以及客户端配置更新状态跟踪在内的一系列开箱即用的配置管理特性,帮助您更安全地在生产环境中管理配置变更和降低配置变更带来的风险。
-
动态 DNS 服务
动态 DNS 服务支持权重路由,让您更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。动态DNS服务还能让您更容易地实现以 DNS 协议为基础的服务发现,以帮助您消除耦合到厂商私有服务发现 API 上的风险。
Nacos 提供了一些简单的 DNS APIs TODO 帮助您管理服务的关联域名和可用的 IP:PORT 列表.
-
服务及其元数据管理
Nacos 能让您从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。
Docker 安装 Nacos
-
安装git
yum install -y git
-
安装docker compose
# 1. 从github上下载docker-compose二进制文件安装 curl -L https://github.com/docker/compose/releases/download/1.27.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose # 2. 添加可执行权限 sudo chmod +x /usr/local/bin/docker-compose # 3. 测试安装结果 docker-compose --version # 卸载(别卸载哈) rm /usr/local/bin/docker-compose
-
Clone 项目
git clone https://github.com/nacos-group/nacos-docker.git cd nacos-docker
-
修改application.properties文件
位置:./nacos-docker/example/init.d/application.properties
主要修改以下几个属性:
-
db.url.0 : MySQL 连接地址
-
db.user.0:MySQL 用户名
-
db.password.0: MySQL 密码
-
-
创建Nacos 数据库,用于保存Nacos中产生的数据
/******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info */ /******************************************/ CREATE TABLE `config_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(255) DEFAULT NULL, `content` longtext NOT NULL COMMENT 'content', `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间', `src_user` text COMMENT 'source user', `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip', `app_name` varchar(128) DEFAULT NULL, `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', `c_desc` varchar(256) DEFAULT NULL, `c_use` varchar(64) DEFAULT NULL, `effect` varchar(64) DEFAULT NULL, `type` varchar(64) DEFAULT NULL, `c_schema` text, `encrypted_data_key` text NOT NULL COMMENT '秘钥', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='config_info'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info_aggr */ /******************************************/ CREATE TABLE `config_info_aggr` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(255) NOT NULL COMMENT 'group_id', `datum_id` varchar(255) NOT NULL COMMENT 'datum_id', `content` longtext NOT NULL COMMENT '内容', `gmt_modified` datetime NOT NULL COMMENT '修改时间', `app_name` varchar(128) DEFAULT NULL, `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='增加租户字段'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info_beta */ /******************************************/ CREATE TABLE `config_info_beta` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(128) NOT NULL COMMENT 'group_id', `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `content` longtext NOT NULL COMMENT 'content', `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps', `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间', `src_user` text COMMENT 'source user', `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip', `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', `encrypted_data_key` text NOT NULL COMMENT '秘钥', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='config_info_beta'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_info_tag */ /******************************************/ CREATE TABLE `config_info_tag` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(128) NOT NULL COMMENT 'group_id', `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', `tag_id` varchar(128) NOT NULL COMMENT 'tag_id', `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `content` longtext NOT NULL COMMENT 'content', `md5` varchar(32) DEFAULT NULL COMMENT 'md5', `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间', `src_user` text COMMENT 'source user', `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip', PRIMARY KEY (`id`), UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='config_info_tag'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = config_tags_relation */ /******************************************/ CREATE TABLE `config_tags_relation` ( `id` bigint(20) NOT NULL COMMENT 'id', `tag_name` varchar(128) NOT NULL COMMENT 'tag_name', `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type', `data_id` varchar(255) NOT NULL COMMENT 'data_id', `group_id` varchar(128) NOT NULL COMMENT 'group_id', `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id', `nid` bigint(20) NOT NULL AUTO_INCREMENT, PRIMARY KEY (`nid`), UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`), KEY `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='config_tag_relation'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = group_capacity */ /******************************************/ CREATE TABLE `group_capacity` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群', `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值', `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_group_id` (`group_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='集群、各Group容量信息表'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = his_config_info */ /******************************************/ CREATE TABLE `his_config_info` ( `id` bigint(64) unsigned NOT NULL, `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `data_id` varchar(255) NOT NULL, `group_id` varchar(128) NOT NULL, `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name', `content` longtext NOT NULL, `md5` varchar(32) DEFAULT NULL, `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00', `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00', `src_user` text, `src_ip` varchar(20) DEFAULT NULL, `op_type` char(10) DEFAULT NULL, `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段', `encrypted_data_key` text NOT NULL COMMENT '秘钥', PRIMARY KEY (`nid`), KEY `idx_gmt_create` (`gmt_create`), KEY `idx_gmt_modified` (`gmt_modified`), KEY `idx_did` (`data_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='多租户改造'; /******************************************/ /* 数据库全名 = nacos_config */ /* 表名称 = tenant_capacity */ /******************************************/ CREATE TABLE `tenant_capacity` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID', `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID', `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值', `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量', `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值', `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数', `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值', `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量', `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间', `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='租户容量信息表'; CREATE TABLE `tenant_info` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id', `kp` varchar(128) NOT NULL COMMENT 'kp', `tenant_id` varchar(128) default '' COMMENT 'tenant_id', `tenant_name` varchar(128) default '' COMMENT 'tenant_name', `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc', `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source', `gmt_create` bigint(20) NOT NULL COMMENT '创建时间', `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`), KEY `idx_tenant_id` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='tenant_info'; CREATE TABLE users ( username varchar(50) NOT NULL PRIMARY KEY, password varchar(500) NOT NULL, enabled boolean NOT NULL ); CREATE TABLE roles ( username varchar(50) NOT NULL, role varchar(50) NOT NULL, constraint uk_username_role UNIQUE (username,role) ); CREATE TABLE permissions ( role varchar(50) NOT NULL, resource varchar(512) NOT NULL, action varchar(8) NOT NULL, constraint uk_role_permission UNIQUE (role,resource,action) ); INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE); INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
在 ./nacos-docker/example 目录下新建 nacos.yaml 文件,配置内容如下:
-
version: "3.8" services: nacos: image: nacos/nacos-server container_name: nacos env_file: - ../env/custom-application-config.env volumes: - ./standalone-logs/:/home/nacos/logs - ./init.d/application.properties:/home/nacos/conf/application.properties ports: - "8848:8848" - "9848:9848" environment: JVM_XMS: 512m JVM_XMX: 512m restart: on-failure
-
JVM_XMS: 就是初始分配堆内存配置-Xms,默认 :2g
-
JVM_XMX:就是堆内存最大配置-Xmx,默认 :2g
-
JVM_XMN:就是堆内存中年轻代内存大小配置-Xmn,默认 :1g
-
JVM_MS:就是元数据空间-XX:MetaspaceSize,默认 :128m
-
JVM_MMS:就是元数据空间-XX:MaxMetaspaceSize,默认 :320m
-
-
单机模式 创建容器
docker-compose -f example/nacos.yaml up
访问:http://你的IP地址:8848/nacos
默认登录账号:nacos
,密码:nacos
.Net 6 使用Nacos
文档:https://github.com/nacos-group/nacos-sdk-csharp
nuget 安装:
-
nacos-sdk-csharp 1.3.4
-
nacos-sdk-csharp.AspNetCore 1.3.4
-
nacos-sdk-csharp.Extensions.Configuration 1.3.4
服务发现
-
任意创建WebAPI项目-NetCloud.Nacos.UserService1
-
appsetting.json 配置:
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*", "nacos": { "ServerAddresses": [ "http://localhost:8848" ], "DefaultTimeOut": 15000, //请求超时时间 "Namespace": "6c0da0b7-eb66-4c15-b73e-15b983d07548", //命名空间id "ListenInterval": 1000, //监听间隔时间 "ServiceName": "NetCloud.Nacos.UserService", //服务名称 "GroupName": "DEFAULT_GROUP", // 默认分组名称 "ClusterName": "DEFAULT", // 如果去掉这个配置项,系统会自动获取服务IP(建议去掉这个配置) // "Ip": "localhost",//注册中心,服务调用的IP地址 // "PreferredNetworks": "localhost", //首选网络 // 如果去掉这个配置项,系统会自动获取服务运行的端口号(建议去掉这个配置) // "Port": 0, // // 写0 表示80端口() "Weight": 100, "RegisterEnabled": true, "InstanceEnabled": true, "Ephemeral": true, "Secure": false, //表示当前服务是否时安全实例,用于标识访问的时候是否要启用 https "AccessKey": "", "SecretKey": "", "UserName": "", "Password": "", "ConfigUseRpc": false, "NamingUseRpc": false, "NamingLoadCacheAtStart": "", "LBStrategy": "WeightRandom", // 负载均衡策略:WeightRandom(随机), WeightRoundRobin(轮询) "Metadata": { "aa": "bb", "cc": "dd" } } }
建议将IP与端口号配置去掉,由系统自行获取注册的IP与端口
-
在program.cs 文件注入服务:
builder.Services.AddNacosAspNet(builder.Configuration);
创建任意控制器
-
[Route("[controller]/[action]")] [ApiController] public class UserController:ControllerBase { [HttpGet] public IActionResult GetUserList() { List<UserInfo> list = new() { new(1, "张三"), new(2, "李四") }; return Ok(list); } } public record UserInfo(int Id,string UserName);
多个实例(负载均衡)
-
任意创建WebAPI项目-NetCloud.Nacos.UserService2
-
将UserService1 的配置重新来一遍
-
注意,服务名称一定要一样,端口号必须不一样(每个项目的端口号肯定不一样)
服务调用
-
创建WebAPI项目-NetCloud.Nacos.UserClient, 用于调用UserService服务
-
同样的步骤,将UserClient项目注册至Nacos中
-
在控制器中调用UserService中的服务
[Route("[controller]/[action]")] [ApiController] public class InvokeController:ControllerBase { private readonly INacosNamingService _nacosNamingService; public InvokeController(INacosNamingService nacosNamingService) { _nacosNamingService = nacosNamingService; } [HttpGet] public async Task<IActionResult> GetUserList() { // 获取服务的实例(自带负载均衡) var instance = await _nacosNamingService.SelectOneHealthyInstance("NetCloud.Nacos.UserService"); var host = $"{instance.Ip}:{instance.Port}"; var baseUrl = instance.Metadata.TryGetValue("secure", out _) //放弃out输出 ? $"https://{host}" : $"http://{host}"; if (string.IsNullOrWhiteSpace(baseUrl)) { return Ok("empty"); } var url = $"{baseUrl}/user/GetUserList"; using (HttpClient client = new HttpClient()) { var result = await client.GetAsync(url); return Ok(await result.Content.ReadAsStringAsync()); } } }
配套视频链接:课程简介 (cctalk.com)