摘抄:https://cloud.tencent.com/developer/article/1444664

一、什么是服务发现

微服务的框架体系中,服务发现是不能不提的一个模块。我相信了解或者熟悉微服务的童鞋应该都知道它的重要性。这里我只是简单的提一下,毕竟这不是我们的重点。我们看下面的一幅图片:

图中,客户端的一个接口,需要调用服务A, B, …, N。客户端必须要知道所有服务的网络位置的,以往的做法是配置是配置文件中,或者有些配置在数据库中。这里就带出几个问题:

  • 需要配置N个服务的网络位置,加大配置的复杂性
  • 服务的网络位置变化,都需要改变每个调用者的配置
  • 集群的情况下,难以做负载(反向代理的方式除外)

总结起来一句话:服务多了,配置很麻烦,问题多多

既然有这些问题,那么服务发现就是解决这些问题的。话说,怎么解决呢?我们再看一张图

与之前一张不同的是,加了个服务发现模块。图比较简单,这边文字描述下。服务A, B, …, N把当前自己的网络位置注册到服务发现模块(这里注册的意思就是告诉),服务发现就以K-V的方式记录下,K一般是服务名,V就是IP:PORT。服务发现模块定时的轮询查看这些服务能不能访问的了(这就是健康检查)。客户端在调用服务A, B, …, N的时候,就跑去服务发现模块问下它们的网络位置,然后再调用它们的服务。这样的方式是不是就可以解决上面的问题了呢?客户端完全不需要记录这些服务网络位置,客户端和服务端完全解耦!

图中的服务发现模块基本上就是中服务发现的作用了。





二、consul 简介

做服务发现的框架常用的有

  • zookeeper
  • eureka
  • etcd
  • consul

这里就不比较哪个好哪个差了,需要的童鞋自己谷歌百度。

那么consul是啥?consul就是提供服务发现的工具。然后下面是简单的介绍:

consul是分布式的、高可用、横向扩展的。

consul提供的一些关键特性:

  • service discovery:consul通过DNS或者HTTP接口使服务注册和服务发现变的很容易,一些外部服务,例如saas提供的也可以一样注册。
  • health checking:健康检测使consul可以快速的告警在集群中的操作。和服务发现的集成,可以防止服务转发到故障的服务上面。
  • key/value storage:一个用来存储动态配置的系统。提供简单的HTTP接口,可以在任何地方操作。
  • multi-datacenter:无需复杂的配置,即可支持任意数量的区域。 我们这里会介绍服务发现,健康检查,还有一些基本KV存储。多数据中心有机会另一篇文章再说。

总结:只要知道它是解决我上一部分提出的问题就行,其它的东西慢慢理解





三、consul的几个概念

上图来自于consul官方文档

我们只看数据中心1,可以看出consul的集群是由N个SERVER,加上M个CLIENT组成的。而不管是SERVER还是CLIENT,都是consul的一个节点,所有的服务都可以注册到这些节点上,正是通过这些节点实现服务注册信息的共享。除了这两个,还有一些小细节,一一简单介绍。

  • CLIENT: CLIENT表示consul的client模式,就是客户端模式。是consul节点的一种模式,这种模式下,所有注册到当前节点的服务会被转发到SERVER,本身是不持久化这些信息。
  • SERVER: SERVER表示consul的server模式,表明这个consul是个server,这种模式下,功能和CLIENT都一样,唯一不同的是,它会把所有的信息持久化的本地,这样遇到故障,信息是可以被保留的。
  • SERVER-LEADER: 中间那个SERVER下面有LEADER的字眼,表明这个SERVER是它们的老大,它和其它SERVER不一样的一点是,它需要负责同步注册的信息给其它的SERVER,同时也要负责各个节点的健康监测。

其它信息其它信息包括它们之间的通信方式,还有一些协议信息,算法。它们是用于保证节点之间的数据同步,实时性要求等等一系列集群问题的解决。这些有兴趣的自己看看官方文档

AI解读

一、数据中心内部(以 Datacenter 1 为例)

  1. 节点角色与通信
    • Server 节点分为LEADER(领导者)和FOLLOWER(跟随者)。
      • Leader Forwarding:非 Leader 的 Server(如 Follower)收到客户端请求后,通过 LEADER FORWARDING 将请求转发给 Leader 处理,确保操作的集中协调。
      • Replication:Leader 处理完数据变更(如写操作)后,通过 REPLICATION(复制)将数据同步给同数据中心内的 Follower,保证数据一致性,使用 TCP/8300 端口。
    • Client 节点:通过 RPC(TCP/8300) 与 Server 通信,发送服务发现、配置查询等请求。
  2. LAN Gossip 机制
    • 同一数据中心内的节点(Client 和 Server)通过 LAN GOSSIP(TCP/UDP/8301) 通信,实现成员发现、状态同步(如节点存活检测),确保集群感知内部节点变化。

二、跨数据中心(Datacenter 1 与 Datacenter 2)

  1. WAN Gossip 通信
    • 不同数据中心的 Server 借助 WAN GOSSIP(TCP/UDP/8302) 跨网络(通过 Internet)通信,用于跨数据中心的成员发现和状态同步,支持多数据中心架构。
  2. 跨数据中心请求处理
    • Remote DC Forwarding:若本地数据中心无法处理请求(如跨数据中心的服务发现),通过 REMOTE DC FORWARDING(TCP/8300) 将请求转发到目标数据中心的 Server 处理,实现跨数据中心的协作。

三、整体协作逻辑

  • 请求处理:客户端请求优先由本地数据中心的 Server 处理,非 Leader 节点通过 Leader Forwarding 集中到 Leader;跨数据中心请求通过 WAN Gossip 和 Remote DC Forwarding 协作。
  • 数据一致性:通过 Replication 在数据中心内同步数据,通过多数据中心的通信机制保证全局状态感知,最终实现分布式环境下的服务发现、配置管理等高可用功能。




四、安装 Consul

1
brew install consul

检查 consul 是否可用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
~ ··················································································································································································································································································· 42s 00:04:18
❯ consul
Usage: consul [--version] [--help] <command> [<args>]

Available commands are:
acl Interact with Consul's ACLs
agent Runs a Consul agent
catalog Interact with the catalog
config Interact with Consul's Centralized Configurations
connect Interact with Consul Connect
debug Records a debugging archive for operators
event Fire a new event
exec Executes a command on Consul nodes
force-leave Forces a member of the cluster to enter the "left" state
info Provides debugging information for operators.
intention Interact with Connect service intentions
join Tell Consul agent to join cluster
keygen Generates a new encryption key
keyring Manages gossip layer encryption keys
kv Interact with the key-value store
leave Gracefully leaves the Consul cluster and shuts down
lock Execute a command holding a lock
login Login to Consul using an auth method
logout Destroy a Consul token created with login
maint Controls node or service maintenance mode
members Lists the members of a Consul cluster
monitor Stream logs from a Consul agent
operator Provides cluster-level tools for Consul operators
peering Create and manage peering connections between Consul clusters
reload Triggers the agent to reload configuration files
resource Interact with Consul's resources
rtt Estimates network round trip time between nodes
services Interact with services
snapshot Saves, restores and inspects snapshots of Consul server state
tls Builtin helpers for creating CAs and certificates
troubleshoot CLI tools for troubleshooting Consul service mesh
validate Validate config files/directories
version Prints the Consul version
watch Watch for changes in Consul

如果出现上面这样代表consul是没问题的。





五、运行 Consul Agent

Consul安装之后,Agent必须运行。 Agent可以在服务器或客户端模式下运行。 每个数据中心都必须至少有一台服务器,但推荐使用3台或5台服务器。 一个单一的服务器部署是非常不推荐的,因为在故障情况下数据丢失是不可避免的。

所有其他代理以客户端模式运行。 客户端是一个非常轻量级的进程,它注册服务,运行健康检查,并将查询转发给服务器。 代理程序必须在集群中的每个节点上运行。

为了简单起见,我们现在将以开发模式启动Consul代理。 这种模式对于快速简单地启动单节点Consul环境非常有用。 它并不打算在生产中使用,因为它不会持续任何状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
~ ······················································································································································································································································································· 00:19:22
❯ consul agent -dev
==> Starting Consul agent...
Version: '1.20.5'
Build Date: '2025-03-11 10:16:18 +0000 UTC'
Node ID: '270aaffe-66cf-67ce-80a9-d3e1985cd60e'
Node name: 'GJVXWHGXQ1'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, gRPC-TLS: 8503, DNS: 8600)
Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
Gossip Encryption: false
Auto-Encrypt-TLS: false
ACL Enabled: false
Reporting Enabled: false
ACL Default Policy: allow
HTTPS TLS: Verify Incoming: false, Verify Outgoing: false, Min Version: TLSv1_2
gRPC TLS: Verify Incoming: false, Min Version: TLSv1_2
Internal RPC TLS: Verify Incoming: false, Verify Outgoing: false (Verify Hostname: false), Min Version: TLSv1_2

==> Log data will now stream in as it occurs:

2025-03-19T00:19:25.846+0800 [DEBUG] agent.grpc.balancer: switching server: target=consul://dc1.270aaffe-66cf-67ce-80a9-d3e1985cd60e/server.dc1 from=<none> to=<none>
2025-03-19T00:19:25.857+0800 [INFO] agent.server.raft: initial configuration: index=1 servers="[{Suffrage:Voter ID:270aaffe-66cf-67ce-80a9-d3e1985cd60e Address:127.0.0.1:8300}]"
2025-03-19T00:19:25.857+0800 [INFO] agent.server.raft: entering follower state: follower="Node at 127.0.0.1:8300 [Follower]" leader-address= leader-id=
2025-03-19T00:19:25.859+0800 [INFO] agent.server.serf.wan: serf: EventMemberJoin: GJVXWHGXQ1.dc1 127.0.0.1
2025-03-19T00:19:25.859+0800 [INFO] agent.server.serf.lan: serf: EventMemberJoin: GJVXWHGXQ1 127.0.0.1
2025-03-19T00:19:25.859+0800 [INFO] agent.router: Initializing LAN area manager
2025-03-19T00:19:25.860+0800 [DEBUG] agent.grpc.balancer: switching server: target=consul://dc1.270aaffe-66cf-67ce-80a9-d3e1985cd60e/server.dc1 from=<none> to=dc1-127.0.0.1:8300
2025-03-19T00:19:25.860+0800 [INFO] agent.server: Adding LAN server: server="GJVXWHGXQ1 (Addr: tcp/127.0.0.1:8300) (DC: dc1)"
2025-03-19T00:19:25.860+0800 [INFO] agent.server: Handled event for server in area: event=member-join server=GJVXWHGXQ1.dc1 area=wan
2025-03-19T00:19:25.861+0800 [INFO] agent.server.autopilot: reconciliation now disabled
2025-03-19T00:19:25.863+0800 [DEBUG] agent.server.autopilot: autopilot is now running
...(省略)
默认端口
1
2
3
4
~ ······················································································································································································································································································· 01:10:44
❯ lsof -i :8500
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
consul 84416 guowei.gong 17u IPv4 0x8f4e9332d460a11b 0t0 TCP localhost:fmtp (LISTEN)

Consul 的 agent 启动后,默认端口通常是 8500 ,这是其 HTTP API 和 Web 界面的默认端口。通过浏览器访问http://localhost:8500 ,在正常启动且端口未被占用的情况下,能看到 Consul 的管理界面。

不过,端口也可以在启动时通过配置参数修改,例如在命令中使用 -http-port 参数指定其他端口 ,像consul agent -dev -http-port=8600,这样启动后访问端口就变为 8600 。 此外,Consul 还有用于其他功能的默认端口,如用于 LAN Gossip 协议的 8301 端口、用于 WAN Gossip 协议的 8302 端口等。





六、集群成员

在另一个终端运行consul members,可以看到Consul集群的成员。 应该只看到一个成员(你自己):

1
2
3
4
~ ······················································································································································································································································································· 00:38:34
❯ consul members
Node Address Status Type Build Protocol DC Partition Segment
GJVXWHGXQ1 127.0.0.1:8301 alive server 1.20.5 2 dc1 default <all>

输出显示了我们自己的节点,它正在运行的地址,运行状况,在集群中的角色以及一些版本信息。 额外的元数据可以通过提供-detailed标志来查看。

1
2
3
4
~ ······················································································································································································································································································· 00:44:04
❯ consul members -detailed
Node Address Status Tags
GJVXWHGXQ1 127.0.0.1:8301 alive acls=0,ap=default,build=1.20.5:74efe419,dc=dc1,ft_fs=1,ft_si=1,grpc_port=8502,grpc_tls_port=8503,id=270aaffe-66cf-67ce-80a9-d3e1985cd60e,port=8300,raft_vsn=3,role=consul,segment=<all>,vsn=2,vsn_max=3,vsn_min=2,wan_join_port=8302

members命令的输出基于gossip协议,并最终一致。 也就是说,在任何时候,当地代理所看到的可能与服务器上的状态不完全一致。 要获得完全一致,请使用HTTP API再将HTTP请求转发给Consul服务器:

为什么不一致?

Consul 中 members 命令输出基于 Gossip 协议出现不一致,主要源于 Gossip 协议的异步特性与 Consul 服务器(Server)的强一致性机制差异,具体原因如下:

1. Gossip 协议的异步传播特性

  • 非实时同步:Gossip 协议通过节点间随机、异步的消息传播同步状态。本地代理(Agent)通过 Gossip 收集成员信息时,消息在集群中扩散需要时间。例如,新节点加入或旧节点故障时,状态变更消息可能因网络延迟、传播路径等因素,无法立即同步到所有节点。
  • 最终一致性:Gossip 仅保证 “最终一致性”,而非强一致性。在传播完成前,不同节点(包括本地代理)看到的成员列表可能是旧状态,导致本地 members 命令输出与集群真实状态存在短暂偏差。

2. Consul 服务器的强一致性保障

  • Raft 协议主导:Consul 服务器(尤其是 Leader 节点)通过 Raft 共识算法维护强一致性。所有关键状态(如成员列表、服务注册数据)的写操作都由 Leader 处理,并通过 Raft 日志复制到其他服务器,确保服务器集群状态实时一致。
  • HTTP API 的权威获取:当通过 HTTP API 将请求转发给 Consul 服务器时,本质是直接查询由 Raft 保证强一致性的权威数据源。服务器会返回当前集群的最新、一致状态,跳过 Gossip 协议的异步传播延迟。

总结

Gossip 协议的设计目标是去中心化、高容错的成员发现,牺牲了实时性;而 Consul 服务器借助 Raft 实现强一致性。因此,依赖 Gossip 的 members 命令可能因异步传播延迟看到旧状态,而 HTTP API 直接查询服务器(Raft 强一致存储)能获取最新一致结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
~ ······················································································································································································································································································· 00:46:14
❯ curl localhost:8500/v1/catalog/nodes
[
{
"ID": "270aaffe-66cf-67ce-80a9-d3e1985cd60e",
"Node": "GJVXWHGXQ1",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"lan_ipv4": "127.0.0.1",
"wan": "127.0.0.1",
"wan_ipv4": "127.0.0.1"
},
"Meta": {
"consul-network-segment": "",
"consul-version": "1.20.5"
},
"CreateIndex": 13,
"ModifyIndex": 14
}
]%

除了HTTP API之外,还可以使用DNS接口查询节点。 请注意,必须确保将DNS查找默认情况下指向在端口8600上运行的Consul代理的DNS服务器。 DNS条目的格式(如“Armons-MacBook-Air.node.consul”)将在后面详细介绍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
❯ dig @127.0.0.1 -p 8600 localhost.localdomain.node.consul

; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 8600 localhost.localdomain.node.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 29138
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;localhost.localdomain.node.consul. IN A

;; AUTHORITY SECTION:
consul. 0 IN SOA ns.consul. hostmaster.consul. 1742318020 3600 600 86400 0

;; Query time: 7 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Wed Mar 19 01:13:40 CST 2025
;; MSG SIZE rcvd: 112




七、停止 Agent

Consul Agent 的两种停止方式及影响

  1. 优雅停止(使用 Ctrl-C)
    • 操作本质:通过发送中断信号(Ctrl-C)告知 Agent 停止运行。
    • 集群交互:Agent 会主动通知集群内其他成员 “自己即将离开”。
    • 后续处理:
      • 该节点的服务注册信息、健康检查记录会从 Consul 目录中直接删除,相当于向集群明确宣告 “该节点不再参与服务”。
      • 对集群其他成员的影响较小,属于 “友好退出”,其他节点能正常更新集群成员状态。
  2. 强行终止(直接杀死进程)
    • 操作本质:未经通知直接结束 Agent 进程(如 kill -9)。
    • 集群交互:集群其他成员无法收到节点离开的通知,只能通过心跳检测等机制发现该节点 “故障”。
    • 后续处理:
      • 该节点的服务健康状态会被标记为 “关键(Critical)”,但服务注册信息不会从目录中删除
      • Consul 会持续尝试重新连接这个 “故障节点”,期待它从网络波动等临时问题中恢复。若节点实际已退出,这种重试会浪费资源,且集群无法及时感知真实状态。

服务器角色的特殊注意事项

当 Agent 以服务器(Server)角色运行时,优雅停止尤为重要:

  • 一致性协议影响:Consul 服务器通过 Raft 等协议维护集群数据一致性。若服务器被强行终止,可能导致 Raft 协议的日志同步、领导者选举等流程中断,引发集群可用性问题(如短暂无法处理写请求)。
  • 安全操作要求:为避免破坏集群一致性,添加或删除服务器节点需遵循官方指南的规范流程(如先优雅退出,再执行删除操作),确保集群在节点变更过程中仍能稳定运行。




八、注册服务

8.1 服务定义

服务可以通过提供服务定义或通过对HTTP API进行适当的调用来注册。

服务定义是注册服务最常用的方式,所以我们将在这一步中使用这种方法。 我们将建立在上一步中介绍的代理配置。

首先,为Consul配置创建一个目录。 Consul将所有配置文件加载到配置目录中,因此Unix系统上的一个通用约定是将目录命名为/etc/consul.d(.d后缀意味着“该目录包含一组配置文件”)。