这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战。
前言
Consul是一个服务网络解决方案,用于跨任何运行时平台和公共或私有云连接和保护服务。
Consul是分布式的,高可用的,可横向扩展的服务发现工具。
使用场景
- 服务发现
- 支持
Servuice Mesh
与常用的服务发现工具对比
Consul 架构
Consul 是 HashiCorp 推出的开源工具。
咱们先聊下这歌架构图
datacenter1和datacenter2可理解为多机房的概念LAN表示的是一个机房(datacenter)内,WAN表示的是多个 机房datacenter之间的传输。Gossip是分布式系统中广泛使用的协议,据说区块链就是用这个协议,主要用来实现分布式节点和进程之间的信息交换。Gossip协议同时满足应用层多播协议i所要求的低负载和高可靠,高扩展性要求。serf是一种用于集群成员资格、故障检测和协调的工具,它是分散的、容错的、高度可用的。可以理解是Gossip协议的实现Gossip协议分为lan和wan池 lan池是一个数据中心的client和server参与 wan池是两个server间参与,跨机房访问的时候实际上是转发 一个lan转发到另一个lan的server端- 使用
raft协议来维持server信息的一致性, raft工作的动图解释包括一致性同步,leader选举log复制等
consul 工作原理
同一个consul agent程序,通过启动的时候指定不同的参数来运行server或client模式, 两种模式下,负责的事务不同
Server 模式
- 参与共识仲裁(raft)
- 存储集群状态(日志存储)
- 处理查询
- 维护各节点的链接(LAN/WAN)
Agent 模式
- 负责通过该节点注册到
consul的微服务的健康检查 - 将客户端注册请求以及查询转化为对server的RPC请求
- 维护与周边(LAN/WAN)各节点关系
服务端口
| 端口 | 作用 |
|---|---|
| 8300 | RPC exchanges |
| 8301 | LAN GOSSIP |
| 8302 | WAN GOSSIP |
| 8400 | RPC exchanges by the CLI |
| 8500 | Used for HTTP API and web interface |
| 8600 | Used for DNS server |
服务发现和注册
服务启动时,会将服务的 ip/port 告诉 consul ,consul 将服务进行注册,每隔一段时间检查服务的健康状态。
服务调用
consumer 请求服务提供者 provider 时,会先从 consul 上获取服务提供者的信息, 然后进行服务调用。
Java 代码实现
github 中 https://github.com/rickfast/consul-client 基于 com.orbitz.consul:consul-client:1.0.1
public class Consul {
public static final String DEFAULT_HTTP_HOST = "localhost";
public static final int DEFAULT_HTTP_PORT = 8500;
private final AgentClient agentClient;
………………
}
中的类
public class AgentClient extends BaseClient {……………………}
其中
interface Api {
@PUT("agent/service/register")
Call<Void> register(@Body Registration registration,
@QueryMap Map<String, Object> options);
@PUT("agent/service/deregister/{serviceId}")
Call<Void> deregister(@Path("serviceId") String serviceId, @QueryMap Map<String, Object> options);
@PUT("agent/check/register")
Call<Void> registerCheck(@Body Check check);
@PUT("agent/check/deregister/{checkId}")
Call<Void> deregisterCheck(@Path("checkId") String checkId);
@GET("agent/self")
Call<Void> ping();
@GET("agent/self")
Call<Agent> getAgent();
@GET("agent/checks")
Call<Map<String, HealthCheck>> getChecks();
@GET("agent/services")
Call<Map<String, Service>> getServices();
@GET("agent/members")
Call<List<Member>> getMembers();
@PUT("agent/force-leave")
Call<Void> forceLeave();
@PUT("agent/check/{state}/{checkId}")
Call<Void> check(@Path("state") String state,
@Path("checkId") String checkId,
@QueryMap Map<String, String> query);
@PUT("agent/join/{address}")
Call<Void> join(String address, Map<String, String> query);
@PUT("agent/service/maintenance/{serviceId}")
Call<Void> toggleMaintenanceMode(@Path("serviceId") String serviceId,
@QueryMap Map<String, String> query);
}
这里面暴露了基本的使用接口 agent/service/register和agent/service/deregister/{serviceId} 是最常使用的
@PUT("agent/check/{state}/{checkId}")
Call<Void> check(@Path("state") String state,
@Path("checkId") String checkId,
@QueryMap Map<String, String> query);
方法用于保持心跳 ,也就是流行语心跳检测
接收applicationevent启动完成事件
public class ServiceReadyListener implements ApplicationListener<ThriftServerReadyEvent> {
private static final Logger log = LoggerFactory.getLogger(ServiceReadyListener.class);
private final ServiceRegistry serviceRegistry;
public ServiceReadyListener(ServiceRegistry serviceRegistry) {
this.serviceRegistry = serviceRegistry;
}
@Override
public void onApplicationEvent(ThriftServerReadyEvent event) {
log.info("registering thrift service {}:{}", event.getTargetInfo(), event.getPort());
serviceRegistry.register(ServiceRegistInfo.builder()
.setTargetInfo(event.getTargetInfo())
.setPort(event.getPort())
.setTags(event.getTags())
.build());
}
}
- 其中
ServiceRegistry有实现类ConsulServiceRegistry和NoopServiceRegistry,ConsulServiceRegistry中实现了向consul注册 主要代码
private void doRegister(ServiceRegistInfo registInfo) {
long ttl = serverDiscoveryProperties.getDefaultConsulTTL();
long heartbeatInterval = Math.max(ttl/2, 1L);
String[] tags = registInfo.getTagList().toArray(new String[registInfo.getTagList().size()]);
String serviceName = registInfo.getTargetInfo().getServiceName();
String serviceID = String.format("%s-%d", serviceName, registInfo.getPort());
getAgentClient().register(registInfo.getPort(), ttl, serviceName, serviceID, tags);
keepHeartbeat(serviceID, heartbeatInterval);
deregisterWhenShutdown(serviceID);
log.info("ThriftService({}:{}:{}) is registered with {} seconds heartbeat interval.",
serviceName, registInfo.getPort(), Arrays.toString(tags), heartbeatInterval);
}
getAgentClient().register(registInfo.getPort(), ttl, serviceName, serviceID, tags);是注册keepHeartbeat(serviceID, heartbeatInterval);是定时ttl更新 类似于心跳 这样你的服务就常活了deregisterWhenShutdown是服务关闭时候摘除你的服务