# spring-cloud-alibaba-notes
**Repository Path**: baoRoot/spring-cloud-alibaba-notes
## Basic Information
- **Project Name**: spring-cloud-alibaba-notes
- **Description**: 学习spring cloud alibaba微服务笔记
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-06-09
- **Last Updated**: 2023-06-15
## Categories & Tags
**Categories**: Uncategorized
**Tags**: spring-cloud-alibab, Java
## README
# spring-cloud-alibaba-notes
学习spring-cloud-alibaba笔记
## 一、版本
Spring-cloud-Alibaba: 2.2.9.RELEASE
Spring-boot: 2.3.12.RELEASE
spring-cloud: Hoxton.SR12
Nacos: 2.1.0
Gateway: 2.2.1.RELEASE
Sentinel: 1.8.5
RocketMQ: 4.9.4
Seata: 1.5.2
Dubbo: 2.7.13
## 二、提交笔记
### 1、开始
创建两个模块(spring-cloud-order/spring-cloud-stock),两个模块之间进行调用,模拟了微服务模块之间的互相调用,但是
发现,我们在A服务中调用B服务时,在A服务中需要写B模块的接口地址,如果有很多个微服务要调用,接口地址就要写很多,并且不知道
端口是多少,万一另外一个模块的接口地址更换,则需要慢慢的去更改接口地址很麻烦,所以我们的解决方案就是使用注册中心Nacos进行管理;
### 2、Nacos注册中心
主要将每一个微服务进行一个注册,在配置类中添加上负载均衡注解
配置类
```java
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced // 负载均衡 Ribbon
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
return restTemplateBuilder.build();
}
}
```
```yml
spring:
application:
name: spring-cloud-stock-nacos
cloud:
nacos:
server-addr: 127.0.0.1:8848 # 配置nacos地址,默认会将上面的名称作为服务名进行注册
```
1、单机压测
======= 10线程30次循环两个模块 =======
=== 总耗时 === 平均接口耗时 ===
=== 105.192 === 42 ===
======= 10线程30次循环一个模块 =======
=== 总耗时 === 平均接口耗时 ===
=== 114.202 === 60 ===
### 3、OpenFeign使用
Feign是一个Netflix开发的声明式、模块化的HTTP客户端,他可以帮助我们更加快捷、优雅的调用HTTP API,Feign
支持多种注解列如Feign自带的注解或者JAX-ES注解等,而OpenFeign是对Feign进行增强,支持Spring MVC,另外还整
合了Ribbon和Nacos,从而使得OpenFeign更加方便,OpenFeign是spring cloud中的,所以需要引入spring cloud
1、在调用的模块中已service的形式将feign进行一个封装,已service的形式注册controller层进行使用,此时重点就是feign中
如何设置
```java
@FeignClient(
name = "spring-cloud-stock-nacos", // 这里的名称只的是调用方法处理的实例模块名称,也就是在nacos注册中心注册的微服务名称
path = "/stock") // 在目标模块中的请求 根地址 也就是相当于@RequestMapping("/stock")注解,
public interface StockFeignService {
// 这里的请求方式,请求参数,请求方法名称、返回值类型和目标模块中需要调用的controller一致
@GetMapping("/redact")
String redact();
/**
例如:我的目标模块中的controller是以下代码,然后以上代码是openFeign的书写方式
@RestController
@RequestMapping("/stock")
public class StockController {
@Value("${server.port}")
private String port;
@GetMapping("/redact")
public String redact(){
return "扣减 OpenFeign" + port;
}
}
*/
}
```
注意:
在A模块中的controller注入StockFeignService报错,需要在启动类上加上@EnableFeignClients注解;
### 4、OpenFeign配置日志
1、定义一个全局配置类,指定日志级别
```java
@Configuration
public class OpenFeignConfig{
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
```
日志等级分为四种:
+ NONE【性能最佳,适用于生产】:不记录任何日志(默认值)
+ BASIC【适用于生产环境追踪问题】:仅记录请求方法、URL、响应状态码以及执行时间
+ HEADERS:记录BASIC级别的基础上,记录请求和响应的header
+ FULL【适用于开发环境】:记录请求和响应的header、body和元数据
2、局部配置,让调用的微服务生效,在@FeignClient注解中指定使用的配置类,注意在日志类中不能添加@Configuration注解,因为是局部
```java
@FeignClient(
name = "spring-cloud-stock-nacos",
path = "/stock",
configuration = OpenFeignConfig.class // 指定日志类,注意在OpenFeignConfig类上不能添加@Configuration注解
)
public interface StockFeignService {
@GetMapping("/redact")
String redact();
}
```
注意:
```yml
logging:
level:
org.xczy.order.feign: debug
```
### 5、OpenFeign配置超时时间
1、通过Options配置,Options第一个参数是连接的超时时间(ms),默认2s,第二个参数是请求处理的超时时间(ms),默认5s
全局配置
```java
@Configuration
public class OpenFeignConfig {
@Bean
public Request.Options options() {
return new Request.Options(5000, 10000);
}
}
```
yml中配置
```yml
feign:
client:
config:
mall-order: # 对应微服务名称
connectTimeout: 5000 # 连接的超时时间
readTimeout: 10000 # 请求处理的超时时间
```
### 6、自定义拦截器(服务调服务的拦截器不是MVC的拦截器)
```java
// 拦截器类
public class FeignAuthQuestInterceptor implements QuesrtInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
// 业务逻辑 例如是否能访问该服务,校验数据信息等
String token = UUID.randomUUID().toString();
template.header("token", token);
}
}
// OpenFeign配置类
@Configuration
public class OpenFeignConfig {
@Bean // 将拦截器配置在这里
public FeignAuthQuestInterceptor feignAuthQuestInterceptor(){
return new FeignAuthQuestInterceptor();
}
}
```
### 7、Nacos配置中心
作用:维护性、时效性、安全性
优点:可以动态感知nacos配置中心数据发生变化
1、启动nacos服务,在浏览器中登录nacos,然后在配置管理的配置列表中添加一个配置列表
```properties
user.name=yx
user.age=18
```
2、然后在项目中配置一个spring boot启动类并引入依赖和nacos配置依赖
```xml
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
org.springframework.boot
spring-boot-starter-web
2.3.12.RELEASE
```
3、在resources下新建一个bootstrap.yml文件
```yml
spring:
application:
name: spring-cloud-order # 去注册中心 读取的注册文件的dataId(就是需要读取哪个配置文件),一般名称和模块名一致
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848 # nacos地址
username: nacos
password: nacos
```
3、在启动项中获取nacos中得到配置信心
```java
@SpringBootApplication
public class NacosConfigApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(NacosConfigApplication.class, args);
String name = run.getEnvironment().getProperty("user.name");
String age = run.getEnvironment().getProperty("user.age");
System.out.println("名字为:" + name);
System.out.println("年龄为:" + age);
}
}
```
4、注意:
a.在nacos注册中心可以配置六种类型的文件(test/json/xml/yaml/html/properties),默认获取的是properties类型的
文件,如果在nacos配置中心是使用yaml配置的数据,则需要在bootstrap.yml中配置以下信息:
```yml
spring:
cloud:
nacos:
config:
file-extension: yml # 配置该服务从nacos配置中心获取的文件类型,默认是properties文件类型
```
这样就可以从yml文件类型的文件中获取数据信心
b.使用@Value注解无法动态的获取到配置中心的值,可以使用@RefreshScope注解
```java
@RestController
@RequestMapping("/stock")
@RefreshScope
public class StockController {
@Value("${user.name}")
private String port; // nacos配置中心的数据发生变化时@Value注解无法动态的感知数据变化,配合@RefreshScope即可
}
```
### 8、Gateway
网关作为流量入口,常用的功能包括路由转发、权限校验、限流等,核心概念:路由、断言、过滤器
1、引入依赖,注意:会和spring-webmvc的依赖冲突,需要排除spring-webmvc
```xml
org.springframework.cloud
spring-cloud-starter-gateway
```
2、编写yml文件
```yml
server:
port: 8080
spring:
application:
name: spring-cloud-gateway
cloud:
# gateway的配置
gateway:
# 路由规则
routes:
- id: order_route # 路由的唯一标识
uri: http://localhost:7002 # 需要转发的地址,注意是 i 不是 l
predicates: # 断言规则 用于路由规则的匹配
- Path=/order/**
# http://localhost:8080/order/stock/redact 转发到下面的路由
# http://localhost:7001/order/stock/redact
filters:
- StripPrefix=1 # 过滤器 转发之前去掉第一层路径,得到下面路径
# http://localhost:7001/stock/redact
```
### 9、gateway整合Nacos
1、引入依赖
```xml
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
```
2、编写yml配置文件
```yml
spring:
application:
name: spring-cloud-gateway-nacos
cloud:
# nacos 配置
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
# gateway的配置
gateway:
# 路由规则
routes:
- id: product-route
uri: lb://service-product # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/order/** # 注意Path首字母大写
filters:
- StripPrefix=1
```
3、简写:去掉关于路由的配置,自动寻找服务
```yml
server:
port: 8081
spring:
application:
name: spring-cloud-gateway-nacos
cloud:
# nacos 配置
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
# gateway的配置
gateway:
discovery:
locator:
enabled: true # 是否启动自动识别nacos服务
```
### 10、过滤器工厂GatewayFilter(局部过滤器)
gateway内置了很多过滤器工厂,例如剔除响应头、添加去除参数等
| 过滤器名称 | 作用 | 参数 |
| :---- | :---- | :---- |
| AddRequestHeader | 为原始请求添加Header | Header的名称及值 |
| AddRequestParameter | 为原始请求添加请求参数 | 参数名称及值 |
| AddResponseHeader | 为原始响应添加Header | Header的名称及值 |
| DedupeResponseHeader | 剔除响应头中重复的值 | 需要去重的Header名称及去重策略 |
| Hystrix | 为路由引入Hystrix的断路器保护 | HystrixCommand的名称 |
| FallbackHeaders | 为fallbackUri的请求头中添加具体的异常信息 | Header的名称 |
| PrefixPath | 为原始请求路径添加前缀 | 前缀路径 |
| PreserveHostHeader | 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host | 无 |
| RequestRateLimiter | 用于对请求限流,限流算法为令牌桶 | keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus |
| RedirectTo | 将原始请求重定向到指定的URL | http状态码及重定向的url |
| RemoveHopByHopHeadersFilter| 为原始请求删除IETF组织规定的一系列Header | 默认就会启用,可以通过配置指定仅删除哪些Header |
| RemoveRequestHeader | 为原始请求删除某个Header | Header名称 |
| RemoveResponseHeader | 为原始响应删除某个Header | Header名称 |
| RewritePath | 重写原始的请求路径 | 原始路径正则表达式以及重写后路径的正则表达式 |
| RewriteResponseHeader | 重写原始响应中的某个Header | Header名称,值的正则表达式,重写后的值 |
| SaveSession | 在转发请求之前,强制执行WebSession::save操作 | 无 |
| secureHeaders | 为原始响应添加一系列起安全作用的响应头 | 无,支持修改这些安全响应头的值 |
| SetPath | 修改原始的请求路径 | 修改后的路径 |
| SetResponseHeader | 修改原始响应中某个Header的值 | Header名称,修改后的值 |
| SetStatus | 修改原始响应的状态码 | HTTP 状态码,可以是数字,也可以是字符串 |
| StripPrefix | 用于截断原始请求的路径 | 使用数字表示要截断的路径的数量 |
| Retry | 针对不同的响应进行重试 | retries、statuses、methods、series |
| RequestSize | 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large | 请求包大小,单位为字节,默认值为5M |
| ModifyRequestBody | 在转发请求之前修改原始请求体内容 | 修改后的请求体内容 |
| ModifyResponseBody | 修改原始响应体的内容 | 修改后的响应体内容 |
| Default | 为所有路由添加过滤器 | 过滤器工厂名称及值 |
使用示例:
```yml
spring:
application:
name: spring-cloud-gateway-nacos
cloud:
# nacos 配置
nacos:
discovery:
server-addr: 127.0.0.1:8848
username: nacos
password: nacos
# gateway的配置
gateway:
# 路由规则
routes:
- id: product-route
uri: lb://service-product # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
predicates:
- Path=/order/** # 注意Path首字母大写
filters:
- StripPrefix=1
- AddRequestHeader=X-Request-color, red # 在请求头中添加一个X-Request-color属性,值为red,在下游可以使用@RequestHeader注解去获取进行使用
```
### 11、全局过滤器
1、Forward Routing Filter
2、ReactiveLoadBalancerClientFilter
3、Netty Routing Filter
4、Netty Write Response Filter
5、RouteToRequestUrl Filter
6、Websocket Routing Filter
7、Gateway Metrics Filter
局部过滤器和全局过滤器的区别:
局部:局部过滤器只针对某个路由,需要在路由中进行配置
全局:针对所有路由请求,一旦定义就会执行
注:详细笔记在语雀文档中记录(www.yuque.com/baoroot)
### 12、Gateway整合Sentinel流控降级
1、引入依赖
```xml
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
```
2、在项目配置文件中加上sentinel配置
```yml
spring:
cloud:
sentinel:
transport:
port: 8719 #sentinel通信接口
dashboard: 192.168.161.3:8774 #sentinel控制台服务地址
```
3、配置流控规则,配置接口流控规则,我们这里QPS设置为1,然后在接口测试响应码为429则实现Sentinel限流
4、如果想针对限流进行定制化信息响应,可以在WebFluxCallbackManager中注册回调自定义的BlockHandler
```java
public class MySentinelBlockHandler implements BlockRequestHandler {
@Override
public Mono handleRequest(ServerWebExchange exchange, Throwable t) {
ErrorResult errorResult = new MySentinelBlockHandler.ErrorResult(
HttpStatus.TOO_MANY_REQUESTS.value(),
"系统繁忙,请稍后再试!");
// JSON result by default.
return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
.contentType(MediaType.APPLICATION_JSON_UTF8)
.body(fromObject(errorResult));
}
private static class ErrorResult {
private final int code;
private final String message;
ErrorResult(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
}
/**
* 响应的结果为
* {
* code: 429,
* message: "系统繁忙,稍后再试!"
* }
*/
```