首页 > 其他 > 详细

微服务框架——Consul

时间:2021-04-18 22:25:43      阅读:22      评论:0      收藏:0      [点我收藏+]
技术分享图片

背景:

Netflix Eureka 2.x官方宣告停止开发,但其对国内用户影响很小,一方面国内大都使用的是Eureka 1.x系列,并且官方也在积极维护1.x 。

另一方面,Spring Cloud支持很多服务发现的软件,Eureka只是其中之一,下面是Spring Cloud支持的服务发现软件以及特性对比。

常见的服务注册中心:

  • Netflix Eureka
  • Alibaba Nacos
  • HashiCorp Consul
  • Apache Zookeeper
  • CoreOS Etcd
  • CNCF CoreDNS
特性 Eureka Nacos Consul Zookeeper
CAP AP CP + AP CP CP
健康检查 Client Beat TCP/HTTP/MySQL/Client Beat TCP/HTTP/gRPC/Cmd Keep Alive
雪崩保护
自动注销实例 支持 支持 不支持 支持
访问协议 HTTP HTTP/DNS HTTP/DNS TCP
监听支持z 支持 支持 支持 支持
多数据中心 支持 支持 支持 不支持
跨注册中心同步 不支持 支持 支持 不支持
Spring Cloud集成 支持 支持 支持 支持

Consul介绍:

Consul是HashoCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置,与其它分布式服务注册于发现的方案

相比,Consul的方案更"一站式",内置了服务注册于发现框架、分布式一致性协议框架、健康检查、key/value存储、多

数据中心方案,不再需要依赖其它工具(比如Zookeeper等),使用起来也较为简单。

Consul使用Go语言编写,因此具有天然的可移植性(支持Linux、Windows和Mac OS),安装包仅包含一个可执行文件,方

便部署,与Docker等轻量级容器可以无缝配合。

Consul特性:

  • Raft算法;
  • 服务注册与发现;
  • 健康检查;
  • Key/Value存储;
  • 多数据中心;
  • 支持http和dns协议;
  • 官方提供web管理界面;

Consul角色:

  • Client:客户端,无状态,将http和dns接口请求转发给局域网内的服务端集群。

  • Server:服务端,保存配置信息,高可用集群,每个数据中心的server数量推荐为3个到5个。

    技术分享图片

    首先,上图中有两个数据中心,分别是Datacenter1和Datacenter2。Consul非常好的支持多个数据中心,每个数据中

    心内,有客户端和服务端,服务器一般为3~5个,这样可以在稳定和性能上达到平衡,因为更多的机器会使数据同步

    变慢,不过客户端是没有限制的,可以有成千上万个。

    数据中心内的所有节点都会加入到Gossip(流言)协议,这就意味着有一个Gossip池,其中包含这个数据中心所有的节

    点。客户端不需要去配置服务器地址信息,发现服务工作会自动完成。故障检测节点的工作不是放在服务器端,而是

    分布式的,这使得失败检测相对于本地化的心跳机制而言更具可扩展性。在选择Leader这种重要的事情发生的时候,

    数据中心被用作消息层来做消息广播。

    每个数据中心内的服务器都是单个Raft中节点集的一部分,这意味着他们一起工作,选择一个单一的领导者—一个具

    有额外职责的选定的服务器。Leader负责处理所有查询和事物,事物也必须作为同步协议的一部分复制到节点集中的

    所有节点。由于这个要求,当非Leader服务器接收到RPC请求时,就会将请求转发给集群Leader。

    服务端节点同时也作为WAN Gossip池的一部分,WAN池和LAN池不同的是,它针对网络高延迟做了优化,而且只包

    含其他Consul服务器的节点。这个池的目的是允许数据中心以最少的消耗方式发现对方。启动新的数据中心与加入现

    有的WAN Gossip一样简单。因为这些服务器都在这个池中运行,它还支持跨数据中心请求。当服务器收到不同数据

    中心的请求时,它会将其转发到正确数据中心中的随机服务器,那个服务器可能会转发给本地的Leader。

Consul工作原理:

技术分享图片
  1. 服务发现与注册:

    当服务Producer启动时,会将自己的ip/host等信息通过发送post请求告知Consul,Consul接收到Producer的注册信

    息后,每隔10s(默认)会向Producer发送一个健康检查的请求,检查Producer是否健康。

  2. 服务调用:

    当Consumer请求Producer时,会先从Consul中拿到存储Producer服务的ip和port的临时表(temp table),从temp

    table表中任选一个Producer的ip和port,然后根据这个ip和port发送访问请求。temp table表只包含通过了健康检查

    的Producer信息,并且每隔10s(默认)更新。

Consul安装:

Eureka其实就是个Servlet程序,运行在Servlet容器中;Consul则是用go语言编写的第三方工具需要单独安装使用。

  1. 下载:

    wget https://releases.hashicorp.com/consul/1.9.4/consul_1.9.4_linux_amd64.zip
    
  2. 解压安装包:

    unzip consul_1.9.4_linux_amd64.zip
    
  3. 单节点启动:

    ./consul agent -dev -client=0.0.0.0  # -dev表示开发模式运行,另外还有-server表示服务模式运行
    
    • -dev:表示以开发模式运行Consul。
    • -client:表示可以进行服务注册与拉取的客户端ip,设置成-client=0.0.0.0表示对ip不设限。
  4. 访问consul管理后台:

    技术分享图片

Consul入门案例:

  1. 创建项目:

    我们创建聚合项目来讲解Consul,首先创建一个pom父工程:

    技术分享图片

    项目创建成功后,删除src目录。

  2. 查看Spring Cloud支持的Spring Boot版本:

    技术分享图片 技术分享图片
  3. 添加依赖——pom.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <!-- 项目坐标位置 -->
        <groupId>org.zjg</groupId>
        <!-- 项目模块名称 -->
        <artifactId>consul-demo</artifactId>
        <!-- 项目版本名称 快照版本SNAPSHOT、正式版本RELEASE -->
        <version>1.0-SNAPSHOT</version>
    
        <!-- 继承 spring-boot-starter-parent 依赖 -->
        <!-- 使用继承方式,实现复用,符合继承的都可以被使用 -->
        <parent>
            <artifactId>spring-boot-starter-parent</artifactId>
            <groupId>org.springframework.boot</groupId>
            <version>2.4.3</version>
        </parent>
    
        <!--
             集中定义以来组件版本号,但不引入
             在子工程中用到声明的依赖时,可以不加依赖的版本号,
             这样可以统一管理工程中用到的依赖版本
         -->
        <properties>
            <spring-cloud.version>2020.0.2</spring-cloud.version>
        </properties>
    
        <!-- 项目依赖管理 父项目只是声明依赖 -->
        <dependencyManagement>
            <dependencies>
                <!-- spring cloud 依赖 -->
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
    </project>
    
  4. 服务提供者service-provider:

    • [ ] 创建项目:

      技术分享图片 技术分享图片 技术分享图片
    • [ ] 添加依赖——pom.xml:

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
          <artifactId>service-provider</artifactId>
      
          <!-- 集成父依赖 -->
          <parent>
              <artifactId>consul-demo</artifactId>
              <groupId>org.zjg</groupId>
              <version>1.0-SNAPSHOT</version>
          </parent>
      
          <!-- 项目依赖 -->
          <dependencies>
      
              <!-- spring cloud consul 依赖 -->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-consul-discovery</artifactId>
              </dependency>
      
              <!-- spring boot actuator 依赖 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-actuator</artifactId>
              </dependency>
      
              <!-- spring boot web 依赖 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
      
              <!-- lombok 依赖 -->
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
              </dependency>
      
              <!-- spring boot test 依赖 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
                  <exclusions>
                      <exclusion>
                          <groupId>org.junit.vintage</groupId>
                          <artifactId>junit-vintage-engine</artifactId>
                      </exclusion>
                  </exclusions>
              </dependency>
      
          </dependencies>
      
      
      </project>
      
    • [ ] 配置文件——application.yml:

      server:
        port: 7070                                                 # 服务请求端口
      spring:
        application:
          name: service-provider                                   # 应用名称
        # 配置consul注册中心
        cloud:
          consul:
            host: 192.168.1.93                                     # 注册中心的访问地址
            port: 8500                                             # 注册中心的访问端口
            # 服务提供者信息(将自身注册到服务注册中心)
            discovery:
              register: true                                       # 是否需要注册
              instance-id: ${spring.application.name}-01           # 注册实例 id(必须唯一)
              service-name: ${spring.application.name}             # 服务名称
              port: ${server.port}                                 # 服务端口
              prefer-ip-address: true                              # 是否使用ip地址注册
              ip-address: ${spring.cloud.client.ip-address}        # 服务请求ip
              healthCheckInterval: 10s                             # 健康检查的间隔时间,默认10s
              health-check-url: http://${spring.cloud.client.ip-
                                address}:${server.port}/actuator/health
      
    • [ ] 实体类——Product.java:

      package com.zjg.pojo;
      
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      
      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class Product {
      
          private Integer id;
      
          private String productName;
      
          private Integer productNum;
      
          private Double productPrice;
      
      }
      
    • [ ] 编写服务——ProductService.java:

      package com.zjg.service;
      
      import com.zjg.pojo.Product;
      import org.springframework.stereotype.Service;
      import java.util.Arrays;
      import java.util.List;
      
      /**
       * 商品服务
       */
      
      @Service
      public class ProductService {
      
          /**
           * 查询商品列表
           */
          public List<Product> selectProductAll(){
              return Arrays.asList(
                      new Product(1,"华为手机",1,5800D),
                      new Product(2,",联想笔记本",1,6888D),
                      new Product(3,"小米平板",5,2020D)
              );
          }
      
      }
      
    • [ ] 控制层——ProductController.java:

      package com.zjg.controller;
      
      import com.zjg.pojo.Product;
      import com.zjg.service.ProductService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      import java.util.List;
      
      @RestController
      @RequestMapping(value = "/product")
      public class ProductController {
      
          @Autowired
          private ProductService productService;
      
          /**
           * 查询商品列表
           */
          @GetMapping(value = "/selectProductAll")
          public List<Product> selectProductAll(){
              return productService.selectProductAll();
          }
      
      }
      
    • [ ] 启动类——ServiceProviderApplication.java:

      package com.zjg;
      
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      
      @SpringBootApplication
      public class ServiceProviderApplication {
      
          public static void main(String[] args) {
              SpringApplication.run(ServiceProviderApplication.class, args);
          }
      
      }
      
    • [ ] 访问:

      技术分享图片
  5. 服务消费者service-consumer:

    • [ ] 创建项目:

      技术分享图片 技术分享图片 技术分享图片
    • [ ] 添加依赖——pom.xml:

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
          <artifactId>service-consumer</artifactId>
      
          <!-- 集成父依赖 -->
          <parent>
              <artifactId>consul-demo</artifactId>
              <groupId>org.zjg</groupId>
              <version>1.0-SNAPSHOT</version>
          </parent>
      
          <!-- 项目依赖 -->
          <dependencies>
      
              <!-- spring cloud consul 依赖 -->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-consul-discovery</artifactId>
              </dependency>
      
              <!-- spring boot actuator 依赖 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-actuator</artifactId>
              </dependency>
      
              <!-- spring boot web 依赖 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
      
              <!-- lombok 依赖 -->
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
              </dependency>
      
              <!-- spring boot test 依赖 -->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
                  <exclusions>
                      <exclusion>
                          <groupId>org.junit.vintage</groupId>
                          <artifactId>junit-vintage-engine</artifactId>
                      </exclusion>
                  </exclusions>
              </dependency>
      
          </dependencies>
      
      
      </project>
      
    • [ ] 配置文件——application.yml:

      server:
        port: 7080                                                 # 服务请求端口
      spring:
        application:
          name: service-consumer                                   # 应用名称
        # 配置consul注册中心
        cloud:
          consul:
            host: 192.168.1.93                                     # 注册中心的访问地址
            port: 8500                                             # 注册中心的访问端口
            # 服务提供者信息(将自身注册到服务注册中心)
            discovery:
              register: false                                      # 是否需要注册
      
    • [ ] 实体类——Order.java:

      @Data
      @NoArgsConstructor
      @AllArgsConstructor
      public class Order {
      
          private Integer id;
      
          private String orderNo;
      
          private String orderAddress;
      
          private Double totalPrice;
      
          private List<Product> productList;
      
      }
      
    • [ ] 编写服务——OrderService.java:

      package com.zjg.service;
      
      import com.zjg.pojo.Order;
      import com.zjg.pojo.Product;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.core.ParameterizedTypeReference;
      import org.springframework.http.HttpMethod;
      import org.springframework.http.ResponseEntity;
      import org.springframework.stereotype.Service;
      import org.springframework.web.client.RestTemplate;
      import java.util.List;
      
      /**
       * 订单服务
       */
      
      @Service
      public class OrderService {
      
          @Autowired
          private RestTemplate restTemplate;
      
          /**
           * 根据主键查询订单
           */
          public Order selectOrderById(Integer id){
              return new Order(id,"order-001","中国",22788D,
                      selectProductByLoadBalancerAnnotation());
          }
      
          /**
           * 通过RestTemplate调用Consul服务注册中心的service-provider服务
          */
          private List<Product> selectProductByLoadBalancerAnnotation(){
              ResponseEntity<List<Product>> response = restTemplate.exchange(
                      "http://service-provider/product/selectProductAll",
                      HttpMethod.GET,
                      null,
                      new ParameterizedTypeReference<List<Product>>() {
                      });
              return response.getBody();
          }
      
      }
      
    • [ ] 控制层——ProductController.java:

      package com.zjg.controller;
      
      import com.zjg.pojo.Order;
      import com.zjg.service.OrderService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.web.bind.annotation.GetMapping;
      import org.springframework.web.bind.annotation.PathVariable;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      @RestController
      @RequestMapping(value = "/order")
      public class OrderController {
      
          @Autowired
          private OrderService orderService;
      
          /**
           * 根据主键查询订单
           */
          @GetMapping(value = "/{id}")
          public Order selectOrderById(@PathVariable(value = "id") Integer id){
              return orderService.selectOrderById(id);
          }
      
      }
      
    • [ ] 启动类——ServiceConsumerApplication.java:

      package com.zjg;
      
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      
      @SpringBootApplication
      public class ServiceConsumerApplication {
      
          @Bean
          @LoadBalanced
          public RestTemplate restTemplate(){
              return new RestTemplate();
          }
        
          public static void main(String[] args) {
              SpringApplication.run(ServiceConsumerApplication.class, args);
          }
      
      }
      

      Spring并不会自动注入RestTemplate,如果要使用RestTemplate需要通过@Bean注解手动注入。

    • [ ] 访问:

      技术分享图片

Consul集群:

技术分享图片

上图是一个简单的Consul Cluster架构,Consul Cluster有Server和Client两种角色,无论是Server还是Client,统称为

Agent,Consul Client是相对无状态的,只负责转发RPC到Consul Server,所以Client资源开销很少。Consul

Server是一个有一组扩展功能的代理,这些功能包括参与Raft选举、维护集群状态、响应RPC查询,与其他数据中心交互

WAN Gossip、转发查询给Leader或者远程数据中心。

每个数据中心,Client和Server是混合的。一般建议有3~5台Server。这事基于有故障情况下的可用性和性能之间的权衡结

果,因为越多的机器加入达成共识越慢,Server之间会选举出一个Leader。然而并不限制Client的数量,一般建议一个微

服务对应一个Client,他们可以很容易的扩展到成千上万台,在开发时我们绑定一组服务注册中心中的客户端即可。

  1. 环境准备:

    服务器IP Consul类型 Node节点
    192.168.1.94 server consul-server-01
    192.168.1.95 server consul-server-02
    192.168.1.96 server consul-server-03
    192.168.1.97 client consul-client-01
  2. 将下载的安装包上传到各服务器:

    scp consul root@192.168.1.94:/usr/local/dev/consul/
    scp consul root@192.168.1.95:/usr/local/dev/consul/
    scp consul root@192.168.1.96:/usr/local/dev/consul/
    scp consul root@192.168.1.97:/usr/local/dev/consul/
    
  3. 解压安装包文件:

    yum install -y unzip
    unzip consul_1.9.4_linux_amd64.zip -d /usr/local/dev/consul/    # 解压到consul目录
    
  4. 创建Consul工作目录:

    mkdir -p /usr/local/dev/consul/data
    
  5. 启动Consul Server端:

    # consul-server-01
    ./consul agent -server -bind=192.168.1.94 -client=0.0.0.0 -ui -bootstrap-expect=3 -data-dir=/usr/local/dev/consul/data/ -node=consul-server-01
    # consul-server-02
    ./consul agent -server -bind=192.168.1.95 -client=0.0.0.0 -ui -bootstrap-expect=3 -data-dir=/usr/local/dev/consul/data/ -node=consul-server-02
    # consul-server-03
    ./consul agent -server -bind=192.168.1.96 -client=0.0.0.0 -ui -bootstrap-expect=3 -data-dir=/usr/local/dev/consul/data/ -node=consul-server-03
    

    参数含义如下:

    • -server:表示以服务模式启动。
    • -bind:表示绑定到哪个ip。
    • -client:表示指定客户端的ip,0.0.0.0表示不限客户端ip,放行所有客户端ip访问。
    • -ui:表示开启web界面访问。
    • -bootstrap-expect=3:表示server集群最低节点数为3,低于这个值集群将不能正常工作()。
    • -data-dir:表示指定数据的存放目录(该目录必须存在,需提前创建好)。
    • -node:表示节点名称。
  6. 启动consul Client端:

    ./conusl agent -client=0.0.0.0 -bind=192.168.1.97 -data-dir=/usr/local/dev/consul/data/ -node=consul-client-01
    
  7. 关联集群,在consul-server-02、consul-server-03、consul-client-01节点下分别执行以下命令:

    ./conusl join 192.168.1.94
    
  8. 查看集群状态:

    ./consul members
    
    技术分享图片
  9. 访问集群中的任意节点,如果看到如下界面说明集群搭建成功:

    技术分享图片

微服务框架——Consul

原文:https://www.cnblogs.com/xiaoshuzhagen/p/14673811.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!