【SpringCloud】1.Consul——服务注册与发现

consul 基础知识

Consul是什么?

Consul是一款开源的分布式服务发现与配置管理系统,由HashiCorp公司使用Go语言开发。 官网地址:https://www.consul.io/,下载地址:https://developer.hashicorp.com/consul/install?product_intent=consul

Consul能做什么

  1. 服务发现:提供HTTP和DNS两种发现方式
  2. 健康检测
  3. KV存储
  4. 多数据中心
  5. 可视化WEB界面

为什么不使用Eurka了?

  1. Eureka停更了,不在开发新版本了
  2. Eureka对初学者不友好
  3. 我们希望注册中心能够从项目中分离出来,单独运行,而Eureka做不到这一点

安装和运行

  1. 下载https://developer.hashicorp.com/consul/install?product_intent=consul 。下载对应的版本即可,【adm64版本的就是x86_64版本的,386就是x86_32版本的】 。这里,本机下载Windows adm64,1.17.1版本。

image

  1. 安装.exe文件。下载后,解压文件。在.exe目录下运行consul -v,即可查看consul版本以及启动consul服务
    image
    image

  2. 使用consul agent -dev命令,启动consul开发者模式。

  3. 浏览器访问8500端口,即可进入consul的UI界面。
    image

  4. 为了方便可以在Path环境变量中配置consul的安装路径,即可在任意位置通过cmd打开consul 。

Consul的存储配置

在浏览器端,选择菜单栏,key/value,点击Create。
image

点击create后,如果新建文件夹,则末位加上"/",否则可以设置value值。完成设置后,点击【save】即可。
image
image

这里,可以看到,我们已经完成了保存。
image

Consul配置持久化和开机自启动

按上述的设置方法,有一个问题。当consul重启以后,设置消失。
image

实际应用中,重启是常常要发生的,我们肯定不希望配置是临时的。那么如何完成持久化呢?

  1. 在consul安装目录新建一个"mydata"文件夹。

  2. 新建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内引用的两个文件目录需要改为自己的文件目录。

  3. 保存之后,将后缀名修改为.bat,并以管理员身份运行consul_start.bat

  4. 打开任务管理器的服务选项,即可看到consul服务。
    image

Consul的路径不能包含空格,否则无法持久化成功!(启动时无法启动成功,报1067错误)

接下来,我们就可以在“服务”中,启动或者停止Consul服务。此时,我们发现consul的存储并不会因为重启而消失。

服务注册与发现

SpringCloud项目中,如何使用服务注册与发现呢?

这里有一个例子。还未引入SpringCloud父子工程的项目,详见:https://www.bilibili.com/video/BV1gW421P7RD ,1-17课的内容。
image

对应的支付接口数据库:

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;

项目结构如下:
image

支付模块对订单做一些基本操作:
image
消费者模块,引用这些接口。

@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中。具体步骤如下:

  1. 引入依赖。需要被注册的模块(支付服务模块、消费者模块)引入consul依赖。

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-consul-discovery</artifactId>
    </dependency>
    
  2. 编写配置文件.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
    
  3. 启动类加上@EnableDiscoveryClient注解 ,(目前其实也可以不加这个注解)。

  4. 重启服务,查看是否注册成功。

image

  1. 将消费之子模块口中支付模块的url地址改为consul中注册的名字 。

    private String url="http://cloud-payment-service";
    
  2. 因为consul默认支持负载均衡,所以http客户端加上@LoadBalanced注解 。

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
       return new RestTemplate();
    }
    
  3. 再次重启,测试消费者子模块调用是否成功。

服务配置

问题说明

当系统拆分之后,产生大量的微服务。每个微服务都有其对应的配置文件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

不同的环境下,值略有不同。

1732850185270

编写读取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
posted @ 2024-11-29 15:17  陆陆无为而治者  阅读(91)  评论(0编辑  收藏  举报