# SpringCloudDemo202509
**Repository Path**: nieps/spring-cloud-demo202509
## Basic Information
- **Project Name**: SpringCloudDemo202509
- **Description**: 案例
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2025-09-18
- **Last Updated**: 2025-09-25
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# SpringCloud笔记
## 系统架构


SAAS 软件既服务
租户/公司
## Nacos

> 其它注册中心:
>
> 1. eureak netflix 一代分布式用的
> 2. zookeeper
> 3. consule
服务健康检查:
* 1个5 默认每隔5s发一次心跳给服务器
* 3个5 15s没有收到服务器的心跳,服务器判定微服务不健康
* 6个5 30 s没有收到心跳,移除
## 服务通信
* RestTemplate SpringMVC中提供的一个请求rest api的工具类
> 1. 默认请求具体的ip 服务器
> 2. 结合负载均衡 ,请求服务名
## 配置中心
### 依赖
~~~xml
org.springframework.boot
spring-boot-starter-web
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-config
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-bootstrap
~~~
### 使用
1. bootstram.yml 配置 配置中心的地址 指定文件的格式 环境 服务器启动时加载
2. 在容器Bean中,如果使用了@Value 注解 绑定配置文件中的值 ,需要配置@RefreshScope// 当配置文件发生变化时 可以监听听
3. 属性采用属性批量绑定时,可以不指定@RefreshScope
### 配置列表
关于data-Id:
在 Nacos Server 中,配置的 dataId(即 Data ID)的完整格式如下:
~~~properties
${prefix}-${spring.profiles.active}.${file-extension}
~~~
dataId 格式中各参数说明如下:
- ${prefix}:默认取值为微服务的服务名,即配置文件中 spring.application.name 的值,我们可以在配置文件中通过配置 spring.cloud.nacos.config.prefix 来指定。
- ${spring.profiles.active}:表示当前环境对应的 Profile,例如 dev、test、prod 等。当没有指定环境的 Profile 时,其对应的连接符也将不存在, dataId 的格式变成 ${prefix}.${file-extension}。
- ${file-extension}:表示配置内容的数据格式,我们可以在配置文件中通过配置项 spring.cloud.nacos.config.file-extension 来配置,例如 properties 和 yaml。
## 负载均衡
* 服务器负载均衡 如nginx
* 客户端负载均衡 ribbon loadbanlance
掌握负载均衡的原理 ,用自己的语言描述出来。
> 主要的逻辑就是给 RestTemplate 增加拦截器,在请求之前对请求的地址进行替换,或者根据具体的负载策略选择服务地址,然后再去调用,这就是 @LoadBalanced 的原理
## OpenFeign
> 像调用本方法一样调用远程方法。
### 步骤
1. 服务消费者 引入依赖
~~~xml
org.springframework.cloud
spring-cloud-starter-openfeign
org.springframework.cloud
spring-cloud-starter-loadbalancer
~~~
2. 在主启动类上增加注解,启用feign支持
~~~java
@SpringBootApplication
@EnableDiscoveryClient//启用服务发现客户端
@EnableFeignClients//启用openfeign注解支持
public class OrderApp {
public static void main(String[] args) {
SpringApplication.run(OrderApp.class, args);
}
}
~~~
3. 定义feign客户端 (一个接口) 关键注解:@FeignClient
~~~java
@FeignClient(value = "sc-stock") //说明当前类是feign客户端类 value = "sc-stock"要调用的目标服务的名称
public interface StockClient {
/**
* 声明一个与目标同名的方法
* @param id
* @return
*/
@PutMapping("/stock")
Map updateStock(@RequestParam("id") int id);
}
~~~
### 日志
需要配置两个条件 :
* feign日志 指定feign远程请求时 输出的内容多少
> 通过源码可以看到日志等级有 4 种,分别是:
>
> - NONE:不输出日志。
> - BASIC:只输出请求方法的 URL 和响应的状态码以及接口执行的时间。
> - HEADERS:将 BASIC 信息和请求头信息输出。
> - FULL:输出完整的请求信息。
>
> 实现方式:
>
> 1. 通过配置类实现
>
> ~~~java
> @Configuration
> public class FeignConfig {
>
> /**
> * 向容器注入feign日志级别
> * @return
> */
> @Bean
> public Logger.Level level(){
> return Logger.Level.FULL;
> }
> }
> ~~~
>
> 2. 通过yml配置文件实现
>
> ~~~yaml
> feign:
> client:
> config:
> sc-stock: #map的key
> logger-level: full #日志级别
> ~~~
>
> config: 类型是: Map
>
> sc-stock: 是map的类 ,是对应的feign客户端关联的服务名
>
> map的值是FeignClientConfiguration,它的配置方式: 属性名: 值 。 logger-level 对应着FeignClientConfiguration类中的属性loggerLevel,用于配置日志级别
* 正常的日志级别,通常设置为debgu (类似于mybaits输出sql语句时要配置mapper的日志级别为debug),这里配置 fegin接口的日志级别
### 认证
两种认证:
* 基础认证
> 
>
> ~~~java
> @Override
> public void apply(RequestTemplate template) {
> template.header("Authorization", headerValue);
> }
> ~~~
>
> 增加请求头: Authorization= 加密过的值
* 自定义认证
> ~~~java
> /**
> * 自定义的 拦截器 传递token
> */
> @Component
> public class TokenRequestInterceptor implements RequestInterceptor {
>
> @Override
> public void apply(RequestTemplate template) {
> template.header("token","abc.afwe4r.adfasd");
> }
> }
> ~~~
对应服务提供者通过拦截器,提供验证功能 。
### 超时配置
#### 配置类
~~~java
/**
* Creates a new Options Instance.
*
* @param connectTimeout value.
* @param connectTimeoutUnit with the TimeUnit for the timeout value.
* @param readTimeout value.
* @param readTimeoutUnit with the TimeUnit for the timeout value.
* @param followRedirects if the request should follow 3xx redirections.
*/
public Options(long connectTimeout, TimeUnit connectTimeoutUnit,
long readTimeout, TimeUnit readTimeoutUnit,
boolean followRedirects){}
~~~
~~~java
/**
* 默认值:
1. 连接超时时间 10s
2. 读取超时时间是 60s
*/
public Options() {
this(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true);
}
~~~
#### yml配置超时
~~~yaml
feign:
client:
config:
sc-stock: #map的key
connect-timeout: 5000 #连接超时时长5000ms
read-timeout: 5000 #读取超时时长1000ms
logger-level: full #日志级别
~~~
#### 超时后处理
1. 捕获超时异常 反回 提示超时 剩下的交给服务的消费者处理
2. 重试机制 一旦超时 可以尝试 第二次 第三次。。。重新请求
> 默认:重试机制是关闭的
>
> 开启:
>
> 默认实现:Retryer.Default
>
> ~~~java
> public Default() {
> this(100, SECONDS.toMillis(1), 5);
> }
> /**
> * period 间隔多长时间重试一次 单位ms
> maxPeriod 最长间隔多长时间 单位ms
> maxAttempts 重试的次数
> */
> public Default(long period, long maxPeriod, int maxAttempts) {
> this.period = period;
> this.maxPeriod = maxPeriod;
> this.maxAttempts = maxAttempts;
> this.attempt = 1;
> }
> ~~~
>
> 实现方式:
>
> 配置类:
>
> ~~~java
> @Bean
> public Retryer retryer(){
> return new Retryer.Default(100,1000,3);
> }
> ~~~
>
> yml配置:
>
> ~~~yaml
> feign:
> client:
> config:
> sc-stock: #map的key
> logger-level: full #日志级别
> retryer: feign.Retryer.Default #重试机制 默认100ms 最大1000ms 尝试次数5
> ~~~
>
>
### 压缩(了解)
~~~yaml
feign:
client:
config:
sc-stock: #map的key
connect-timeout: 5000 #连接超时时长5000ms
read-timeout: 5000 #读取超时时长1000ms
logger-level: full #日志级别
retryer: feign.Retryer.Default #重试机制
httpclient:
enabled: true #默认值为true 代表默认使用httpclient请求 库
okhttp:
enabled: false #默认值为false 代表禁用okhttp请求
compression: #是否对请求响应数据压缩以节省网络资源
request:
enabled: true #默认值为false 代表请求的数据不压缩
min-request-size: 2048 #默认请求的内容达到2048字节 开始压缩
mime-types: #以下是默认的压缩类型
- application/json # json格式
- text/xml
- application/xml
response:
enabled: true # 默认值为false 代表请响应的数据不压缩
~~~
### 继承特性
通过将feign接口定义抽取出来,服务提供者依赖该接口并实现接口(通过这种方式 可以继承路径映射、参数,只需要提供实现),服务消费者依赖feign接口,只需要调用 。
声明feign接口时:
路径映射: 支持springmvc路径映射注解 ,feign接口声明路径映射后,实现该接口的控制类无需重新定义路径映射,可以直接使用。
* @RequestMapping
* @GetMapping
* @PostMapping
* @DeleteMapping
* @PutMapping
关于参数:
要求在声明feign接口时,必须指定参数注解 ,相关注解
* @RequestParam 指定具体的参数名
* @PathVariable 获取路径变量
* @RequestBody 获取json格式 参数 转换为javabean或map
## 网关
### 路由 Route
> Route 主要由 路由id、目标uri、断言集合和过滤器集合组成,那我们简单看看这些属性到底有什么作用。
(1)id:路由标识,要求唯一,名称任意(默认值 uuid,一般不用,需要自定义)
(2)uri:请求最终被转发到的目标地址
(3)order: 路由优先级,数字越小,优先级越高
(4)predicates:断言数组,即判断条件,如果返回值是boolean,则转发请求到 uri 属性指定的服务中
(5)filters:过滤器数组,在请求传递过程中,对请求做一些修改
### 断言

### 动态路由
两种方式 :
* uri 的值使用 lb://service-name lb负载均衡协议 service-name 微服务注册的名称
* 开启动态路由
~~~yaml
spring:
cloud:
gateway:
discovery:
locator:
enabled: true #开启动态路由
~~~
开启后,访问: http://网关IP:网关端口号/要访问的目标服务名/接口地址,如http://localhost/sc-order/order
### 过滤器
* AddRequestHeader 添加请求头
* AddRequestParameter 添加请求参数
* AddResponseHeader 增加响应头参数
token验证流程:
* 从请求头获取token 如果没有 返回无权访问
* 如果有 验证 有效 继续请求 无效返回
## Sentinel
### 资源的定义
#### 适配主流框架自动定义资源
为了减少开发的复杂程度,我们对大部分的主流框架,例如 Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor 等都做了适配。您只需要引入对应的依赖即可方便地整合 Sentinel。
以SpringMVC为例,默认使用DefaultBlockExceptionHandler 处理
~~~java
public class DefaultBlockExceptionHandler implements BlockExceptionHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
// Return 429 (Too Many Requests) by default.
response.setStatus(429);
PrintWriter out = response.getWriter();
out.print("Blocked by Sentinel (flow limiting)");
out.flush();
out.close();
}
}
~~~
#### SphU手动定义资源(了解)
通过SphU类定义资源,使用try-catch
#### `SphO` 提供 if-else 风格的 API
#### 注解处理异常
通过aop ,使用切面SentinelResourceAspect 来实现异常处理:
1. 先判断注解上有没有定义blockHandler方法 ,如果定义了,直接调用你定义的方法
2. 如果没有定义blockHandler方法,判断注解上是否定义 fallback方法,如果定义了,调用定义的方法
3. 如果没有定义 fallback方法,判断注解上是否定义 defaultFallback方法,如果定义了,调用定义的方法,如果没有定义 ,将异常抛给虚拟机
4. 如果异常抛给虚拟机后,可以通过全局异常处理,来处理异常
fallback不同于blockHandler:
* fallback 默认处理所有的异常(包括BlockException)
* fallback 不处理 注解属性exceptionsToIgnore里指定 的异常
注意:如下属性
* fallbackClass 对应存放 fallback 里指定 的方法
* blockHandlerClass 对应存放 blockHandler里指定 的方法
要求:
1. 方法public static
2. 方法的返回值与原方法一样
3. 方法的参数与原方法一样
4. 可以增加额外的参数 表示异常,fallback 方法对应的是Thowable, blockHandler 方法对应的是BlockException
### 规则
#### 流控规则


##### 流控模式
* *直接* 统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
* 关联 统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流

> 订单服务与商品服务,同时访问库存,竞争关系,当我们在商品服务在做限流时,设置关联资源为订单服务时,如果订单服务更新库存频繁,则 会限制商品服务的访问。
* 链路 统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流

> 上面订单和商品两个服务都要访问库存服务,如果库存服务设置 链路模式且入口资源是商品服务,
>
> 这时的qps只会 统计 来自于商品服务的请求,当达到阈值时,只会 限制商品服务。
##### 流控效果
* 快速失败 **直接拒绝** 当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出`FlowException` 默认值
* Warm Up 预热/冷启动方式

* 排队等待

当qps达到阈值时,有新的请求进来,判断该请求是否可以加入队列。
当一个请求进来时,要判断如果加入队列,需要等待的时间是否超进timeout,(看队列上请求的数量\* 每次请求耗费时长),如果预期时间超过timeout,直接拒绝,如果没有超过,加入队列等待。
**注意**:匀速排队模式暂时不支持 QPS > 1000 的场景。
### 熔断规则


### fallback降级处理(了解)
1. 在@FeignClient注解上通过属性fallback指定实现feign接口降级处理实现类
~~~java
@FeignClient(value = "sc-sentinel",fallback = UserClientFallback.class)
public interface UserClient {
@GetMapping("/user5")
public Map query5();
}
~~~
2. feign接口实现类 降级逻辑 当通过feign远程调用时,**超时或者异常 走降级**
~~~java
/**
* feign客户端对应的降级处理类
*/
@Component
public class UserClientFallback implements UserClient{
@Override
public Map query5() {
Map map=new HashMap();
map.put("code",0);
map.put("msg","feign降级处理");
return map;
}
}
~~~
3. 必须引入熔断器 这里使用sentinel 支持feign
~~~yaml
feign:
client:
config:
sc-sentinel:
logger-level: none
read-timeout: 200 #读200ms超时
sentinel:
enabled: true # 开启sentinel对fegin的支持 引入熔断器
~~~
### 熔断与feign fallback
两者都是调用远程服务,远程服务出现异常或者超时(在熔断里是慢调用 ),保护自己不被远程拖垮做的降级处理。
区别:
* 熔断 如果出现慢调用 短路器打开 受保存的服务走降级处理,不再调用远程服务 (远程服务压力减少)
* feign fallback 出现超时或异常时走降级处理,但是 仍然会调用远程服务(远程服务仍然存在压力)
### 熔断器状态

### 异常比例

### 异常数

### 授权规则
## 分布式事务
单体项目:
* 在业务方法上增加@Transactional
> 复杂:
>
> 1. spring 事务什么情况下会失效?(声明式事务,使用aop,原理是动态代理 ,动态代理失效 事务也会失效 )
> 2. 嵌套事务 涉及到 事务的传播行为 7种
在微服务中,每个微服务 独立运行,数据库独立
模块两个服务:
* 订单服务
> 多条订单明细,每个明细包含商品id 数量 。。。
* 库存服务
CP : 一致性 分区容错性 所有节点的数据一致,这个时候某个节点网络端了,(跟其它节点不能通信 ),不影响该节点对外提供服务 (数据是一致)
AP: 可用性 分区容错性, 牺牲了一致性, 部分节点数据 是新的,部分节点数据是旧的 ,保存了可用性