【SpringCloud】1.Consul——服务注册与发现
consul 基础知识
Consul是什么?
Consul是一款开源的分布式服务发现与配置管理系统,由HashiCorp公司使用Go语言开发。 官网地址:https://www.consul.io/,下载地址:https://developer.hashicorp.com/consul/install?product_intent=consul
Consul能做什么
- 服务发现:提供HTTP和DNS两种发现方式
- 健康检测
- KV存储
- 多数据中心
- 可视化WEB界面
为什么不使用Eurka了?
- Eureka停更了,不在开发新版本了
- Eureka对初学者不友好
- 我们希望注册中心能够从项目中分离出来,单独运行,而Eureka做不到这一点
安装和运行
- 下载:https://developer.hashicorp.com/consul/install?product_intent=consul 。下载对应的版本即可,【adm64版本的就是x86_64版本的,386就是x86_32版本的】 。这里,本机下载Windows adm64,1.17.1版本。
-
安装.exe文件。下载后,解压文件。在.exe目录下运行
consul -v
,即可查看consul版本以及启动consul服务
-
使用
consul agent -dev
命令,启动consul开发者模式。 -
浏览器访问8500端口,即可进入consul的UI界面。
-
为了方便可以在Path环境变量中配置consul的安装路径,即可在任意位置通过cmd打开consul 。
Consul的存储配置
在浏览器端,选择菜单栏,key/value,点击Create。
点击create后,如果新建文件夹,则末位加上"/",否则可以设置value值。完成设置后,点击【save】即可。
这里,可以看到,我们已经完成了保存。
Consul配置持久化和开机自启动
按上述的设置方法,有一个问题。当consul重启以后,设置消失。
实际应用中,重启是常常要发生的,我们肯定不希望配置是临时的。那么如何完成持久化呢?
-
在consul安装目录新建一个"mydata"文件夹。
-
新建consul_start.txt文件,文件内容如下:
@echo.服务启动...... @echo off @sc create Consul binpath= "D:\Program Files\consul\consul_1.17.1\consul.exe agent -server -ui -bind=127.0.0.1 -client=0.0.0.0 -bootstrap-expect 1 -data-dir D:\Program Files\consul\consul_1.17.1\mydata " @net start Consul @sc config Consul start= AUTO @echo.Consul start is OK......success @pause
binpath内引用的两个文件目录需要改为自己的文件目录。
-
保存之后,将后缀名修改为
.bat
,并以管理员身份运行consul_start.bat
。 -
打开任务管理器的服务选项,即可看到consul服务。
Consul的路径不能包含空格,否则无法持久化成功!(启动时无法启动成功,报1067错误)
接下来,我们就可以在“服务”中,启动或者停止Consul服务。此时,我们发现consul的存储并不会因为重启而消失。
服务注册与发现
SpringCloud项目中,如何使用服务注册与发现呢?
这里有一个例子。还未引入SpringCloud父子工程的项目,详见:https://www.bilibili.com/video/BV1gW421P7RD ,1-17课的内容。
对应的支付接口数据库:
create database db2024;
use db2024;
DROP TABLE IF EXISTS `t_pay`;
CREATE TABLE `t_pay` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`pay_no` VARCHAR(50) NOT NULL COMMENT '支付流水号',
`order_no` VARCHAR(50) NOT NULL COMMENT '订单流水号',
`user_id` INT(10) DEFAULT '1' COMMENT '用户账号ID',
`amount` DECIMAL(8,2) NOT NULL DEFAULT '9.9' COMMENT '交易金额',
`deleted` TINYINT(4) UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除标志,默认0不删除,1删除',
`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='支付交易表';
INSERT INTO t_pay(pay_no,order_no) VALUES('pay17203699','6544bafb424a');
SELECT * FROM t_pay;
项目结构如下:
支付模块对订单做一些基本操作:
消费者模块,引用这些接口。
@RestController
public class OrderController {
private String url="http://localhost:8001"; // 先硬编码,写死
@Resource
private RestTemplate restTemplate;
@PostMapping(value = "/consumer/pay/add")
@Operation(summary = "新增订单",description = "新增交易订单")
public ResultData<String> addPay(@RequestBody Pay pay) {
return restTemplate.postForObject(url + "/pay/add",pay,ResultData.class);
}
@DeleteMapping(value = "/consumer/pay/del/{id}")
@Operation(summary = "根据Id删除订单",description = "删除交易订单")
public ResultData<Integer> deletePay(@PathVariable("id") Integer id) {
return restTemplate.exchange(url + "/pay/del/" + id, HttpMethod.DELETE,null,ResultData.class).getBody();
}
@PutMapping(value = "/consumer/pay/update")
@Operation(summary = "更新订单信息",description = "更新交易订单信息")
public ResultData<String> updatePay(@RequestBody PayDTO payDTO) {
HttpEntity<PayDTO> httpEntity = new HttpEntity<>(payDTO);
return restTemplate.exchange(url + "/pay/update",HttpMethod.PUT,httpEntity,ResultData.class).getBody();
}
@GetMapping(value = "/consumer/pay/get/{id}")
@Operation(summary = "根据Id获取订单信息",description = "根据Id获取订单信息")
public ResultData<Pay> getById(@PathVariable("id")Integer id) {
return restTemplate.getForObject(url + "/pay/get/"+id ,ResultData.class,id);
}
@GetMapping(value = "/consumer/pay/gets")
@Operation(summary = "获取所有订单信息",description = "获取所有订单信息")
public ResultData<List> getPayList() {
return restTemplate.getForObject(url + "/pay/gets",ResultData.class);
}
}
这里是硬编码,定义接口号为8001。这样显然,不利于维护。我们最好获取服务名称,通过服务名称找到服务。所以,我们需要将订单支付和消费者服务先注册到Consul中。具体步骤如下:
-
引入依赖。需要被注册的模块(支付服务模块、消费者模块)引入consul依赖。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency>
-
编写配置文件.yml。这些内容适合保存在系统配置文件中(
bootstrap.yml
)spring: application: name: cloud-payment-service cloud: consul: host: localhost port: 8500 discovery: # 配置当前服务器注册里使用到的名字 server-name: ${spring.application.name} # 开启consul的健康检查 heartbeat: enabled: true ttl: 30s
-
启动类加上
@EnableDiscoveryClient
注解 ,(目前其实也可以不加这个注解)。 -
重启服务,查看是否注册成功。
-
将消费之子模块口中支付模块的url地址改为consul中注册的名字 。
private String url="http://cloud-payment-service";
-
因为consul默认支持负载均衡,所以http客户端加上
@LoadBalanced
注解 。@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
-
再次重启,测试消费者子模块调用是否成功。
服务配置
问题说明
当系统拆分之后,产生大量的微服务。每个微服务都有其对应的配置文件yml。如果其中某个配置文件发生了修改,一个个的微服务修改会很麻烦。因此,一套集中式的、动态的配置管理设施并不可少。
例如:让班级的每个同学知道,下节课不上课。麻烦的方法:向每一个同学发送消息。简单的办法:直接在班级群里@所有人。
Consul的k-v设置
Consul里的key和value设置,发挥的就是这个功能。前面我们已经叙述了key/value的使用和持久化。现在,我们在创建consul中创建二级文件夹config/
微服务名/
,然后创建data
文件,供项目测试是否能够读取 。
说明:配置默认存储到config/微服务名-配置文件版本/data中,项目启动的时候使用的哪套application.yaml文件就会来这里找对应的文件
例如:cloud-payment-service微服务如果在application.yml没有指定启用的配置文件
config/cloud-payment-service/data
例如:cloud-payment-service微服务如果在application.yml指定启用的配置文件是application-dev.yml
config/cloud-payment-service-dev/data
例如:cloud-payment-service微服务如果在application.yml指定启用的配置文件是application-prod.yml
config/cloud-payment-service-prod/data
data内文本内容为:
atguigui:
info: welcome to atguigu default, version=1
不同的环境下,值略有不同。
编写读取Consul的k-v代码
引入支持读取config的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>${spring.cloud.config.version}</version>
</dependency>
编写方法:
@GetMapping(value = "/pay/get/consul")
@Operation(summary = "获取Consul值",description = "获取Consul中的key-value值")
public String getInfoByConsul(@Value("${info}") String info){
return info;
}
测试代码
代码在本地并没有测试成功,找不到info。
动态刷新
需求说明:希望Consul的配置变动之后,项目读取的内容也能立马改变。
说明:在consul配置数据源,项目启动之后,改变数据源没作用,不知道为什么。
在主启动类加上@RefreshScope
注解【如果不生效,就放到controller上】
然后在bootstrap.yml中设置刷新的间隔【这一步不设置也可以,因为官网默认设置了1s刷新】
spring:
application:
name: cloud-payment-service
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
config:
profile-separator: '-'
format: YAML
#设置了这里,1s刷新
watch:
wait-time: 1