SpringCloud-consul
(尚硅谷Cloud学习笔记总结)
1. consul介绍
1.1 为什么要引入服务注册中心?
微服务所在的IP地址和端口号硬编码到订单微服务中,会存在非常多的问题
(1)如果订单微服务和支付微服务的IP地址或者端口号发生了变化,则支付微服务将变得不可用,需要同步修改订单微服务中调用支付微服务的IP地址和端口号。
(2)如果系统中提供了多个订单微服务和支付微服务,则无法实现微服务的负载均衡功能。
(3)如果系统需要支持更高的并发,需要部署更多的订单微服务和支付微服务,硬编码订单微服务则后续的维护会变得异常复杂。
所以,在微服务开发的过程中,需要引入服务治理功能,实现微服务之间的动态注册与发现,从此刻开始我们正式进入SpringCloud实战
1.2 什么是consul?
Consul 是一套开源的分布式服务发现和配置管理系统,由 HashiCorp 公司用 Go 语言开发。
提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。它具有很多优点。包括: 基于 raft 协议,比较简洁; 支持健康检查, 同时支持 HTTP 和 DNS 协议 支持跨数据中心的 WAN 集群 提供图形界面 跨平台,支持 Linux、Mac、Windows
1.3 consul能干什么
1.3.1 服务发现
提供HTTP和DNS两种发现方式
1.3.2 健康监测
支持多种方式,HTTP、TCP、Docker、壳牌脚本定制化监控
1.3.3 KV存储
Key、Value的存储方式
1.3.4 多数据中心
Consul支持多数据中心
1.3.5 可视化Web界面
2. consul下载、查看版本和启动
1.3.1 consul下载
https://developer.hashicorp.com/consul/downloads
1.3.2 consul查看版本
1.点开文件夹,点击consul.exe启动或者cmd命令
2.consul --version 查看版本
1.3.3 consul启动
consul agent -dev
访问:http://localhost:8500/。访问到页面证明启动成功。
3. 服务入住consul中心
3.1 服务提供者 8001
3.1.1 引入pom依赖
<!--SpringCloud consul discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
3.1.2 yml中的配置:
server:
port: 8001
# ==========applicationName + druid-mysql8 driver===================
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db2024?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: root
password: 123456
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
# ========================mybatis===================
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.cloud.entities
configuration:
map-underscore-to-camel-case: true
3.1.2 主启动类上加入@EnableDiscoveryClient:
package com.atguigu.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@MapperScan("com.atguigu.cloud.mapper") //importtk.mybatis.spring.annotation.MapperScan;
@EnableDiscoveryClient//入住进服务
public class Main8001
{
public static void main(String[] args)
{
SpringApplication.run(Main8001.class,args);
}
}
3.1.3 启动,成功入住
3.2 服务消费者80
3.2.1 引入pom:
<!--SpringCloud consul discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
3.2.2 yml的配置:
server:
port: 80
spring:
application:
name: cloud-consumer-order
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
prefer-ip-address: true #优先使用服务ip进行注册
service-name: ${spring.application.name}
3.2.3 主启动类上加入:
package com.atguigu.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient //该注解用于向使用consul为注册中心时注册服务
public class Main80
{
public static void main(String[] args)
{
SpringApplication.run(Main80.class,args);
}
}
3.2.4 OrderController层:
package com.atguigu.cloud.controller;
import com.atguigu.cloud.entities.PayDTO;
import com.atguigu.cloud.resp.ResultData;
import jakarta.annotation.Resource;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
public class OrderController
{
// public static final String PaymentSrc_URL="http://localhost:8001";//现在先硬编码写死。
public static final String PaymentSrc_URL="http://cloud-payment-service";//服务注册中心的微服务名称
@Resource
private RestTemplate restTemplate;
@GetMapping(value = "/consumer/pay/add")
public ResultData addOrder(PayDTO payDTO)
{
/**
* postForObject(url,request,responseType.class)
* url表示的是地址值
* request表示的是请求参数
* responseType.class表示的是响应的类型
*/
return restTemplate.postForObject(PaymentSrc_URL + "/pay/add", payDTO, ResultData.class);
}
@GetMapping(value = "/consumer/pay/get/{id}")
public ResultData getPayInfo(@PathVariable("id") Integer id)
{
/**
* getForObject(url,responseType.class,urlVariables,responseType.class)
* url表示的是地址值
* responseType.class表示的是响应的类型
* urlVariables表示的是url中的参数值
*/
return restTemplate.getForObject(PaymentSrc_URL + "/pay/get/"+id, ResultData.class,id);
}
//删除和修改,作业 全部查询
/**
* 删除
* @param id
* @return
*/
/* @GetMapping(value = "/consumer/pay/del/{id}")
public ResultData delPayInfo(@PathVariable("id") Integer id)
{
*//**
* getForObject(url,responseType.class,urlVariables,responseType.class)
* url表示的是地址值
* responseType.class表示的是响应的类型
* urlVariables表示的是url中的参数值
*//*
return restTemplate.getForObject(PaymentSrc_URL + "pay/del/"+id, ResultData.class,id);
}*/
@DeleteMapping(value = "/consumer/pay/del/{id}")
public ResultData delPayOrder(@PathVariable("id") Integer id)
{
/**
* getForObject(url,responseType.class,urlVariables,responseType.class)
* url表示的是地址值
* responseType.class表示的是响应的类型
* urlVariables表示的是url中的参数值
*/
restTemplate.delete(PaymentSrc_URL + "/pay/del/"+id);
return ResultData.success("删除成功");
}
/* @GetMapping(value = "/consumer/pay/update")
public ResultData updataOrder(PatDTO patDTO)
{
*//**
* postForObject(url,request,responseType.class)
* url表示的是地址值
* request表示的是请求参数
* responseType.class表示的是响应的类型
*//*
return restTemplate.postForObject(PaymentSrc_URL + "/pay/update", patDTO, ResultData.class);
}*/
@PutMapping(value = "/consumer/pay/update")
public ResultData updateOrder(@RequestBody PayDTO payDTO)
{
/**
* postForObject(url,request,responseType.class)
* url表示的是地址值
* request表示的是请求参数
* responseType.class表示的是响应的类型
*/
restTemplate.put(PaymentSrc_URL + "/pay/update", payDTO);
return ResultData.success(payDTO);
}
@GetMapping(value = "/consumer/pay/list")
public ResultData getPayAll()
{
/**
* getForObject(url,responseType.class,urlVariables,responseType.class)
* url表示的是地址值
* responseType.class表示的是响应的类型
* urlVariables表示的是url中的参数值
*/
return restTemplate.getForObject(PaymentSrc_URL + "/pay/list", ResultData.class);
}
// 声明一个DiscoveryClient类型的变量discoveryClient
@Resource
private DiscoveryClient discoveryClient;
// 声明一个@GetMapping类型的方法,路径为"/consumer/discovery",返回值为String类型
@GetMapping("/consumer/discovery")
public String discovery()
{
// 调用discoveryClient的getServices()方法,获取所有注册的服务名
List<String> services = discoveryClient.getServices();
// 遍历services
for (String element : services) {
// 打印出services中的元素
System.out.println(element);
}
// 打印一条分隔符
System.out.println("===================================");
// 调用discoveryClient的getInstances()方法,获取指定服务的实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("cloud-payment-service");
// 遍历instances
for (ServiceInstance element : instances) {
// 打印出instances中的元素的属性
System.out.println(element.getServiceId()+"\t"+element.getHost()+"\t"+element.getPort()+"\t"+element.getUri());
}
// 返回第一个实例的服务ID和服务端口
return instances.get(0).getServiceId()+":"+instances.get(0).getPort();
}
}
3.2.5 启动服务,成功入住:
3.2.6 配置类RestTemplateConfig注意问题!!
因为consul天生就自带负载均衡,所以我们要加入 @LoadBalanced
package com.atguigu.cloud.config;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.client.RestTemplate;
@Configuration
@LoadBalancerClient(
//下面的value值大小写一定要和consul里面的名字一样,必须一样
value = "cloud-payment-service",configuration = RestTemplateConfig.class)
//这个注解的作用是:在启动类上添加@LoadBalancerClient注解,并指定负载均衡的配置类
//cloud-payment-service指定consul中哪个服务,configuration = RestTemplateConfig.class这个类是自定义的负载均衡配置类
/*
默认的轮询负载均衡
public class RestTemplateConfig
{
@Bean
@LoadBalanced//因为consul天生就自带负载均衡,所以我们要加入 @LoadBalanced
public RestTemplate restTemplate()
{
*/
/**
* 1. 创建一个公用的RestTemplate对象
*//*
return new RestTemplate();
}
}
*/
3.3 三个注册中心异同点(CAP)
3.3.1 cap是什么?
C:Consistency(强一致性)
A:Availability(可用性)
P:Partition tolerance(分区容错性)
3.3.2 经典AP:
3.3.3 经典CP:
4. consul 统一配置
4.1 为什么需要统一配置?
微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要的配置信息才能运行,所以一套集中式的、动态的配置管理设施是必不可少的。比如某些配置文件中的内容大部分都是相同的,只有个别的配置项不同。就拿数据库配置来说吧,如果每个微服务使用的技术栈都是相同的,则每个微服务中关于数据库的配置几乎都是相同的,有时候主机迁移了,我希望一次修改,处处生效。
当下我们每一个微服务自己带着一个application.yml,上百个配置文件的管理…/(ㄒoㄒ)/~~
4.2 demo讲解
4.2.1 引入pom:
< !--SpringCloud consul config-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
4.2.2 配置yml:
4.2.2.1 bootstrap.yml
(applicaiton.yml是用户级的资源配置项
bootstrap.yml是系统级的,优先级更加高
Spring Cloud会创建一个“Bootstrap Context”,作为Spring应用的Application Context
的父上下文。初始化的时候,Bootstrap Context
负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的Environment
。
Bootstrap
属性有高优先级,默认情况下,它们不会被本地配置覆盖。Bootstrap context
和Application Context
有着不同的约定,所以新增了一个bootstrap.yml
文件,保证Bootstrap Context
和Application Context
配置的分离。
application.yml文件改为bootstrap.yml,这是很关键的或者两者共存
因为bootstrap.yml是比application.yml先加载的。bootstrap.yml优先级高于application.yml)
spring:
application:
name: cloud-payment-service
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
service-name: ${spring.application.name}
config:
profile-separator: '-' # default value is ",",we update '-'
format: YAML
# config/cloud-payment-service/data
# /cloud-payment-service-dev/data
# /cloud-payment-service-prod/data
4.2.2.2 application.yml
server:
port: 8001
# ==========applicationName + druid-mysql8 driver===================
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db2024?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
username: root
password: 123456
profiles:
#active: dev # 多环境配置加载内容dev/prod,不写就是默认default配置,这个对应的就是bootstrap.yml中的 config/cloud-payment-service-dev/data
#active: prod
active:
# ========================mybatis===================
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.cloud.entities
configuration:
map-underscore-to-camel-case: true
4.2.3 consul服务器key/value配置填写
4.2.3.1 参考规则
创建config文件夹,以/结尾
config文件夹下分别创建其它3个文件夹,以/结尾:
cloud-payment-service
cloud-payment-service-dev
cloud-payment-service-prod
上述3个文件夹下分别创建data内容,data不再是文件夹
4.2.3.2 测试controller类:
@Value("${server.port}")
private String port;
@GetMapping(value = "/pay/get/info")
private String getInfoByConsul(@Value("${atguigu.info}") String atguiguInfo)
{
return "atguiguInfo: "+atguiguInfo+"\t"+"port: "+port;
}
4.2.3.3 测试服务:
通过修改application.yml里面的激活配置部分,进行内容的验证
测试地址:http://localhost:8001/pay/get/info
4.2.3.4 加上@RefreshScope // 动态刷新 :(当我们修改consul中的配置文件,后端也能得到对应的数据)
package com.atguigu.cloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import tk.mybatis.spring.annotation.MapperScan;
/**
* @auther zzyy
* @create 2023-11-03 17:54
*/
@SpringBootApplication
@MapperScan("com.atguigu.cloud.mapper") //import tk.mybatis.spring.annotation.MapperScan;
@EnableDiscoveryClient //服务注册和发现
@RefreshScope // 动态刷新
public class Main8001
{
public static void main(String[] args)
{
SpringApplication.run(Main8001.class,args);
}
}
5.总结
consul可以用来服务的注入管理,也可以用来统一入住进consul的服务的配置文件。(粗浅的总结和学习,还需更深层次的学习。)