# 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笔记 ## 系统架构 ![image-20250918103836131](assets/image-20250918103836131.png) ![image-20250918103844397](assets/image-20250918103844397.png) SAAS 软件既服务 租户/公司 ## Nacos ![image-20250918142138236](assets/image-20250918142138236.png) > 其它注册中心: > > 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接口的日志级别 ### 认证 两种认证: * 基础认证 > ![image-20250919151645263](assets/image-20250919151645263.png) > > ~~~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:过滤器数组,在请求传递过程中,对请求做一些修改 ### 断言 ![image-20250922114017851](assets/image-20250922114017851.png) ### 动态路由 两种方式 : * 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 ### 规则 #### 流控规则 ![image-20250923152147728](assets/image-20250923152147728.png) ![image-20250923152257821](assets/image-20250923152257821.png) ##### 流控模式 * *直接* 统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式 * 关联 统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流 ![image-20250923153819842](assets/image-20250923153819842.png) > 订单服务与商品服务,同时访问库存,竞争关系,当我们在商品服务在做限流时,设置关联资源为订单服务时,如果订单服务更新库存频繁,则 会限制商品服务的访问。 * 链路 统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流 ![image-20250923154211847](assets/image-20250923154211847.png) > 上面订单和商品两个服务都要访问库存服务,如果库存服务设置 链路模式且入口资源是商品服务, > > 这时的qps只会 统计 来自于商品服务的请求,当达到阈值时,只会 限制商品服务。 ##### 流控效果 * 快速失败 **直接拒绝** 当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出`FlowException` 默认值 * Warm Up 预热/冷启动方式 ![image-20250923163338801](assets/image-20250923163338801.png) * 排队等待 ![image-20250923164314735](assets/image-20250923164314735.png) 当qps达到阈值时,有新的请求进来,判断该请求是否可以加入队列。 当一个请求进来时,要判断如果加入队列,需要等待的时间是否超进timeout,(看队列上请求的数量\* 每次请求耗费时长),如果预期时间超过timeout,直接拒绝,如果没有超过,加入队列等待。 **注意**:匀速排队模式暂时不支持 QPS > 1000 的场景。 ### 熔断规则 ![image-20250923174127840](assets/image-20250923174127840.png) ![image-20250924112319907](assets/image-20250924112319907.png) ### 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 出现超时或异常时走降级处理,但是 仍然会调用远程服务(远程服务仍然存在压力) ### 熔断器状态 ![image-20250924113417183](assets/image-20250924113417183.png) ### 异常比例 ![image-20250924113707275](assets/image-20250924113707275.png) ### 异常数 ![image-20250924114501793](assets/image-20250924114501793.png) ### 授权规则 ## 分布式事务 单体项目: * 在业务方法上增加@Transactional > 复杂: > > 1. spring 事务什么情况下会失效?(声明式事务,使用aop,原理是动态代理 ,动态代理失效 事务也会失效 ) > 2. 嵌套事务 涉及到 事务的传播行为 7种 在微服务中,每个微服务 独立运行,数据库独立 模块两个服务: * 订单服务 > 多条订单明细,每个明细包含商品id 数量 。。。 * 库存服务 CP : 一致性 分区容错性 所有节点的数据一致,这个时候某个节点网络端了,(跟其它节点不能通信 ),不影响该节点对外提供服务 (数据是一致) AP: 可用性 分区容错性, 牺牲了一致性, 部分节点数据 是新的,部分节点数据是旧的 ,保存了可用性