SpringCloud
认识Springcloud
Springcloud
定义Spring Cloud是一系列框架的有序集合
它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署
Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包
核心组件(本文将要学习的)
- 服务注册与发现:AlibabaNacos
- 服务调用和负载均衡:OpenFeign
- 分布式事务:Alibaba Seata
- 服务熔断和降级:Alibaba Sentinel
- 服务网关:GateWay
- 分布式配置管理:AlibabaNacos
Spring Cloud Alibaba
定义 Spring Cloud Alibaba是阿里巴巴结合自身的微服务实践开源的微服务全家桶
它是对Spring Cloud组件的扩展和兼容,提供了更加丰富的微服务组件和功能
2018.10.31,Spring Cloud Alibaba 正式入驻了 Spring Cloud 官方孵化器,并在 Maven 中央库发布了第一个版本官网:https://spring.io/projects/spring-cloud-alibaba
Github:https://github.com/alibaba/spring-cloud-alibaba
中文参考文档:https://spring-cloud-alibaba-group.github.io/github-pages/2022/zh-cn/
英文参考文档:https://spring-cloud-alibaba-group.github.io/github-pages/2022/en-cn/
组件:
- Sentinel:把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性
- Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务平台
- seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式解决方案
快速开始
准备工作:步骤一:
创建一个初始java工程步骤二:
注解生效激活 setting-> Build.. -> Compiler -> Annotation Processors步骤三:
字符编码 setting-> Editor -> file Encoding -> 全部选择utf-8步骤四:
setting-> Build.. -> Compiler -> Java Compiler -> 选择Java17版本步骤五:
引入相关依赖
注意:
先把dependencyManagement注释掉,下载完成依赖后再写上去,不然会爆红
dependencyManagement在Maven项目中的作用主要是集中管理项目的依赖版本,确保项目中各个模块使用相同的依赖版本,减少潜在的冲突和兼容性问题,简化依赖声明,提高项目的可维护性。通过这个元素,所有依赖的版本可以由父模块统一管理,但是需要注意的是,>里只是声明依赖,并不自动实现引入(可能这就是直接下载依赖爆红的原因),需要子项目声明需要用的依赖,如果不在子项目中声明依赖,是不会从父项目中继承下来的。如果在子项目中写了该依赖项,但是没有指定具体版本,这时候才会从父项目中继承相应依赖以及相应的version和scope。
1 |
|
步骤六:
新建子工程mybatis_generator2024,并引入相关依赖
1 |
|
步骤八:
创建数据库,使用navicat,先创建一个名为db2024的数据库,运行以下sql文
1 | DROP TABLE IF EXISTS `t_pay`; |
步骤七:
在子工程mybatis_generator2024下的src\main\resource路径下新建以下包
此版本对应Mysql51
2
3
4
5
6
7#User表包名
package.name=com.atguigu.cloud
# mysql5.7
jdbc.driverClass = com.mysql.jdbc.Driver
jdbc.url= jdbc:mysql://localhost:3306/db2024?useUnicode=true&characterEncoding=UTF-8&useSSL=false
jdbc.user = root
jdbc.password =123456
此版本对应Mysql81
2
3
4
5
6
7
8#t_pay表包名
package.name=com.atguigu.cloud
# mysql8.0
jdbc.driverClass = com.mysql.cj.jdbc.Driver
jdbc.url= jdbc:mysql://localhost:3306/db2024?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
jdbc.user = root
jdbc.password =123456
1 |
|
步骤八:
打开侧栏maven界面,mybatis_generator2024 -> Plugins -> mybatis-generator -> 左键双击mybatis-generator:gererate一键生成entity+mapper接口+xml实现SQL步骤九:
新建子工程cloud-provider-payment8001,引入pom
1 |
|
步骤十:
新建子工程cloud-provider-payment8001的resource下新建application.xml
1 | server: |
步骤十一:
新建子工程cloud-provider-payment8001的主启动 Main 改为Main8001
1 | package com.atguigu.cloud; |
步骤十二:
将之前自动生成的实体类和mapper都迁移到cloud-provider-payment8001中,并删除原本的(个人觉得不如直接用MybatisX生成)
并且新建PayDTO,PayDTO就像Pay的儿子一样,他拥有Pay的部分可以直接暴露给前端的属性(比如密码,身份证号之类的隐私属性就不适合暴露给前端)
1 |
|
步骤十三:
完成增删改查相关代码
1 | public interface PayService |
1 |
|
1 |
|
注意
完成以上代码后运行Main8001,我遇到报错
java: JDK isn’t specified for module ‘cloud-provider-payment8001’
解决方案:File -> Modules -> Dependencies 这里面的Module SDK版本,不要使用Project SDK17(这是idea自带的),使用自己安装的那个jdk17
但是:这样修改的话这个模块就独立了jdk版本,这意味着一旦出问题,就要全部都检查一下,微服务后面模块会非常多的,子模块应该要被父模块pom统一管理版本
步骤十四:
使用Apifox/swagger进行相关测试
这里对swagger的使用再次进行讲解(还记得上次是在哪里讲的吗?)
Swagger
常用注解
最常用的是 @Tag @Schema @Operation
注解 | 标注位置 | 作用 |
@Tag | controller类 | 标识controller作用 |
@Schema | model层的JavaBean | 描述模型作用及每个属性 |
@Operation | 方法 | 描述方法作用 |
@Parameter | 参数 | 标识参数作用 |
@Parameters | 参数 | 参数多重说明 |
@ApiResponse | 方法 | 描述响应状态码等 |
1 |
|
1 |
|
1 |
|
"createTime": "2024-11-29T00:53:31.000+00:00"
"updateTime": "2024-11-29T00:53:31.000+00:00"
如果就这样传递给前端的话会挨揍的吧?
所以,遇事不决,注解解决
1 | /** |
“createTime”: “2024-11-29 08:53:31”,
“updateTime”: “2024-11-29 08:53:31”
具体实现步骤:
1. 新建枚举类ReturnCodeEnum,HTTP请求返回的状态码
新建枚举类 ReturnCodeEnum是为了减少因状态码拼写错误或值不匹配而导致的运行时错误,
他主要用于定义和管理一组与应用程序返回状态相关的常量,每个枚举实例代表了一个特定的状态码和对应的描述信息
这种设计在软件开发中非常常见,特别是在需要统一处理错误和响应状态的场景中
2. 新建统一定义返回对象ResultData
3. 修改PayController
1 |
|
1 |
|
1 |
|
具体实现步骤:
1. 新建全局异常类GlobalExceptionHandler
2. 修改Controller
1 |
|
1 |
|
HttpStatus通常指的是HTTP状态码,它们是由服务器在HTTP响应中返回的三位数字代码,用于指示客户端请求的结果。HTTP状态码分为五大类,每一类的第一个数字表示其类型:
- 1xx 信息响应:表示接收到请求,正在处理。
100 Continue:客户端应该继续发送请求的剩余部分,如果服务器已经收到并将会处理请求的话。
101 Switching Protocols:服务器已经理解了客户端的请求,并将通过Upgrade消息头把连接转换到另一个协议。 - 2xx 成功响应:表示请求已成功被服务器接收、理解、并接受。
200 OK:请求成功。
201 Created:请求已经被成功处理,服务器创建了新的资源。
202 Accepted:服务器已接受请求,但尚未处理。
203 Non-Authoritative Information:服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝。
204 No Content:服务器成功处理了请求,但不需要返回任何内容。
205 Reset Content:服务器成功处理了请求,且没有返回任何内容。但是与204响应不同,此响应要求请求者重置文档视图(例如,清除表单内容以输入新内容)。
206 Partial Content:服务器成功处理了部分GET请求。 - 3xx 重定向响应:表示需要客户端采取进一步的操作才能完成请求。
300 Multiple Choices:被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的视图。
301 Moved Permanently:请求的资源已永久移动到新的URL上。
302 Found:请求的资源现在临时从不同的URI响应请求。
303 See Other:对应当前请求的响应可以在另一个URI上被找到,而且客户端应当采用GET的方式访问那个资源。
304 Not Modified:如果客户端发送了一个带条件的GET请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变。
307 Temporary Redirect:请求的资源现在临时从不同的URI响应请求。 - 4xx 客户端错误响应:表示请求包含语法错误或无法完成请求。
400 Bad Request:服务器无法理解请求的格式,客户端不应当重复此请求。
401 Unauthorized:请求要求身份验证。
402 Payment Required:此响应码保留以便将来使用。初始目的是用于表示要求付款。
403 Forbidden:服务器理解请求但拒绝执行它。
404 Not Found:服务器无法根据客户端的请求找到资源(网页)。
405 Method Not Allowed:服务器禁用了请求中指定的方法。
406 Not Acceptable:根据客户端请求头中的Accept字段,服务器无法提供客户端想要的格式。
407 Proxy Authentication Required:要求客户端在代理服务器上进行身份验证。
408 Request Timeout:服务器等候请求时发生超时。
409 Conflict:请求因为与资源的当前状态相冲突而无法完成。
410 Gone:请求的资源已不在服务器上,且没有任何已知的转发地址。 - 5xx 服务器错误响应:表示服务器在处理请求的过程中发生了错误。
500 Internal Server Error:服务器内部错误,无法完成请求。
501 Not Implemented:服务器不支持请求的功能,无法完成请求。
502 Bad Gateway:作为网关或代理工作的服务器从上游服务器收到无效响应。
503 Service Unavailable:服务器目前无法使用(由于超载或停机维护)。
504 Gateway Timeout:服务器作为网关或代理,但是没有及时从上游服务器收到请求。
505 HTTP Version Not Supported:服务器不支持请求中所用的HTTP协议版本。
RuntimeException异常
是 Java 编程语言中的一种异常类型,它继承自Exception类,但是与Exception类中的其他异常不同,RuntimeException及其子类属于未检查异常(Unchecked Exception)的范畴
这意味着当 RuntimeException 或其子类异常发生时,Java 编译器不会强制要求开发者在编译时对其进行处理,需要在方法签名中使用 throws 关键字声明,或者在方法内部使用 try-catch 块捕获,这里我们当然推荐throws 关键字声明啦
它通常表示程序中的逻辑错误或资源访问问题,如空指针引用、数组越界、类型转换错误等
RuntimeException常见子类:
NullPointerException:
当应用程序试图在需要对象的地方使用null时抛出。例如,当调用null对象的实例方法或访问null对象的字段时,会抛出此异常。
ArrayIndexOutOfBoundsException:
当索引超出字符串或数组的有效索引范围时抛出。这通常发生在访问数组或字符串的非法索引时。
ArithmeticException:
当发生数学错误时抛出。例如,整数除零时会抛出此异常。
ClassCastException:
当试图将对象强制转换为不是实例的类时抛出。这通常发生在类型转换失败时。
NumberFormatException:
当应用程序试图将字符串转换为一种数值类型,但该字符串没有有效的转换格式时抛出。
下面我们对空指针异常进行测试:
1 | //指定该方法将处理所有的RuntimeException及其子类的异常 |
1 |
|
1 | { |
1 | <dependencies> |
1 | server: |
1 |
|
1 |
|
url
requestMap
ResponseBean.class
这三个参数分别代表
REST请求地址、请求参数、HTTP响应转换被转换成的对象类型
Modifier and Type | Method | Description |
<T>ResponseEntity<T> | getForEntity(String url, Class |
Retrieve an entity by doing a GET on the specified URL. 通过对指定的 URL 执行 GET 来检索表示形式 |
<T> T | getForObject(String url, Class |
Retrieve a representation by doing a GET on the specified URL. 通过对指定的 URL 执行 GET 来检索表示形式 |
代码示例:
返回对象为响应体中数据转化成的对象,基本上可以理解为Json1
2
3
4
5
public CommonResult<Payment> getPayment( Long id)
{
return restTemplate.getForObject(PAYMENT_SRV + "/payment/get/"+id, CommonResult.class);
}
返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等1
2
3
4
5
6
7
8
9
10
public CommonResult<Payment> getPayment2( Long id)
{
ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_SRV + "/payment/get/"+id, CommonResult.class);
if(entity.getStatusCode().is2xxSuccessful()){
return entity.getBody();
}else{
return new CommResult(444,"操作失败");
}
}
1 |
|
1 | <T> T getForObject(String url, Class<T> responseType, Object... uriVariables); |
1 | <T> T postForObject(String url, ; Object request, Class<T> responseType, Object... uriVariables) |
controller代码如下(可以正常运行但是感觉写的有的有点问题):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class OrderController {
public static final String PaymentSrv_URL = "http://localhost:8001";//先写死,硬编码
private RestTemplate restTemplate;
/**
* 创建订单
*/
public ResultData addOrder( PayDTO payDTO)
{
return restTemplate.postForObject(PaymentSrv_URL + "/pay/add", payDTO, ResultData.class);
}
/**
* 根据id查询订单
*/
public ResultData getPayInfo( Integer id)
{
return restTemplate.getForObject(PaymentSrv_URL + "/pay/get/" + id, ResultData.class,id);
}
/**
* 查询所有订单
*/
public ResultData getAllPayInfo()
{
return restTemplate.getForObject(PaymentSrv_URL + "/pay/getAll", ResultData.class);
}
/**
* 删除订单
*/
public ResultData deletePayInfo( { Integer id)
// 使用 delete 方法而不是 getForObject
ResponseEntity<ResultData> response = restTemplate.exchange(
PaymentSrv_URL + "/pay/del/" + id,
HttpMethod.DELETE,
null, // 因为是 DELETE 请求,通常不需要请求体
ResultData.class,
id // 这里的 id 作为 URI 变量传递,但在 DELETE 请求中通常不需要作为参数传递
);
return response.getBody();
}
/**
* 修改订单
*/
public ResultData updatePayInfo( { PayDTO payDTO)
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<PayDTO> request = new HttpEntity<>(payDTO, headers); // 假设您已经设置了适当的headers
ResponseEntity<ResultData> response = restTemplate.exchange(
PaymentSrv_URL + "/pay/update",
HttpMethod.PUT,
request,
ResultData.class
);
return response.getBody();
}
}
工程重构
项目进行到这里,大家应该会发现,我们的项目中有一些重复的类,例如:PayDTO、ResultData等等
如果每次都要这样写的话,那不是很麻烦吗?
所以我们要新建一个module:cloud-api-commons,封装对外暴露通用的组件/api/接口/工具类等
①改pom文件
②将entity下的PayDTO、统一返回(resp)粘贴到当前目录下,全局异常类可加可不加,酌情考虑
③maven命令 clean
install
④删除订单80和支付8001中,刚才粘贴走的内容
⑤各自粘贴pom内容
1 | <dependencies> |
1 | <!-- 引入自己定义的api通用包 --> |
Openfeign服务接口调用
基本介绍
Feign是一个声明性web服务客户端,它使编写web服务客户端变得更容易
使用Feign创建一个接口并对其进行注释,它具有可插入的注释支持,包括Feign注释和JAX-RS注释
Feign还支持可插拔编码器和解码器
Spring Cloud添加了对Spring MVC注释的支持,以及对使用Spring Web中默认使用的HttpMessageConverter的支持
Spring Cloud集成了Eureka、Spring Cloud CircuitBreaker以及Spring Cloud LoadBalancer,以便在使用Feign时提供负载平衡的http客户端
官方网站:https://springdoc.cn/spring-cloud-openfeign/
GitHub:https://github.com/OpenFeign
总结:openfeign是一个声明式的web服务客户端,只需要创建一个Rest接口并在该接口上添加注解<code>@FeignClient</code>即可
OpenFeign基本上就是当前微服务之间调用的事实标准
OpenFeign能干什么
前面在使用SpringCloud LoadBalancer+RestTemplate时,利用RestTemplate对http请求的封装处理形成了一套模版化的调用方法
但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用
所以,OpenFeign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义
在OpenFeign的实现下,我们只需创建一个接口并使用注解的方式来配置它(在一个微服务接口上面标注一个@FeignClient注解即可),即可完成对服务提供方的接口绑定,统一对外暴露可以被调用的接口方法,大大简化和降低了调用客户端的开发量,也即由服务提供者给出调用接口清单,消费者直接通过OpenFeign调用即可,O(∩_∩)O
OpenFeign同时还集成SpringCloud LoadBalancer
可以在使用OpenFeign时提供Http客户端的负载均衡,也可以集成阿里巴巴Sentinel来提供熔断、降级等功能
而与SpringCloud LoadBalancer不同的是,通过OpenFeign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用
总结:
- 可插拔的注解支持,包括Feign注解和JAX-RS注解
- 支持可插拔的HTTP编码器和解码器
- 支持Sentinel和他的Fallback
- 支持SpringCloudLoadBanancer的负载均衡
- 支持HTTP请求和响应的压缩
使用流程
- 新建Module:cloud-consumer-feign-order80
- 改pom
- 写yml
- 主启动,修改类名为MainOpenFeign0
- 业务类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57<dependencies>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--SpringCloud consul discovery-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 引入自己定义的api通用包 -->
<dependency>
<groupId>com.atguigu.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--web + actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--hutool-all-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!--fastjson2-->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
</dependency>
<!-- swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>1
2
3
4
5
6
7
8
9
10
11
12
13
14server:
port: 80
spring:
application:
name: cloud-consumer-openfeign-order
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
prefer-ip-address: true #优先使用服务ip进行注册
service-name: ${spring.application.name}1
2
3
4
5
6
7
8
//该注解用于向使用consul为注册中心时注册服务
//启用feign客户端,定义服务+绑定接口,以声明式的方法优雅而简单的实现服务调用
public class MainOpenFeign80 {
public static void main(String[] args) {
SpringApplication.run(MainOpenFeign80.class,args);
}
}
- 前置准备
订单模块要去调用支付模块,订单和支付两个微服务,需要通过Api接口解耦,一般不要在订单模块写非订单相关的业务,自己的业务自己做+其它模块走FeignApi接口调用
服务消费者 -> 公共API模块(服务接口,与服务提供者对应)-> 服务提供者方法签名,注解路径要和服务提供者一致
1
2
3
4
5
public interface xxxService{
boolean getxxx(...);
}1
2
3
4
5
public class xxxController{
boolean getxxx(...);
} - 修改cloud-api-commons通用模块
- 引入openfeign依赖
- 新建服务接口PayFeignApi,配置@FeignClient注解
- 参考微服务8001的Controller层,新建PayFeignApi接口
1 | <dependency> |
1 |
|
- 拷贝之前80工程的controller到本模块
- 修改controller层
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class OrderController {
private PayFeignApi payFeignClient;
public ResultData addOrder( { PayDTO payDTO)
System.out.println("第一步:模拟本地addOrder新增订单成功 第二步:在开启addPay支付微服务远程调用");
ResultData resultData = payFeignClient.addPayInfo(payDTO);
return resultData;
}
public ResultData getPayInfo( { Integer id)
System.out.println("支付微服务远程调用,按照id查询订单支付流水");
ResultData resultData = payFeignClient.getPayInfo(id);
return resultData;
}
/**
* 测试负载均衡
* @return
*/
public String mylb() {
return payFeignClient.mylb();
}
}
测试
- 先启动Consul服务器
- 启动微服务8001、8002、cloud-consumer-feign-order80
- Apifox测试
- 启动8002,测试负载均衡:http://localhost/feign/pay/mylb
高级特性
OpenFeign超时控制
在Spring Cloud微服务架构中,大部分公司都是利用OpenFeign进行服务间的调用,而比较简单的业务使用默认配置是不会有多大问题的,但是如果是业务比较复杂,服务要进行比较繁杂的业务计算,那后台很有可能会出现Read Timeout这个异常,因此定制化配置超时时间就有必要了
此处我们故意设置超时,演示出错情况:
- 服务提供方cloud-provider-payment8001故意写暂停62秒钟程序
- 服务调用方cloud-consumer-feign-order80写好捕捉超时异常
- 测试:http://localhost/feign/pay/get/1
结论:OpenFeign静默等待60秒钟,超时后报错
1 |
|
1 |
|
默认OpenFeign客户端等待60秒钟,但是服务端处理超过规定时间会导致Feign客户端返回报错
为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制,默认60秒太长或者业务时间太短都不好
yml文件中开启配置:connectTimeout
连接超时时间readTimeout
请求处理超时时间
修改cloud-consumer-feign-order80,yml文件里需要开启OpenFeign客户端超时控制1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25server:
port: 80
spring:
application:
name: cloud-consumer-openfeign-order
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
prefer-ip-address: true #优先使用服务ip进行注册
service-name: ${spring.application.name}
openfeign:
client:
config:
#全局
#default:
#connectTimeout: 4000 #连接超时时间
#readTimeout: 4000 #读取超时时间
#自定义(优先级更高)
cloud-payment-service:
connectTimeout: 8000 #连接超时时间
readTimeout: 8000 #读取超时时间
OpenFeign重试机制
在微服务架构中,服务之间的调用是不可避免的,但是由于网络延迟、服务故障或负载过高等原因,服务调用可能会失败
开启重试机制可以在初次调用失败后,自动进行若干次重试,从而提高服务的可用性,确保请求能够成功到达目标服务1
2
3
4
5
6
7
8
9
10
public class FeignConfig {
public Retryer myRetryer(){
//return Retryer.NEVER_RETRY; //Feign默认配置是不走重试策略的
//最大请求次数为3(1+2)次,每次请求间隔时间为100毫秒,重试最大间隔时间为1s
return new Retryer.Default(100, 1000, 3);
}
}
OpenFeign默认HttpClient修改
如果不做特殊配置,OpenFeign默认使用JDK自带的HttpURLConnection发送HTTP请求
由于默认HttpURLConnection没有连接池、性能和效率比较低,如果采用默认,性能上不能达到最佳,所以加到最大
按照官网建议,我们将采用使用Apache HttpClient5 替换OpenFeign默认的HttpURLConnection
在微服务cloud-consumer-openfeign-order80下进行修改:
- 在FeignConfig类里面将Retryer属性修改为默认
- 修改pom
- Apache HttpClient5配置开启
- yml修改
1
2
3
4
5
6
7
8
9
public class FeignConfig
{
public Retryer myRetryer()
{
return Retryer.NEVER_RETRY; //Feign默认配置是不走重试策略的
}
}1
2
3
4
5
6
7
8
9
10
11
12<!-- httpclient5-->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.3</version>
</dependency>
<!-- feign-hc5-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hc5</artifactId>
<version>13.1</version>
</dependency>1
2
3
4
5
6
7# Apache HttpClient5 配置开启
spring:
cloud:
openfeign:
httpclient:
hc5:
enabled: true1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110server:
port: 80
spring:
application:
name: cloud-consumer-openfeign-order
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
prefer-ip-address: true #优先使用服务ip进行注册
service-name: ${spring.application.name}
openfeign:
client:
config:
default:
connectTimeout: 4000 #连接超时时间
readTimeout: 4000 #读取超时时间
httpclient:
hc5:
enabled: true
#cloud-payment-service:
#connectTimeout: 4000 #连接超时时间
#readTimeout: 4000 #读取超时时间
```
<!-- endtab -->
{% endtabs %}
### OpenFeign请求/响应压缩
对请求和响应进行GZIP压缩
Spring Cloud OpenFeign支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗
通过下面的两个参数设置,就能开启请求与相应的压缩功能:
<code>spring.cloud.openfeign.compression.request.enabled=true</code>
<code>spring.cloud.openfeign.compression.response.enabled=true</code>
细粒度化设置
对请求压缩做一些更细致的设置,比如下面的配置内容指定压缩的请求数据类型并设置了请求压缩的大小下限:
只有超过这个大小的请求才会进行压缩:
spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.request.mime-types=text/xml,application/xml,application/json #触发压缩数据类型
spring.cloud.openfeign.compression.request.min-request-size=2048 #最小触发压缩的大小
对application.yml进行配置:
```yml
server:
port: 80
spring:
application:
name: cloud-consumer-openfeign-order
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
prefer-ip-address: true #优先使用服务ip进行注册
service-name: ${spring.application.name}
openfeign:
client:
config:
#全局
default:
connectTimeout: 4000 #连接超时时间
readTimeout: 4000 #读取超时时间
#自定义(优先级更高)
#cloud-payment-service:
#connectTimeout: 8000 #连接超时时间
#readTimeout: 8000 #读取超时时间
httpclient:
hc5:
enabled: true
compression:
request:
enabled: true
min-request-size: 2048 #最小触发压缩的大小
mime-types: text/xml,application/xml,application/json #触发压缩数据类型
response:
enabled: true
```
### OpenFeign日志打印功能
Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节
说白了就是对Feign接口的调用情况进行监控和输出
>日志级别
NONE:默认的,不显示任何日志;
BASIC:仅记录请求方法、URL、响应状态码及执行时间;
HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;
FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。
配置日志Bean以及application.yml
{% tabs %}
<!-- tab FeignConfig-->
```java
public class FeignConfig
{
public Retryer myRetryer()
{
return Retryer.NEVER_RETRY; //默认
}
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}公式(三段):logging.level + 含有@FeignClient注解的完整带包名的接口名+debug
1
2
3
4
5
6
7
8# feign日志以什么级别监控哪个接口
logging:
level:
com:
atguigu:
cloud:
apis:
PayFeignApi: debug
Nacos服务注册与发现
Nacos简介
Nacos是什么Nacos:Dynamic Naming and Configuration Service
前四个字母分别为Naming和Configuration的前两个字母,最后的s为Service
官网:https://nacos.io/
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台
Nacos就是 注册中心 + 配置中心的组合
Nacos = Eureka + Config + Bus
Nacos = Spring Cloud Consul
Nacos能干嘛
①代替Eureka/Consul做服务注册中心
②代替(Config + Bus)/Consul做服务配置中心和满足动态刷新广播通知
各种注册中心的比较:
服务注册与发现框架 | CAP模型 | 控制台管理 | 社区活跃度 |
Eureka | AP | 支持 | 低(2.x版本闭源) |
Zookeeper | CP | 不支持 | 中 |
Consul | CP | 支持 | 高 |
Nacos | AP | 支持 | 高 |
据说 Nacos 在阿里巴巴内部有超过 10 万的实例运行,已经过了类似双十一等各种大型流量的考验
Nacos默认是AP模式,但也可以调整切换为CP,我们一般用默认AP即可
小Tip:CAP模型CAP模型,又称为CAP理论或Brewer’s理论,由Eric Brewer教授在2000年提出
它描述了在分布式系统中三个重要属性之间的取舍关系,这三个属性分别是一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)
CAP理论指出,在一个分布式系统中,不可能同时满足这三个属性的强一致性。因此,系统设计者需要在它们之间进行权衡和取舍。具体来说,有以下三种组合方式:
- CA模型:
保证系统的一致性和可用性,但损失了分区容错性。
这种模型通常适用于单机系统或小规模分布式系统,但在大型分布式系统中,由于网络分区和节点故障的可能性增加,因此很难同时满足一致性和可用性。- CP模型:
保证了分布式架构中数据的一致性,但牺牲了可用性。
这种模型适用于对一致性要求苛刻的业务场景,如金融、医疗等领域。在这些场景中,数据的一致性至关重要,即使牺牲一些可用性也是值得的。- AP模型:
保证了服务在分布式架构中的可用性,但牺牲了一致性。
这种模型适用于对可用性要求较高的业务场景,如互联网服务、社交媒体等领域。在这些场景中,系统的可用性至关重要,因为用户希望随时能够访问和使用服务。同时,由于数据量巨大且变化频繁,因此很难保证数据的一致性。不过,可以通过最终一致性(Eventual Consistency)等机制来尽量保证数据的正确性。
快速开始
步骤一:
为了和前面的版本相匹配,此处的Nacos务必下载2.2.3版本步骤二:
下载完成后解压压缩包,直接运行bin目录下的startup.cmd(或者在bin目录下运行cmd,运行命令startup.cmd -m standalone
)步骤三:
命令运行成功后直接访问:http://192.168.x.x:8848/nacos/index.html 根据自己命令行输出结果访问
默认账号密码都是nacos步骤四:
关闭服务器,有两种方法,可以直接运行shutdown.cmd
也可以 Ctrl+C
Nacos Discovery服务注册中心
通过 Nacos Server 和 spring-cloud-starter-alibaba-nacos-discovery 实现服务的注册与发现
建议和官方文档搭配食用:https://spring-cloud-alibaba-group.github.io/github-pages/2022/zh-cn/
基于Nacos的服务提供者
- 新建Module:cloudalibaba-provider-payment9001
- pom
- yml
- 主启动
- 业务类
- 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49<dependencies>
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 引入自己定义的api通用包 -->
<dependency>
<groupId>com.atguigu.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--SpringBoot通用依赖模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>1
2
3
4
5
6
7
8
9
10server:
port: 9001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址 根据上一步的地址写1
2
3
4
5
6
7
8
9
public class Main9001
{
public static void main(String[] args)
{
SpringApplication.run(Main9001.class,args);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package com.atguigu.cloud.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
public class PayAlibabaController
{
private String serverPort;
public String getPayInfo( Integer id)
{
return "nacos registry, serverPort: "+ serverPort+"\t id"+id;
}
}
测试:访问http://localhost:9001/pay/nacos/11
显示:nacos registry, serverPort: 9001 id11
访问控制台,查看服务列表,nacos-payment-provider已存在
基于Nacos的服务消费者
- 新建Module:cloudalibaba-consumer-nacos-order83
- pom
- yml
- 主启动
- 业务类
- 配置config
- OrderNacosController
- 测试测试:访问http://localhost:83/consumer/pay/nacos/15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49<dependencies>
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 引入自己定义的api通用包 -->
<dependency>
<groupId>com.atguigu.cloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--SpringBoot通用依赖模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>1
2
3
4
5
6
7
8
9
10
11
12
13server:
port: 83
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
#消费者将要去访问的微服务名称(nacos微服务提供者叫什么你写什么)
service-url:
nacos-user-service: http://nacos-payment-provider1
2
3
4
5
6
7
8
9
public class Main83
{
public static void main(String[] args)
{
SpringApplication.run(Main83.class,args);
}
}1
2
3
4
5
6
7
8
9
10
public class RestTemplateConfig
{
//赋予RestTemplate负载均衡的能力
public RestTemplate restTemplate()
{
return new RestTemplate();
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class OrderNacosController
{
private RestTemplate restTemplate;
private String serverURL;
public String paymentInfo( Integer id)
{
String result = restTemplate.getForObject(serverURL + "/pay/nacos/" + id, String.class);
return result+"\t"+" 我是OrderNacosController83调用者。。。。。。";
}
}
显示:nacos registry, serverPort: 9001 id15 我是OrderNacosController83调用者。。。。。。
访问控制台,查看服务列表,nacos-order-consumer已存在
为了方便,我们这里采用直接拷贝虚拟端口映射的方式来新建9002端口:
①在IDEA的service中,右键Main9001,点击copy Configuration..选项
②name命名为Main9002(copy from 9001)
③点击Modify options,点击 Add VM options,填写-DServer.port=9002
④运行9001和9002.打开Nacos控制台,发现当前nacos-payment-provider的实例个数变为2
测试:访问http://localhost:83/consumer/pay/nacos/15,不断点击刷新
现象:端口号9001和9002交替出现,说明负载均衡达到
Nacos Config服务配置中心
通过Nacos Server和spring-cloud-starter-alibaba-nacos-config实现配置的动态变更
- 新建Module:cloudalibaba-config-nacos-client3377
- pom
- application.yml 和 bootstrap.yml
为什么配置两个?
Nacos同Consul一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动
springboot中配置文件的加载是存在优先级顺序的,bootstrap优先级高于application
这样配置不仅遵循了Spring Boot配置文件加载的优先级规则,还确保了项目在初始化阶段能够正确地从配置中心拉取配置信息,并在运行时根据动态刷新的配置进行灵活调整 - 主启动
- 业务类
- 在Nacos中添加配置信息(重点)
- 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41<dependencies>
<!--bootstrap-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--web + actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15# nacos配置
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml #指定yaml格式的配置
# nacos端配置文件DataId的命名规则是:
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
# 本案例的DataID是:nacos-config-client-dev.yaml1
2
3
4
5
6
7
8server:
port: 3377
spring:
profiles:
active: dev # 表示开发环境
#active: prod # 表示生产环境
#active: test # 表示测试环境1
2
3
4
5
6
7
8
9
public class NacosConfigClient3377
{
public static void main(String[] args)
{
SpringApplication.run(NacosConfigClient3377.class,args);
}
}1
2
3
4
5
6
7
8
9
10
11
12
//在控制器类加入@RefreshScope注解使当前类下的配置支持Nacos的动态刷新功能。
public class NacosConfigClientController
{
private String configInfo;
public String getConfigInfo() {
return configInfo;
}
}
小Tip:Nacos命名规则
在Nacos Spring Cloud中,数据集(Data Id)的配置完整格式如下:${prefix}-${spring.profile.active}.${file-extension}prefix:
表示配置的服务名,通常是在服务注册时注册到服务中心的服务名spring.profile.active:
表示配置的开发环境,如开发环境(dev)、测试环境(test)、生产环境(prod)等。通过配置不同的环境标识,可以方便地切换不同环境的配置文件file-extension:
表示配置文件的类型,如properties、yaml等
例如,对于一个名为nacos-config-client的服务,在开发环境(dev)下的配置文件命名为nacos-config-client-dev.yaml
进入Nacos控制台 -> 配置列表 -> 创建配置 -> Data ID:nacos-config-client-dev.yaml -> 配置格式为yaml1
2config:
info: nacos-config-client-dev.yaml, come from nacosconfig, version=1
Nacos数据模型之Namespace-Group-DataId
问题1:实际开发中,通常一个系统会准备
dev开发环境
test测试环境
prod生产环境
如何保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件呢?
一个大型分布式微服务系统会有很多微服务子项目,
每个微服务项目又都会有相应的开发环境、测试环境、预发环境、正式环境……
那怎么对这些微服务配置进行分组和命名空间管理呢?
Namespace+Group+dataId 三者的关系是什么?为什么这么设计?
Nacos数据模型Key由三元组唯一确定,Namespace默认是空串,公共命名空间(public),分组默认是DEFAULT_GROUP
- Namespace(命名空间):
作用:用于进行粗粒度的配置隔离。不同的命名空间下,可以存在相同的Group或Data ID的配置
场景:常用于区分不同的开发或部署环境,如开发测试环境和生产环境的资源(如配置、服务)隔离等- Group(分组):
作用:位于Namespace之下,用于区分不同的微服务或应用组件。当不同的应用或组件使用了相同的配置类型时,可以利用Group来区分
场景:例如,一个应用可能使用了database_url配置和MQ_topic配置,可以将这些配置分别划分到不同的Group中,以便更好地管理和维护- DataID(数据标识符):
作用:位于最内层,用于唯一标识具体的配置信息。每个DataID对应一个具体的配置信息,例如一个数据库连接信息或消息队列的配置
场景:通过DataID,可以轻松地查找、获取和更新配置信息
GateWay新一代网关
Gateway是在Spring生态系统之上构建的API网关服务,基于Spring6,Spring Boot 3和Project Reactor等技术
它旨在为微服务架构提供一种简单有效的统一 API 路由管理方式,并为它们提供跨领域的关注点,例如:安全性、监控/度量和恢复能力
Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关
但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关SpringCloud Gateway替代Zuul
那就是SpringCloud Gateway一句话:gateway是原zuul1.x版的替代
官网地址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
中文文档(看起来版本很旧):https://springdoc.cn/spring-cloud-gateway/
Gateway的作用:
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
总结
Spring Cloud Gateway组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对应的微服务
Spring Cloud Gateway是加在整个微服务最前沿的防火墙和代理器,隐藏微服务结点IP端口信息,从而加强安全保护
Spring Cloud Gateway本身也是一个微服务,需要注册进服务注册中心
小Tip:GateWay 和 Nginx
- Nginx
类型:高性能Web服务器和反向代理服务器
特点:基于C语言开发,具有低内存使用率和高并发性
架构:独立的Web服务器,可以与各种Web框架集成
配置:使用Nginx.conf配置文件来配置反向代理、负载均衡、缓存等功能
性能:由于Nginx是基于C语言开发的,并且经过了大量的优化,因此其性能非常出色,特别是在静态内容和/或高并发请求的情况下
扩展性:Nginx可以通过编写Nginx模块来扩展其功能,但需要一定的C语言编程能力
应用场景:适用于需要高性能、高并发性的Web服务器场景,可以作为第一层网关,抵御第一波的并发流量。- GateWay
类型:Spring Cloud生态中的网关组件
特点:基于Java开发,旨在为微服务架构提供一种简单而有效的统一的API路由管理方式
架构:基于Spring Cloud和Spring Boot构建的微服务网关
配置:通过Java代码或YAML配置文件来定义路由规则和过滤器链
性能:相对于Nginx来说,Gateway的性能可能会有一些损失,因为它是基于Java和Spring Boot构建的
扩展性:Gateway可以通过Spring Cloud的插件机制来扩展其功能,这对于Java开发者来说更加友好
应用场景:适用于微服务架构中的API路由管理场景,可以作为第二层网关,根据不同的微服务来整合不同的网关系统(如移动端网关、自媒体端网关、管理员端网关等)。
Gateway的三大核心:
- Route(路由): 构建网关的基本模块,它由ID、目标URI、一系列断言(Predicate)和过滤器(Filter)组成。如果断言为true,则路由被匹配
- Predicate(断言): 参考的是 Java 8的java.util.function.Predicate。开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
- Filter(过滤器): 指的是Spring框架中的GatewayFilter 的实例,使用过滤器,可以在请求被路由前或之后修改请求和响应
总结web前端请求,通过一些匹配条件,定位到真正的服务节点,并在这个转发过程的前后,进行一些精细化控制
predicate就是我们的匹配条件
filter,就可以理解为一个无所不能的拦截器
有了这两个元素,再加上目标uri,就可以实现一个具体的路由了
GateWay工作流程:
客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(Pre)或之后(Post)执行业务逻辑
在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等;
在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。
核心逻辑:路由转发 + 断言判断 + 执行过滤器链
入门配置
- 新建Module:cloud-gateway9527
- pom
- yml
- 主启动
- 业务类 不用写!网关和业务无关
- 测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--服务注册发现consul discovery,网关也要注册进服务注册中心统一管控-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>1
2
3
4
5
6
7
8
9
10
11
12
13server:
port: 9527
spring:
application:
name: cloud-gateway #以微服务注册进consul或nacos服务列表内
cloud:
consul: #配置consul地址
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}1
2
3
4
5
6
7
8
public class Main9527 {
public static void main(String[] args) {
SpringApplication.run(Main9527.class,args);
}
}