Spring Cloud Consul 集群

Spring Cloud Consul 集群

Spring Cloud Consul 是通过Consul 的 REST API(JAVA )封装的一套解决方案,基于此可以通过Java来使用Consul的功能

Spring Cloud Consul 核心模块

  • spring-cloud-consul-binder
    Consul事件功能封装
  • spring-cloud-consul-config
    Consul配置功能封装
  • spring-cloud-consul-core
    基础配置和健康检查模块
  • spring-cloud-consul-discovery
    Consul服务治理功能封装

Spring Cloud Consul Discovery(服务治理)

根据功能划分为服务发现服务注册两个部分。

  • Spring Cloud Consul Discovery主要功能分布
服务发现
心跳报告 默认不开启(TtlScheduler)
服务发现 ConsulServerList
服务注册
服务上线 ConsulServiceRegistry
服务下线 ConsulServiceRegistry

服务注册

服务启动时,会通过ConsulServiceRegistry.register()向Consul注册自身的服务。并告诉Consul以下信息:

信息 描述 默认
ID 服务ID 服务名-端口号
Name 服务名 系统名
Tags 服务器标签 [secure = false]
Address 服务地址 主机名(如果没有,则返回IP)
Port 服务端口 服务web端口
Check 健康信息检测,包括Interval(健康检查间隔)和HTTP(健康检查地址)

通常,不需要配置,Consul会有默认值,但某些场景还是需要自己指定的,下面介绍consul discovery常见配置和适用场景:

配置项 修改方法
省略如下前缀:spring.cloud.consul.discovery
适用场景
Address prefer -ip-address=true 需要注册IP地址而非主机名
Address ip-address=${HOST_IP} 如果服务部署在docker容器中,需要告诉consul真实的地址
Port port=${HOST_PORT} 如果服务部署在docker容器中,需要告诉consul真实的端口
Tags tags=${GROUP},test 如果需要给相同服务进行分组,可以使用该配置给服务打不同标签,不同标签之间用英文逗号间隔
Check health-check-interval=20s Consul默认对服务的健康检查是10s,可以通过修改此值进行配置
Check health-check-path=/health.json Consul默认健康检查路径是/actuator/health,如果没有使用spring-cloud-actuator,或由于其他原因定制健康检测路径,通过修改此值配置

演示自定义注册信息

通过修改上面的cloud-csl-provider项目体现自定义注册功能

  • 修改bootstrap.yml(左边是修改前,右边是修改后)
    在这里插入图片描述
    discovery.ip-address和discovery.port还存在疑问后续补充。

  • 修改HelloController.java

/**
 * @description:自定义健康检查,默认的请求地址是/actuator/health
 * @version 1.0
 * @date: 2019/1/17 下午12:58
 * @mofified By:
 */
@GetMapping("/health")
public String health() {
    logger.info("自定义健康检查");
    return "SUCCESS";
}

删除原来接口为/actuator/health的函数,用上面代码替换

  • 启动provider,登入consul控制台
    在这里插入图片描述
    可以看到,provider注册成功,再观察IDEA控制台健康检查时间和自定义路径也生效来。
    secure表示是否是https协议。它取自配置spring.clould.consul.discovery.scheme,默认值是http。所以显示false,如果服务器使用https,就一定要配置spring.clould.consul.discovery.scheme,secure才会等于true。

思考

  • 集群环境下,Consul如何配置?
  • provider的yml中指的docker consul,是不是一个集群?如果是,集群内部如何部署的?provider这样配置是不是就注册到集群内了?
  • 反观admin,是通过Feign来做的服务调用,service中使用@FeignClient注解来找到对应的consul provider,yml配置中指向的consul是本机端口为8500的consul,所以feign找的应该是consul中配置名为consul-provider的servies,那么admin的consul配置指向docker的consul集群,是不是就直接就可以消费docker集群内的服务呢?

服务发现

Spring Cloud Consul提供两种方式提供服务发现功能:RibbionDiscoveryClient

使用 如何做到服务发现
Feign / @LoadBalanced 默认使用的是ConsulServerList.java提供的服务发现逻辑
自定义客户端发现 可以使用Discovery-Client实现

演示服务发现

实现上面介绍的两种方式来做到服务发现

Feign / @LoadBalanced

演示项目简介

项目 端口 描述 代码地址
cloud-csl-provider-tag-1 8081 服务提供者1号 https://github.com/FrankCy/cloud-master/tree/master/cloud-csl-provider-tag-1
cloud-csl-provider-tag-2 8082 服务提供者2号 https://github.com/FrankCy/cloud-master/tree/master/cloud-csl-provider-tag-2
cloud-csl-consumer-ribbon 8083 服务调用者(客户端负载均衡器) https://github.com/FrankCy/cloud-master/tree/master/cloud-csl-consumer-ribbon

cloud-csl-provider-tag-1

不多赘述了,导包参照上面的例子。

  • 主程序(标准写法)
@SpringBootApplication
public class ConsulProviderTag1Application {
    public static void main(String[] args) {
        SpringApplication.run(ConsulProviderTag1Application.class, args);
    }    
}
  • Controller
@RestController
public class Tag1Controller {

    private static final Log logger = LogFactory.getLog(Tag1Controller.class);

    /**
     * @description:自定义健康检查,默认的请求地址是/actuator/health
     * @version 1.0
     * @date: 2019/1/17 下午12:58
     * @mofified By:
     */
    @GetMapping("/actuator/health")
    public String health() {
        logger.info("自定义健康检查");
        return "SUCCESS";
    }

    /**
     * @description:提供 sayHello 服务:根据对方传来的名字 XX,返回:hello XX
     * @version 1.0
     * @date: 2019/1/16 下午3:02
     * @mofified By:
     */
    @GetMapping("/sayHello")
    public String sayHello(String name){
        logger.info("sayHello被请求了");
        return "hello," + name + ", 我是Tag1";
    }
}
  • yml
server:
  port: 8081

spring:
  # 定义应用名称为consul-provider,多个provider的名称要相同
  application:
    name: consul-provider
  cloud:
    consul:
      host: 127.0.0.1
      port: 8500
      # 使用discovery,指定tag是tag1
      discovery:
        tags: tag1

cloud-csl-provider-tag-2
cloud-csl-provider-tag-1相同代码copy一份,修改部分:

  • yml
server:
  # 修改端口,实际发布时端口也要不一样
  port: 8082

spring:
  # 还叫consul-provider,实际发布时也是如此,服务提供者名字相同,这样会向consul注册2个相同服务
  application:
    name: consul-provider
  cloud:
    consul:
      host: 127.0.0.1
      port: 8500
      # tag改为tag2,代表是另外一个tag
      discovery:
        tags: tag2

只需要改这一个,但是为了方便学习,我还改了一下controller

  • Controller(只写出修改部分)
/**
 * @description:提供 sayHello 服务:根据对方传来的名字 XX,返回:hello XX
 * @version 1.0
 * @date: 2019/1/16 下午3:02
 * @mofified By:
 */
@GetMapping("/sayHello")
public String sayHello(String name){
    logger.info("sayHello被请求了");
    //只修改了这个输出语句,告诉请求方是tag2
    return "hello," + name + ", 我是Tag2";
}

cloud-csl-consumer-ribbon

因为要使用Feign,所以pom要把Feign引入

  • pom.xml
<properties>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-commons</artifactId>
        <version>2.0.2.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Consul服务发现和服务注册 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        <version>2.0.1.RELEASE</version>
    </dependency>

    <!-- Feign依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
        <version>2.0.0.RELEASE</version>
    </dependency>

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
  • 主程序(@EnableFeignClients注解
@EnableFeignClients
@SpringBootApplication
public class ConsulConsumerRibbonApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsulConsumerRibbonApplication.class, args);
    }
}
  • Controller
@RestController
public class ConsumerRibbonController {

    private static final Log logger = LogFactory.getLog(ConsumerRibbonController.class);

    /**
     * 方式一:Feign方式调用
     */
    @Autowired
    private HelloService helloService;

    /**
     * 方式二:RestTemplate方式调用
     */
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 创建RestTemplate Bean,并用@LoadBalanced注解
     * @return
     */
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    /**
     * 接收前端参数,使用Feign方式调用远程接口,并返回调用结果
     * @param name
     * @return
     */
    @GetMapping (value = "/hello1")
    public String hello1(String name) {
        return helloService.sayHello(name);
    }

    /**
     * 接收前端参数,使用RestTemplate方式调用远程接口,并返回调用结果
     * @param name
     * @return
     */
    @GetMapping (value = "/hello2")
    public String hello2(String name) {
        return restTemplate.getForObject("http://consul-provider/sayHello?name="+name, String.class);
    }

    /**
     * 健康检查
     * @return
     */
    @GetMapping("/actuator/health")
    public String health(){
        logger.info("consumer-ribbon客户端自动检测");
        return "SUCCESS";
    }

}

Feign实现是/hello1,RestTemplate实现是/hello2

  • yml
server:
  port: 8083

spring:
  application:
    name: consul-consumer-ribbon
  cloud:
    consul:
      host: 127.0.0.1
      port: 8500
      discovery:
        # 通过名称来访问consul中多个相同应用名称的provider
        service-name: consul-provider
        # 这里要设置false,否则会把自己注册到consul中,并提供服务,轮询时会找自己
        register: false

启动并访问

总结
Feign / @LoadBalanced 实现服务发现演示就结束了,并做到了客户端负载均衡,方式是轮询(应该是默认方式,我没有去考正)

自定义客户端发现

演示项目简介

项目 端口 描述 代码地址
cloud-csl-provider-tag-1 8081 服务提供者1号 https://github.com/FrankCy/cloud-master/tree/master/cloud-csl-provider-tag-1
cloud-csl-provider-tag-2 8082 服务提供者2号 https://github.com/FrankCy/cloud-master/tree/master/cloud-csl-provider-tag-2
cloud-consumer-discovery-client 8084 自定义客户端 https://github.com/FrankCy/cloud-master/tree/master/cloud-consumer-discovery-client

cloud-consumer-discovery-client

  • pom.xml
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        <version>2.0.0.RELEASE</version>
    </dependency>
</dependencies>
  • 主程序
@SpringBootApplication
public class ConsulDiscoveryClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsulDiscoveryClientApplication.class, args);
    }
}
  • Controller
@RestController
public class DiscoveryClientController {

    private static final Log logger = LogFactory.getLog(DiscoveryClientController.class);

    /**
     * DiscoveryClient 会在程序启动时初始化
     */
    @Autowired
    private DiscoveryClient discoveryClient;

    /**
     * 获取服务
     * @param serviceId
     * @return
     */
    @GetMapping("/getServer")
    public List<ServiceInstance> getServer(String serviceId) {
        logger.info("传递的serviceId : " + serviceId);
        return discoveryClient.getInstances(serviceId);
    }
    
    /**
     * 健康检查
     * @return
     */
    @GetMapping("/actuator/health")
    public String health(){
        logger.info("DiscoveryClientController 自动检测");
        return "SUCCESS";
    }
}

private DiscoveryClient discoveryClient是关键,在程序启动时初始化,我们就可以通过这个对象获取服务了,即自定义客户端服务发现。

启动并访问

总结
自定义服务发现演示代码就这些,我们怎么消费服务呢?后面会介绍


Spring Cloud Consul Config(配置)

Spring Cloud Consul Config 配置刷新原理

  • Spring Cloud Consul Config是通过HTTP的方式跟Consul交互的,更新如何做到的实时?
    并不是实时生效的! 并不是实时生效的! 并不是实时生效的!
    org.springframework.cloud.consul.config.ConfigWathc定时方法watchConfigKeyValues(),默认每个1秒执行一次(可通过spring.cloud.consul.config.watch.delay自定义时间),去Consul获取最新的信息,配置发生变化时,Spring通过ApplicationEventPublisher重新刷新配置。Consul Config通过这种方式实现实时生效的效果。
  • 客户端如何发现配置变更了?
    Consul返回的数据中每一项配置都会有"consulIndex"属性,如果更新,属性就会自增。
    Spring Cloud Consul Config 就是通过缓存“consulIndex”判断配置是否发生改变。

Spring Cloud Consul Config 高级配置

实际投产时,分布式系统会有多个配置,虽然Consul支持K/V的配置,但是我们要每一个配置都手动一个一个录入的话未满太麻烦,那我们如何实现批量配置或简易配置呢:
通过将yml或properties放在‘V’中实现批量配置操作
下面通过代码来说明如何配置(类似Profiles功能)
创建cloud-csl-config-customize项目

  • pom.xml
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
</properties>
<dependencies>
    <!-- 因为只用到了Spring Cloud Consul 的配置功能, 这里不全部引入-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-config</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<!-- 管理依赖 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Finchley.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<!--注意: 这里必须要添加,否则各种依赖有问题 -->
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/libs-milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
  • 主程序
@SpringBootApplication
public class ConsulConfigCustomizeApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsulConfigCustomizeApplication.class, args);
    }
}
  • Controller
@RestController
public class ConsulConfigCustomizeController {

    /**
     * 日志
     */
    private static final Log logger = LogFactory.getLog(ConsulConfigCustomizeController.class);

    /**
     * 读取远程配置
     */
    @Value("${foo.bar.name}")
    private String name;

    /**
     * 将配置展示在页面
     * @return
     */
    @GetMapping("/getName")
    public String getName() {
        logger.info("配置是:" + name);
        return name;
    }

}

上面都是常规的代码,请注意下面yml的配置,和注释

  • 创建application.yml
server:
  port: 8081

spring:
  profiles:
    # 指定启动时的profiles是test,这样服务启动时,就会向consul去找test的配置
    active: test
  • 创建bootstrap.yml
spring:
  application:
    # 此处consul中config会读
    name: consul-config-customize
  cloud:
    consul:
      config:
        # enabled 设置是否启用config,默认是true
        enabled: true
        # Consul中Value配置格式为yaml,支持类型( YAML、PROPERTIES、KEY-VALUE、FILES)
        format: YAML
        # Consul配置文件目录为configuration,默认是config
        prefix: configuration
        # 去该目录下查找缺省配置,默认为application
        default-context: app
        # profiles配置分隔符,默认为','
        profile-separator: ':'
        # 如果指定配置格式为yaml或者properties,则需要该值作为key,默认为data
        data-key: data
  • 配置consul (K/V)
    • 配置dev
      在这里插入图片描述
    • 配置test
      在这里插入图片描述
  • 启动程序
    系统默认设置端口是8081,active=test,根据config配置,端口会编程8083,并且获取到foo.bar.name的参数,我们试试
    • 启动的控制台
      在这里插入图片描述
    • 访问 http://localhost:8083/getName
      在这里插入图片描述
      可看出,端口变成8083了,并且读到test的配置信息,我又测试了一下dev,对应改变也生效,至此,配置就做完了,是模拟profiles的一种操作。
项目 端口 描述 代码地址
cloud-csl-config-customize 8082 / 8083
取决application.yml中的active
profils配置 https://github.com/FrankCy/cloud-master/tree/master/cloud-csl-config-customize

猜你喜欢

转载自blog.csdn.net/Cy_LightBule/article/details/86598174