# SpringCloud **Repository Path**: fanode/springcloud ## Basic Information - **Project Name**: SpringCloud - **Description**: springcloud - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-05-05 - **Last Updated**: 2024-12-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 常用链接 * springboot * git源码地址:[Releases · spring-projects/spring-boot (github.com)](https://github.com/spring-projects/spring-boot/releases/) * springboot官网:[Spring Boot](https://spring.io/projects/spring-boot) * 中文官网:[Spring Boot 中文文档 (springdoc.cn)](https://springdoc.cn/spring-boot/) * springcloud * git源码地址: [Spring Cloud · GitHub](https://github.com/spring-cloud) * 官网:[Spring Cloud](https://spring.io/projects/spring-cloud#overview) * springcloud albab * git版本说明:[alibaba](https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E) * 中文文档:[spring-cloud-alibaba/README-zh.md at 2023.x · alibaba/spring-cloud-alibaba · GitHub](https://github.com/alibaba/spring-cloud-alibaba/blob/2023.x/README-zh.md) * 查询bug * [Newest 'spring-cloud' Questions - Stack Overflow](https://stackoverflow.com/questions/tagged/spring-cloud) # 版本选择 * cloud决定boot版本 * Java: Java17+ * cloud 2023.0.0 * boot 3.2.0 * cloud alibba 2020.0.0.0 | 2023.0.0.0-RC1 [解决feign和sentinel兼容问题] * Maven 3.9+ * Mysql 8.0+ # 实用工具 * 绘图软件:[Excalidraw](https://excalidraw.com/) # 提示 * 微服务小口诀 1. 建module 2. 改pom 3. 写YML 4. 主启动 5. 业务类 * 新技术学习方法 * 是什么 * 能干嘛 * 去哪喜爱 * 这么玩 # 概念 * CAP原则,是分布式计算机领域的概念 * C一致性 * 同一时间数据相同 * A可用性 * 任何时候都能响应客户端的读写需求,即使某些节点失败 * P分区容错性 * 网络问题导致的错误时,依然能运作 * 通常视为分布式系统必须处理的问题 # # RestTemplate [实现独立的两个微服务之间的调用] * 官网:[RestTempalate](https://docs.spring.io/spring-framework/docs/6.0.11/javadoc-api/org/springframework/web/client/RestTemplate.html) * 提供了多种便携访问远程Http服务的方法 * 使用方式: * 配置类中new RestTemplate()并加入ioc容器中 * 使用restTemplate访问restful接口 * 参数: REST请求地址 , 请求参数 , HTTP响应转换成的对象类型 * 这个模块并不是SpringCloud的内容 * 后续REST请求地址可以通过Consul进行改进 # Consul [服务注册与发现] * 官网:[Consul by HashiCorp](https://www.consul.io/) * 为什么不使用老牌的Eureka * Eureka停更 * 初学者不友好,自我保护机制 * 不独立,且不解耦 * Nacos的崛起 * Eureka不支持分布式配置中心 * Consul属于CAP中的CP,而Eureka属于AP * 禁止使用问题 * 中国出口管控调条例禁止hashicorp推销或使用企业版Vault * 禁止使用的是同公司的Vault,不是Consul * 命令: * consul -version 查看版本号 * consul agent -dev 开发者模式启动 * 访问首页: http://localhost:8500 * 注册与发现使用: * POM添加依赖:spring-cloud-starter-consul-discovery * YAL添加 * cloud: consul: host: localhost port: 8500 discovery: service-name: ${spring.application.name} * 启动类添加:@EnableDiscoveryClient * 启动时可能会弹出Standard Commons Logging discovery in action with spring-jcl: please remove commons-logging.jar from classpath in order to avoid potential conflicts * 需要排除名为commons-logging的依赖 * 即在对应导的依赖下exclusions - exclusion - 排除的依赖 * RestTemplate的改进 * 发送通信的url的http://localhost:端口号可以改为http://Consul服务注册中心上的名称 * 原因:Consul自带负载均衡,需要支持,默认多个负载均衡轮序 * 需要在配置类中new RestTemplate()需要添加@LoadBalanced 注解以支持 * 注解 * @EnableDiscoveryClient 主启动类注解,标识改程序会向服务中心注册 * @LoadBalanced 使RestTeamplate支持均衡负载轮序 # Consul [分布式配置和管理] * 每一个服务都需要必要的配置信息才能运作,例如端口号,所以需要一套集中动态的配置管理设施 * 分布式配置和管理使用: * 导入spring-cloud-starter-consul-config | spring-cloud-starter-bootstrap 两个依赖 * 新增bootstrap.yml * application.yml是用户级的,而bootstrap.yml是系统级的,优先级更高,所以不会被覆盖 * 从cloud中提取一部分配置信息 * consul配置中心 key/Value 新增config/#{spring.cloud.service-name}/#{spring.profiles.active}/data * spring.cloud.service-name 一般放置在bootstrap中 * spring.profiles.active 一般放置在application中 * data 即需要读取yml内容 * Consul的配置持久化 * 将Consul数据持久化并注册为windows服务 * 注解 * @RefreshScope 主启动类注解,使Consul配置文件修改可以直接同步 # LoadBalancer [负载均衡和服务调用] * 官网: [Spring Cloud LoadBalancer :: Spring Cloud Commons](https://docs.spring.io/spring-cloud-commons/reference/spring-cloud-commons/loadbalancer.html) * 作用:由SpringCloud官方提供的一个开源的客户端负载均衡器 * 客户端负载和服务端负载均衡的区别? * Nginx是服务器端负载均衡,客户端的所有请求都会交给Nginx,由Nginx实现转发请求, * Loadbalancer本地负载均衡,在调用微服务接口时,会在Consul中获取到服务接口列表,找到空闲并调用 * Ribbon已经进入维护,不再使用,LoadBalancer只是平替 * 使用[系统用户端]: * POM添加依赖spring-cloud-starter-loadbalancer * 如果需要使用负载均衡轮序,需要多个微服务的spring.cloud.service-name一致 * 如果使用 RestTemplate 请在注册类中添加@LoadBalanced注解以支持负载均衡轮序 * 注解 * @LoadBalanced 使RestTeamplate支持均衡负载轮序 # OpenFeign [服务接口调用] * 官网:[Spring Cloud OpenFeign](https://spring.io/projects/spring-cloud-openfeign) * 作用:为了解耦和规范,让服务提供者被多个系统用户端调用时允许暴露那些接口,通过接口进行调用 * 也是客户端的负载均衡器 * 同时也集成了LoadBalancer * 声明式Web服务客户端,当前微服务之间调用的事实标准,只需创建一个Rest接口并添加@FeignClient即可 * LoadBalancer 和 OpenFeign 都是用于服务调用和负载均衡 * 当使用Feign客户端调用其他服务时,Feign会自动与`LoadBalancer` 集成,以确保请求被路由到合适的服务实例。 * 使用[系统用户端]: * POM添加依赖:spring-cloud-starter-openfeign * 主启动类添加注解:@EnableFeignClients * 编写Feign客户端接口,添加@FeignClient("#{spring.cloud.service-name}") * #{spring.cloud.service-name} 在Consul也能直接复制,写的是那个,就对应的那个模块 * 编写服务端暴露的方法。方法和服务提供者的Controller一致即可 * 在Controller层直接使用@Resource获得客户端接口的实现类[由fegin实现],调用方法 * OpenFeigng高级特性 * 超时控制 * 默认超时60s。超过会报错 * 如果需要修改需要修改 connectTimeout 和 readTimeout 两个参数 * yml文件 spring.cloud.openfeign.client.config.#{xxx} * #{xxx} 为defalut 即为全局配置 * #{xxx} 为 #{spring.cloud.service-name}, 即为指定的服务提供者 * 重试机制 * 默认关闭 * 开启Retryer功能: * 将配置类中将new Retryer.Default(初始间隔毫秒,最大间隔秒,最大请求次数);加入到ioc容器 * 注意:重试时会触发负载均衡 * 默认HttpClient修改 * OpenFeign默认使用JDK自带的HttpURLConection发送HTTP请求,没有连接池性能和效率比较低 * 使用Apache HttpClient 5 替换 * POM引入依赖 httpclient5 和 feign-hc5 两个依赖,版本高依赖 * YML开启配置:spring.cloud.openfeign.httpclient.hc5.enabled = true * 请求/响应压缩 * YML开启配置:spring.cloud.openfeign ```    # 开启压缩 compression: request: enabled: true min-request-size: 2048 #最小触发压缩的大小 mime-types: text/xml,application/xml,application/json #触发压缩数据类型 response: enabled: true ``` * 日志打印功能 * 对Feign接口的调用情况进行监控和输出 * 开启配置 * YML文件 logging.level.要被监控的类的全限定符 = debug * 配置类 return Logger.Level.监控强度 # CircuitBreaker (断路器)[服务的熔断和降级] * Hystrix[豪猪]目前也进入维护模式 * 断路器: * 服务降级:后台挂了快速返回系统繁忙,请稍后再试,而不是让用户等待 * 服务雪崩:多个微服务之间调用。某个调用寄了,就会占用大量系统资源引起系统崩溃 * 熔断器:解决有问题的节点,快速熔断【烧保险丝跳闸】,向调用方返回一个符合预期的备选响应【FallBack】,而不是长时间等待或者抛出无法处理的异常 * CircuitBreaker是一套规范和接口,支持且落地的实现 Resilience4j 和 Spring Retry * 当一个组件寄了,CircuitBreaker会迅速切换为开放OPEN状态。阻止发送请求到该组件的请求,过一段时间会进入半开放HALF_OPEN状态,尝试通过几个请求,如果可用则进入闭合CLOSED状态,完全接收请求,如果还是不可以则依然为开放OPEN状态 # Resilience4j [服务的熔断和降级] * 作用:轻量级的容错库 * 中文文档:[Resilience4j-Guides-Chinese)](https://github.com/lmhmhl/Resilience4j-Guides-Chinese/blob/main/index.md) * 兜底方法:返回类型同原方法一致 兜底的方法名 (参数同原方法一致,Throwable 异常) * 熔断(CircuitBreaker)(服务熔断和服务降级)使用 * 两种实现 按照计数COUNT_BASED 和按照时间TIME_BASED * POM添加 spring-cloud-starter-circuitbreaker-resilience4j 和 spring-boot-starter-aop 依赖 * YML添加 * spring.cloud.circuitbreaker.enabled = true * spring.cloud.circuitbreaker.group.enabled = true * ```yml 按照计数 两者取其一。推荐使用计数 # Resilience4j CircuitBreaker 按照次数:COUNT_BASED 的例子 # 6次访问中当执行方法的失败率达到50%时CircuitBreaker将进入开启OPEN状态(保险丝跳闸断电)拒绝所有请求。 # 等待5秒后,CircuitBreaker 将自动从开启OPEN状态过渡到半开HALF_OPEN状态,允许一些请求通过以测试服务是否恢复正常。 # 如还是异常CircuitBreaker 将重新进入开启OPEN状态;如正常将进入关闭CLOSE闭合状态恢复正常处理请求。 resilience4j: circuitbreaker: configs: default: failureRateThreshold: 50 #设置50%的调用失败时打开断路器,超过失败请求百分⽐CircuitBreaker变为OPEN状态。 slidingWindowType: COUNT_BASED # 滑动窗口的类型 slidingWindowSize: 6 #滑动窗⼝的⼤⼩配置COUNT_BASED表示6个请求,配置TIME_BASED表示6秒 minimumNumberOfCalls: 6 #断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。如果minimumNumberOfCalls为10,则必须最少记录10个样本,然后才能计算失败率。如果只记录了9次调用,即使所有9次调用都失败,断路器也不会开启。 automaticTransitionFromOpenToHalfOpenEnabled: true # 是否启用自动从开启状态过渡到半开状态,默认值为true。如果启用,CircuitBreaker将自动从开启状态过渡到半开状态,并允许一些请求通过以测试服务是否恢复正常 waitDurationInOpenState: 5s #从OPEN到HALF_OPEN状态需要等待的时间 permittedNumberOfCallsInHalfOpenState: 2 #半开状态允许的最大请求数,默认值为10。在半开状态下,CircuitBreaker将允许最多permittedNumberOfCallsInHalfOpenState个请求通过,如果其中有任何一个请求失败,CircuitBreaker将重新进入开启状态。 recordExceptions: - java.lang.Exception instances: cloud-payment-service: #需要设置熔断器的微服务实例名 baseConfig: default 按照时间 两者取其一。推荐使用计数 # Resilience4j CircuitBreaker 按照时间:TIME_BASED 的例子 resilience4j: timelimiter: configs: default: timeout-duration: 10s #神坑的位置,timelimiter 默认限制远程1s,超于1s就超时异常,配置了降级,就走降级逻辑 circuitbreaker: configs: default: failureRateThreshold: 50 #设置50%的调用失败时打开断路器,超过失败请求百分⽐CircuitBreaker变为OPEN状态。 slowCallDurationThreshold: 2s #慢调用时间阈值,高于这个阈值的视为慢调用并增加慢调用比例。 slowCallRateThreshold: 30 #慢调用百分比峰值,断路器把调用时间⼤于slowCallDurationThreshold,视为慢调用,当慢调用比例高于阈值,断路器打开,并开启服务降级 slidingWindowType: TIME_BASED # 滑动窗口的类型 slidingWindowSize: 2 #滑动窗口的大小配置,配置TIME_BASED表示2秒 minimumNumberOfCalls: 2 #断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。 permittedNumberOfCallsInHalfOpenState: 2 #半开状态允许的最大请求数,默认值为10。 waitDurationInOpenState: 5s #从OPEN到HALF_OPEN状态需要等待的时间 recordExceptions: - java.lang.Exception instances: cloud-payment-service: #需要设置熔断器的微服务实例名 baseConfig: default ``` * Controller 添加注释: @CircuitBreaker(name = "cloud-payment-service", fallbackMethod = "myCircuitFallback ") * name:熔断器的微服务实例名 * fallbackMethod:兜底服务降级的方法 * 降级方法 - 服务降级 * 舱壁隔离(BulkHead) * 作用:依赖隔离&负载保护:用于限制对于下游服务的最大并发数量和限制,防止局部崩溃导致的全局崩溃 * 两种实现方式 * SemaphoreBulkhead(信号量舱壁) * 信号量空闲则处理,全占则阻塞,超时拒请求,获信号量则处理。 * POM添加依赖:resilience4j-bulkhead * YML: * ```yml ####resilience4j bulkhead 的例子 resilience4j: bulkhead: configs: default: maxConcurrentCalls: 2 # 隔离允许并发线程执行的最大数量 maxWaitDuration: 1s # 当达到并发调用数量时,新的线程的阻塞时间,我只愿意等待1秒,过时不候进舱壁兜底fallback instances: cloud-payment-service: #需要设置熔断器的微服务实例名 baseConfig: default ``` * Controller 添加注释:@Bulkhead(name = "cloud-payment-service",fallbackMethod = "myBulkheadFallback",type = Bulkhead.Type.SEMAPHORE) * name:舱壁隔离用于的微服务实例名 * fallbackMethod:兜底方法 * type:指定信号量舱壁的方式 * 其他 * FixedThreadPoolBulkhead(固定线程舱壁) * 线程池空闲处理等待队列任务和请求,无空闲进入等待队列,等待队列无空间则直接拒接请求 * POM添加依赖:resilience4j-bulkhead * YML: * ```yaml ####resilience4j bulkhead -THREADPOOL的例子 resilience4j: timelimiter: configs: default: timeout-duration: 10s #timelimiter默认限制远程1s,超过报错不好演示效果所以加上10秒 thread-pool-bulkhead: configs: default: core-thread-pool-size: 1 max-thread-pool-size: 1 # 最大线程包含核心线程 所以可以处理1+1个服务,第三个报错 queue-capacity: 1 instances: cloud-payment-service: baseConfig: default # spring.cloud.openfeign.circuitbreaker.group.enabled 请设置为false 新启线程和原来主线程脱离 ``` * 因为是JUC,所以Controller 添加注释如上,修改type为type = Bulkhead.Type.THREADPOOL,且方法返回值多包一层CompletableFuture * CompletableFuture<原返回值类型> * return CompletableFuture.supplyAsync(() -> 原返回值); * 限流(RateLimiter) * 作用:对并发请求进行限速,或者对一个时间窗口内的请求进行限速,以保护系统 * 实现 * POM添加依赖:resilience4j-ratelimiter * YML: * ```yml ####resilience4j ratelimiter 限流的例子 resilience4j: ratelimiter: configs: default: limitForPeriod: 2 #在一次刷新周期内,允许执行的最大请求数 limitRefreshPeriod: 1s # 限流器每隔limitRefreshPeriod刷新一次,将允许处理的最大请求数量重置为limitForPeriod timeout-duration: 1 # 线程等待权限的默认等待时间 instances: cloud-payment-service: baseConfig: default ``` * Controller 添加注释:@RateLimiter(name = "cloud-payment-service",fallbackMethod = "myRatelimitFallback") * 注释: * 以下方法公用参数 * name= 需要设置的微服务在Consul上的实例名 * fallbackMethod = 兜底的方法,同方法的返回值和参数args... 参数额外增加一个Throwable 异常 * @CircuitBreaker 熔断 * @Bulkhead 舱壁隔离 * type = 使用的舱壁隔离实现方式 * @RateLimiter 限流 # Micrometer + ZipKin[分布式链路追踪] * Sleuth已停止维护,改头换面为Micrometer * 功能:分布式请求都是一条复杂的分布式服务调用链路,任何高延迟或错误都会引起整个请求的崩溃,所以我们需要实时观测整体调用链路情况 * 可以获得:各个服务节点的耗时,请求具体到达的机器,每个服务的请求状态等等 * Micrometer【收集】提供了一套完整的分布式链路追踪解决方案且兼容支持了zipkin【展示】展示 * 使用[需要监控的都得加]: * POM导入:详见POM文件夹 * YML修改: * ```yaml # ========================zipkin=================== management: zipkin: tracing: endpoint: http://localhost:9411/api/v2/spans tracing: sampling: probability: 1.0 #采样率默认为0.1(0.1就是10次只能有一次被记录下来),值越大收集越及时。 ``` * 启动ZipKin ,浏览器查看[Zipkin](http://127.0.0.1:9411/zipkin/) # Gateway [网关] * Spring生态系统上构建的API网关服务,为微服务提供一种统一的API路由管理方式 * 作用:一系列的过滤器,通过这些过滤器将客户端的请求转发到对应的微服务,同时也是整个微服务最前沿的防火墙和代理器,隐藏微服务节点IP端口信息 * Gateway本身也是一个微服务,需要注册进服务注册中心 * 关系:请求-> negix[负载均衡] -> Gateway -> 微服务 * 三大核心概念: * Route[路由]:   URL匹配 [这么找] * Predicate[断言]:匹配携带内容 [能不能访问] * Filter[过滤器]:对请求进行修改 * 基本使用 * POM引入依赖:spring-cloud-starter-gateway * YML配置: * ```yml spring.cloud下的 gateway: routes: - id: pay_routh1 #pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名 # uri: http://localhost:8001 #匹配后提供服务的路由地址 uri: lb://cloud-payment-service //支持负载均衡 lb即LoadBalancer predicates: - Path=/pay/gateway/get/** # 断言,路径相匹配的进行路由 ``` - id: pay_routh2 #pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名 # uri: http://localhost:8001 #匹配后提供服务的路由地址 uri: lb://cloud-payment-service //支持负载均衡 lb即LoadBalancer predicates: - Path=/pay/gateway/info/** # 断言,路径相匹配的进行路由 ``` ``` * 实例: * 混淆之后http://localhost:8001/pay/gateway/get/1 现在可以通过http://localhost:9527/pay/getway/get/1 访问了 * 另外一提feign是微服务内部调用 * 所以这就产生了两个路径 * 系统内环境,直接找微服务 * 系统外访问,先找网关再服务 * feign应该先找网关,即@FeignClient("cloud-gateway") * 关系链 用户->feign->网关->微服务 * Route以微服务名-动态获取服务URL * 在配置route url时候可以使用 lb://微服务名 即负载均衡来配置。他会从服务注册中心中获取以完成路由功能 * Predicates断言 * [Route Predicate Factories :: Spring Cloud Gateway](https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/request-predicates-factories.html) * 组成predicates: - 断言的类型 = 值 * 实例 : * ```yml 断言,路径相匹配的进行路由 - Path=/pay/gateway/info/** 时间格式:ZonedDateTime 在这个时间段之后才能访问 - After=2024-06-11T14:21:53.752033200+08:00[Asia/Hong_Kong] 在这个时间段之前可以访问 - Before=2024-06-11T14:21:48.284306300+08:00[Asia/Hong_Kong] 在这个时间段之内可以访问 - Between=2024-06-11T14:21:53.752033200+08:00[Asia/Hong_Kong], 2024-06-11T14:21:48.284306300+08:00[Asia/Hong_Kong] Cookie需要两个参数,分别是Cookiename的名字 和 值的正则表达式 - Cookie=username,value 请求头需有X-Request-Id属性值为整数的正则表达式 - Header=X-Request-Id, \d+ 含有主机地址 - Host=**.atguigu.com 要有Query参数为username并且值为整数 - Query=username, \d+ 只允许GET和POST请求 - Method=GET,POST ``` * Filter过滤器 * 请求头相关 * AddRequestHeader:添加请求头 * RemoveRequestHeader:删除请求头 * SetRequestHeader:修改请求头 * ```yml - id: pay_routh3 #pay_routh3 uri: lb://cloud-payment-service #匹配后提供服务的路由地址 predicates: - Path=/pay/gateway/filter/** # 断言,路径相匹配的进行路由 请求头kv,若一头含有多参则重写一行设置 filters: 该请求经过网关会被添加下面两个请求头 - AddRequestHeader=X-Request-atguigu1,atguiguValue1 - AddRequestHeader=X-Request-atguigu2,atguiguValue2 删除请求头sec-fetch-site - RemoveRequestHeader=sec-fetch-site 修改请求头     - SetRequestHeader=sec-fetch-mode, Blue-updatebyzzyy ``` ``` ``` * 请求参数相关 * AddRequestParameter:添加请求参数 * RemoveRequestParameter:删除请求参数 * 回应头相关 * AddResponseHeader:添加回应头参数 * SetResponseHeader * RemoveResponseHeader * 前缀和路径相关组 * PrefixPath:自动添加路径前缀 * SetPath:访问路径修改 * RedirectTo:请求转发重定向 * 示例: * ```yml - id: pay_routh3 #pay_routh3 uri: lb://cloud-payment-service #匹配后提供服务的路由地址 predicates: #- Path=/pay/gateway/filter/** # 被分拆为: PrefixPath + Path 1. - Path=/gateway/filter/** # 断言,为配合PrefixPath测试过滤,暂时注释掉/pay 2. - Path=/XYZ/abc/{segment} filters: predicates的path会被拼接为/pay/gateway/filter/** 但是用户访问是/gateway/filter/** 用于隐藏真实地址 1. - PrefixPath=/pay # http://localhost:9527/pay/gateway/filter 修改访问路径 XYZ=pay abc=gateway {segment}是占位符,前面两个都会被网关修改,但是最后这个不会,最后这个是用户输入的路径 2. - SetPath=/pay/gateway/{segment} 如果你访问时执行302重定向,会被重定向到尚硅谷首页 3. - RedirectTo=302, http://www.atguigu.com/ # 访问http://localhost:9527/pay/gateway/filter跳转到http://www.atguigu.com/ ``` ``` ``` * 其他 * Default Filters:全局配置,自定义秒变Global,不推荐使用 * spring.cloud.gateway.default-filters:配置