服务网格解决方案-Consul简介与Java 实现

1,092 阅读4分钟

这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

前言

Consul是一个服务网络解决方案,用于跨任何运行时平台和公共或私有云连接和保护服务。

Consul是分布式的,高可用的,可横向扩展的服务发现工具。

使用场景

  • 服务发现
  • 支持 Servuice Mesh

与常用的服务发现工具对比

在这里插入图片描述

Consul 架构

Consul 是 HashiCorp 推出的开源工具。 在这里插入图片描述

咱们先聊下这歌架构图

  • datacenter1datacenter2 可理解为多机房的概念
  • LAN 表示的是一个机房(datacenter)内, WAN 表示的是多个 机房 datacenter 之间的传输。
  • Gossip 是分布式系统中广泛使用的协议,据说区块链就是用这个协议,主要用来实现分布式节点和进程之间的信息交换。Gossip 协议同时满足应用层多播协议i所要求的低负载和高可靠,高扩展性要求。
  • serf 是一种用于集群成员资格、故障检测和协调的工具,它是分散的、容错的、高度可用的。可以理解是Gossip协议的实现
  • Gossip协议分为lanwan池 lan池是一个数据中心的client和server参与 wan池是两个server间参与,跨机房访问的时候实际上是转发 一个lan转发到另一个lanserver
  • 使用raft协议来维持server信息的一致性, raft工作的动图解释包括一致性同步,leader选举 log复制等

consul 工作原理

同一个consul agent程序,通过启动的时候指定不同的参数来运行server或client模式, 两种模式下,负责的事务不同

Server 模式

  • 参与共识仲裁(raft)
  • 存储集群状态(日志存储)
  • 处理查询
  • 维护各节点的链接(LAN/WAN)

Agent 模式

  • 负责通过该节点注册到 consul 的微服务的健康检查
  • 将客户端注册请求以及查询转化为对server的RPC请求
  • 维护与周边(LAN/WAN)各节点关系

服务端口 在这里插入图片描述

端口作用
8300RPC exchanges
8301LAN GOSSIP
8302WAN GOSSIP
8400RPC exchanges by the CLI
8500Used for HTTP API and web interface
8600Used for DNS server

服务发现和注册

服务启动时,会将服务的 ip/port 告诉 consulconsul 将服务进行注册,每隔一段时间检查服务的健康状态。

服务调用

consumer 请求服务提供者 provider 时,会先从 consul 上获取服务提供者的信息, 然后进行服务调用。

Java 代码实现

githubhttps://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/registeragent/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 有实现类 ConsulServiceRegistryNoopServiceRegistry, 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 是服务关闭时候摘除你的服务

欢迎关注公众号:程序员财富自由之路

参考资料