Spring Cloud Consul 是通过Consul 的 REST API(JAVA )封装的一套解决方案,基于此可以通过Java来使用Consul的功能
根据功能划分为服务发现
、服务注册
两个部分。
服务发现 | ||
心跳报告 | 默认不开启(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项目体现自定义注册功能
/**
* @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的函数,用上面代码替换
secure表示是否是https协议。
它取自配置spring.clould.consul.discovery.scheme,默认值是http。所以显示false
,如果服务器使用https,就一定要配置spring.clould.consul.discovery.scheme
,secure才会等于true。通过Feign来做的服务调用,service中使用@FeignClient注解来找到对应的consul provider,yml配置中指向的consul是本机端口为8500的consul,所以feign找的应该是consul中配置名为consul-provider的servies
,那么admin的consul配置指向docker的consul集群,是不是就直接就可以消费docker集群内的服务呢?Spring Cloud Consul提供两种方式提供服务发现功能:Ribbion
和DiscoveryClient
。
使用 | 如何做到服务发现 |
---|---|
Feign / @LoadBalanced | 默认使用的是ConsulServerList.java提供的服务发现逻辑 |
自定义客户端发现 | 可以使用Discovery-Client实现 |
实现上面介绍的两种方式来做到服务发现
演示项目简介
项目 | 端口 | 描述 | 代码地址 |
---|---|---|---|
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);
}
}
@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";
}
}
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一份,修改部分:
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
/**
* @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引入
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-commonsartifactId>
<version>2.0.2.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
<version>2.0.1.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
<version>2.0.0.RELEASEversion>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
@EnableFeignClients注解
)@EnableFeignClients
@SpringBootApplication
public class ConsulConsumerRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulConsumerRibbonApplication.class, args);
}
}
@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
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
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
<version>2.0.0.RELEASEversion>
dependency>
dependencies>
@SpringBootApplication
public class ConsulDiscoveryClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulDiscoveryClientApplication.class, args);
}
}
@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是关键,在程序启动时初始化,我们就可以通过这个对象获取服务了,即自定义客户端服务发现。
启动并访问
总结
自定义服务发现演示代码就这些,我们怎么消费服务呢?后面会介绍
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”判断配置是否发生改变。
实际投产时,分布式系统会有多个配置,虽然Consul支持K/V的配置,但是我们要每一个配置都手动一个一个录入的话未满太麻烦,那我们如何实现批量配置或简易配置呢:
通过将yml或properties放在‘V’中实现批量配置操作
。
下面通过代码来说明如何配置(类似Profiles功能)
:
创建cloud-csl-config-customize项目
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Finchley.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<repositories>
<repository>
<id>spring-milestonesid>
<name>Spring Milestonesname>
<url>https://repo.spring.io/libs-milestoneurl>
<snapshots>
<enabled>falseenabled>
snapshots>
repository>
repositories>
@SpringBootApplication
public class ConsulConfigCustomizeApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulConfigCustomizeApplication.class, args);
}
}
@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的配置,和注释
server:
port: 8081
spring:
profiles:
# 指定启动时的profiles是test,这样服务启动时,就会向consul去找test的配置
active: test
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
系统默认设置端口是8081,active=test,根据config配置,端口会编程8083,并且获取到foo.bar.name的参数,我们试试
项目 | 端口 | 描述 | 代码地址 |
---|---|---|---|
cloud-csl-config-customize | 8082 / 8083 取决application.yml中的active |
profils配置 | https://github.com/FrankCy/cloud-master/tree/master/cloud-csl-config-customize |