Consul的简单入门
- 当Producer启动时,会向Consul发送一个post请求,告诉Consul自己的ip和Port;
- Consul接收到producer的注册后,每个10S(默认),会向producer发送一个健康检查的请求,检验Producer是否健康
- 当Consumer发送GET方式请求/api/address到Producer时,会先从Consul中拿到一个存储服务IP和Port的临时表,从表中拿到Producer的Ip和Port后再发送GET方式请求/api/address
- 该临时表每隔10s会更新,只包含有通过健康检查的Producer
1 实操 (安装Consul客户端)
实操的内容: 创建一个生产者(producer)和一个消费者(consumer). 二者通过Feign远程调用接口
从网上下载下来是一个后缀为exe
的文件,但是不要直接双击启动 ,而是在CMD窗口中找到当前的目录 使用下面的指令启动
1. 开发模式
数据保存在内存 重启之后会丢失
consul agent -dev
2. 服务模式
可以保留以前数据
consul agent -server -ui -bootstrap-expect 1 -data-dir D:\StudySoftWare\consul\data -node=n1 -bind=127.0.0.1
主页地址: http://localhost:8500/
2. 代码实现
项目结构:
这里使用的Maven的父子工程结构
1. 依赖导入
父工程:
// 这里使用2.3.1 注意这里版本不能太高, 不然好像会出现不兼容现象
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
</parent>
//
<dependencies>
<!-- 服务注册和发现的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 配置中心的依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<!-- SpringBootweb依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
2. yml配置文件
bootstrap.yml
,注意这里一定要使用bootstrap.yml
作为配置文件,因为这个名称的优先级必application.yml的高, 而我们在Consul中的配置希望是在系统一启动就去读取,所以一定一定,因为我踩过坑
消费者配置文件
server:
port: 8888
spring:
application:
name: consumer-server # 服务名
cloud:
consul:
host: 127.0.0.1 # 默认
port: 8500 # 默认
discovery:
service-name: ${spring.application.name}
heartbeat:
enabled: true # 开启心跳检测
config: # 先看标题5,配置中心的使用
enabled: true
prefix: config # 此时拼接出来的配置所在Consul地址就是 config/consumer/data.yaml
name: consumer
data-key: data
format: yaml
feign:
client:
config:
default:
connectTimeout: 3000 #单位毫秒 不设置connectTimeout会导致readTimeout设置不生效
readTimeout: 6000 #单位毫秒
提供者的配置文件
server:
port: 9999
spring:
application:
name: producer-server
cloud:
consul:
host: 127.0.0.1
port: 8500
discovery:
service-name: ${spring.application.name}
heartbeat:
enabled: true
3. 定义消费者
定义controller, 这里为了测试就先简单的返回一个字符串.
package com.yfs1024.producer.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author : Cookie
* date : 2023/6/13 19:23
* explain :
*/
@RestController
@RequestMapping
public class ProducerController {
@GetMapping
public String getDesc(){
return "你好这里是提供者";
}
}
4.定义消费者
这里因为我们用到了feign进行远程调用,所以这里需要在pom文件中添加feign的依赖
注意: 对于feign依赖的添加, 是谁想获取其他的服务就在谁的pom文件中添加feign依赖.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1. 启动类加注解开启feign
@SpringBootApplication
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
2. 定义feign客户端
注解的value属性就是想要调用服务的名称,
注意: 如果 参数列表中如果有参数, 注解不可以省略,注解不可以省略,注解不可以省略, feign不支持,
踩过坑
错: String getDesc(String name);
对: String getDesc(@RequestParam(“name”) String name);
@FeignClient("producer-server")
public interface ProducerClient {
@GetMapping
String getDesc();
}
3. 定义控制器
@RestController
@RequestMapping("/index")
//@RefreshScope
public class IndexController {
@Autowired
ProducerClient producerClient;
@GetMapping
public void getName(String name) {
System.out.println("here is consumer value ===>>" + name + producerClient.getDesc());
}
}
此时启动服务,可以看到已经注册到Consul中
此时请求方法,查看控制台
OK,实现了服务的注册以及调用.下面就差最后的使用Consul的配置中心
5.配置中心
刚才我们在Consumer的配置中其实已经配置了指定读取的配置文件的路径及名称,
config:
enabled: true
prefix: config # 此时拼接出来的配置所在Consul地址就是 config/consumer/data.yaml
name: consumer
data-key: data
format: yaml
下面就在consul中创建这样的一个文件
注意绿色的说明, 如果创建文件夹就使用
/
,后面yaml
格式的选择,以及标签的缩进,和 冒号后面的空格都不能省略
1.读取配置
使用@Value注解
@RestController
@RequestMapping("/index")
//@RefreshScope
public class IndexController {
@Value("${school.name}")
String schoolName;
@Value("${school.addr}")
String schoolAddr;
@Autowired
ProducerClient producerClient;
@GetMapping
public void getName(String name) {
System.out.println("here is consumer value ===>>" + name + producerClient.getDesc() + schoolName + schoolAddr);
}
}
控制台结果
这样其实已经成功了,但是如果我们想对配置进行热更新
应该怎么办? 其实可以测试如果使用当前方法是没有办法实现热更新的.但是可以通过下面的两种方法事项
配置热更新方式一
在类名上添加
@RefreshScope
,注解配合@Value
注解可以实现配置内容的热更新
@RestController
@RequestMapping("/index")
@RefreshScope
public class IndexController {
@Value("${school.name}")
String schoolName;
@Value("${school.addr}")
String schoolAddr;
....
}
配置热更新方式二
@ConfigurationProperties("school")
注解天然支持热更新,所以可以定义一个属性配置类,来批量的读取随后在代码中注入,调用get方法
@Configuration
@ConfigurationProperties("school")
@Data
public class ConsumerConfig {
String name;
String addr;
}