# spring-cloud-project **Repository Path**: huqingtao/spring-cloud-project ## Basic Information - **Project Name**: spring-cloud-project - **Description**: 微服务架构基础:nacos+gateway+sentinel+sentinel-dashboard+sleuth+zipkin+openfeign - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 2 - **Created**: 2021-03-16 - **Last Updated**: 2024-12-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 单项目多端口启动 VMoptions: -Dserver.port=8081 ### nacos 1. 本地nacos单机启动 bin目录 startup.cmd -m standalone 2. nacos web后台 nacos/nacos http://localhost:8848/nacos/index.html 3. 恢复nacos配置中心的sentinel配置 SENTINEL_GROUP.zip 解压移到:C:\Users\huqingtao\nacos\config\fixed-127.0.0.1_8848_nacos\snapshot nacos_offline.sh 主要解决:微服务间通过openFeign调用时本地ip:port缓存未及时拉去最新的 > 如果一个服务集群数量超过三个,也可不配置下线操作,配置好重试即可解决 ``` #!/bin/bash if [ $ENV = test ];then mse_path="nacos_domain" namespaceId="namespage" elif [ $ENV = uat ];then mse_path="nacos_domain" namespaceId="namespage" elif [ $ENV = pro ];then mse_path="nacos_domain" namespaceId="namespage" fi ip=`ip a |grep 192.168 |awk -F" +|\/" '{print $3}'` curl -X PUT "${mse_path}:8848/nacos/v1/ns/instance?serviceName=$serviceName&namespaceId=$namespaceId&ip=$ip&port=$port&weight=0" ``` ### gateway核心 #### filter Spring-Cloud-Gateway的过滤器接口分为两种: GlobalFilter : 全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器 GatewayFilter : 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上。 #### 验证动态参数filter:AddUserInfoFilter http://localhost:7060/provider/test/hello?str=kingtao http://localhost:7060/provider/test/hello3?str=kingtao http://localhost:7060/provider/test/hello5?str=kingtao&num=0 http://localhost:7060/provider/test/hello5?str=kingtao&num=1 http://localhost:7060/provider/test/hello6?str=kingtao&num=1 http://localhost:7060/provider/test/hello7?str=kingtao&num=1 http://localhost:7060/provider/test/hello8?str=kingtao http://localhost:7060/provider/test/hello9?str=kingtao http://localhost:7060/provider/test/hello10?str=kingtao http://localhost:7060/consumer/test/send?str=kingtao http://localhost:7060/consumer/test/send2?str=kingtao ### sentinel [文档](https://sentinelguard.io/zh-cn/docs/introduction.html) 1. 本地sentinel单机启动 java -server -Xms64m -Xmx256m -Dserver.port=8849 -Dcsp.sentinel.dashboard.server=localhost:8849 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar 2. sentinel web后台 sentinel/sentinel http://localhost:8849 3. 规则配置说明 + 流控规则 ``` [ { // 资源名 "resource": "/test", // 针对来源,若为 default 则不区分调用来源 "limitApp": "default", // 限流阈值类型(1:QPS;0:并发线程数) "grade": 1, // 阈值 "count": 1, // 是否是集群模式 "clusterMode": false, // 流控效果(0:快速失败;1:Warm Up(预热模式);2:排队等待) "controlBehavior": 0, // 流控模式(0:直接;1:关联;2:链路) "strategy": 0, // 预热时间(秒,预热模式需要此参数) "warmUpPeriodSec": 10, // 超时时间(排队等待模式需要此参数) "maxQueueingTimeMs": 500, // 关联资源、入口资源(关联、链路模式) "refResource": "rrr" } ] 示例: [ { "resource": "provider-test0", "limitApp": "default", "grade": 1, "count": 1, "strategy": 0, "controlBehavior": 0, "clusterMode": false }, { "resource": "provider-hello3", "limitApp": "default", "grade": 1, "count": 1, "strategy": 0, "controlBehavior": 0, "clusterMode": false } ] ``` + 熔断降级规则: 熔断降级强依赖自定义fallBack方法 ``` [ { // 资源名 "resource": "/test1", "limitApp": "default", // 熔断策略(0:慢调用比例,1:异常比率,2:异常计数) "grade": 0, // 最大RT、比例阈值、异常数 "count": 200, // 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) "slowRatioThreshold": 0.2, // 最小请求数 "minRequestAmount": 5, // 当单位统计时长(类中默认1000) "statIntervalMs": 1000, // 熔断时长 "timeWindow": 10 } ] 示例: [ { "resource": "provider-hello5", "limitApp": "default", "grade": 2, "count": 1, "slowRatioThreshold": 0.1, "minRequestAmount": 2, "statIntervalMs": 2000, "timeWindow": 10 },{ "resource": "provider-hello6", "limitApp": "default", "grade": 0, "count": 50, "slowRatioThreshold": 0.5, "minRequestAmount": 4, "statIntervalMs": 2000, "timeWindow": 10 },{ "resource": "/test/hello7", "limitApp": "default", "grade": 0, "count": 50, "slowRatioThreshold": 0.5, "minRequestAmount": 4, "statIntervalMs": 2000, "timeWindow": 10 } ] ``` + 热点参数规则:热点参数的注意点,1.必须定义资源名 2.参数必须是基本类型或者String(也就是方法体上面的参数必须为基本类型或String) 3.参数例外项优先级更高 ``` [ { // 资源名 "resource": "/test1", // 限流模式(QPS 模式,不可更改) "grade": 1, // 参数索引 "paramIdx": 0, // 单机阈值 "count": 13, // 统计窗口时长 "durationInSec": 6, // 是否集群 默认false "clusterMode": 默认false, // "burstCount": 0, // 集群模式配置 "clusterConfig": { // "fallbackToLocalWhenFail": true, // "flowId": 2, // "sampleCount": 10, // "thresholdType": 0, // "windowIntervalMs": 1000 }, // 流控效果(支持快速失败和匀速排队模式) "controlBehavior": 0, // "limitApp": "default", // "maxQueueingTimeMs": 0, // 高级选项 "paramFlowItemList": [ { // 参数类型 "classType": "int", // 限流阈值 "count": 222, // 参数值 "object": "2" } ] } ] 示例: [ { "resource": "provider-test8", "grade": 1, "paramIdx": 0, "count": 2, "durationInSec": 2, "burstCount": 0, "controlBehavior": 0, "limitApp": "default" } ] ``` + 系统规则:无resource,是对整个服务进程资源判断,qps模式慎用 ``` [ { // RT "avgRt": 1, // CPU 使用率 "highestCpuUsage": -1, // LOAD "highestSystemLoad": -1, // 线程数 "maxThread": -1, // 入口 QPS "qps": -1 } ] 示例: [ { "avgRt": -1, "highestCpuUsage": -1, "highestSystemLoad": -1, "maxThread": -1, "qps": 2 } ] ``` + 授权规则:依赖自定义解析来源信息组件:RequestOriginParser ``` [ { // 资源名 "resource": "sentinel_spring_web_context", // 流控应用 "limitApp": "appA,appB", // 授权类型(0代表白名单;1代表黑名单。) "strategy": 0 } ] 示例: [ { "resource": "provider-test10", "limitApp": "gateway", "strategy": 1 } ] ``` 4. 特别说明 + 资源名-【自定义名】:如果sentinel后台设置得资源名为@SentinelResource的value,则触发的是全局系统异常:{@link com.example.providerservice.advice.GlobalExceptionHandler} - a.当且仅当自定义名时,才能触发fallback、blockHandler + 资源名-【路由】:如果sentinel后台设置的资源名为路由[/test/hello],则触发的是全局Block异常:{@link com.example.providerservice.sentinel.GlobalBlockExceptionHandler} 5. 全局系统异常GlobalExceptionHandler、全局Block异常GlobalBlockExceptionHandler 和 fallback、blockHandler + 全局系统异常和全局Block异常,项目中建议都添加 - 使用场景:接口源端流控 - 优点:便于偷懒,可随时对未设置资源名的接口流控 + fallback、blockHandler,项目中按需添加 - 使用场景:客户端对接口源端调用流控 - 优点:对远端接口流控时,可自定义资源返回结果 - 异同点: + fallback:失败调用,若资源方法、接口出现未知异常,就会走fallback + blockHandler:sentinel定义的失败调用或者限制调用,如限流、熔断降级,就会走blockHandler + 关于[系统规则流控] - 不可指定资源名:默认触发全局Block异常 6. 集群限流:需要注意 > 集群限流只有特定场景需要使用 > 单机流控的好处:集群扩容时不用考虑从新设置流控参数 + Token Server自动管理(分配/选举) + Token Server高可用 7. 自定义全局降级方法:BlockExceptionHandler 源码实现已代替UrlBlockHandler 8. 引入sentinel所需依赖 ``` com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery com.alibaba.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.csp sentinel-datasource-nacos ``` 9. sentinel使用nacos配置中心 ``` sentinel: transport: dashboard: 127.0.0.1:8849 # sentinel datasource nacos :http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/ datasource: flow: nacos: server-addr: 127.0.0.1:8848 dataId: ${spring.application.name}-flow-rules groupId: SENTINEL_GROUP rule-type: flow degrade: nacos: server-addr: 127.0.0.1:8848 dataId: ${spring.application.name}-degrade-rules groupId: SENTINEL_GROUP rule-type: degrade system: nacos: server-addr: 127.0.0.1:8848 dataId: ${spring.application.name}-system-rules groupId: SENTINEL_GROUP rule-type: system authority: nacos: server-addr: 127.0.0.1:8848 dataId: ${spring.application.name}-authority-rules groupId: SENTINEL_GROUP rule-type: authority param-flow: nacos: server-addr: 127.0.0.1:8848 dataId: ${spring.application.name}-param-flow-rules groupId: SENTINEL_GROUP rule-type: param-flow ``` 10. 常见限流算法 + 令牌桶算法 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理; 根据限流大小,设置按照一定的速率往桶里添加令牌; 桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝; 请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除; 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流; + 漏桶算法 就是注水流水过程 以任务速率流入水,以固定速率流出水 弊端:无法应对短时间的突发流量 + 滑动窗口 - 计数器算法 计数器算法是限流算法里最简单也是最容易实现的一种算法。 比如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个。 那么我们可以这么做:在一开 始的时候,我们可以设置一个计数器counter,每当一个请求过来的时候,counter就加1,如果counter的值大于100并且该请求与第一个 请求的间隔时间还在1分钟之内,那么说明请求数过多; 如果该请求与第一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么就重置 counter - 滑动窗口算法 更加细粒度的计数器算法 ==》 就是滑动窗口算法 当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确 11. 限流配置线上最佳实践 + 网关:配置系统保护 + 网关:配置各服务流控(匀速排队) + 网关:配置各服务降级(熔断) + 服务:配置系统保护 + 服务:配置高频单接口流控(匀速排队) + 服务:配置高频单接口降级(熔断) + 实操: - 服务级别,配置[降级]-[慢调用比例] - RT: 平均响应时间的5、10倍 - 比例阈值:0.3 - 熔断时长:1s - 最小请求数:按实际情况 - 统计时长:10s - 一分钟触发一次就扩容 - 扩容借助,jenkins等调用脚本:修改pod弹性伸缩机器数量 - 缩容借助,k8s自带缩容机制 12. sentinel限流日志记录两种方式:sentinel-block.log > 如果要监控流控并告警,就直接监控sentinel-block.log文件 + 1.指定sentinel日志输出路径: classpath目录下配置文件 sentinel.properties ``` csp.sentinel.log.dir: /logs/csp csp.sentinel.log.oupput.type: console ``` + 2.默认在用户目录下 - windows: c:/user/xxx/logs - linux: root/logs - 可在jvm启动参数添加: ``` -Dcsp.sentinel.config.file=classpath:sentinel.properties,也可不添加 ``` ### provider接口 http://localhost:7073/provider/test/hello?str=kingtao&userId=100 ### consumer 1. openfeign调用provider + 开启sentinel支持 ``` feign: sentinel: enabled: true ``` + 可设置全局feign超时(会使ribbon超时、重试失效,不建议配置) ``` feign: client: config: default: connectionTimeout: 1000 readTimeout: 2000 ``` + 可设置全局ribbon超时 ``` ribbon: ReadTimeout: 1000 ConnectTimeout: 1000 ``` + feign、ribbon超时关系: - 没有设置feign超时,使用ribbon超时 - 设置了feign超时,使用feign超时 - 建议设置feign超时取代ribbon + feignclient ``` @FeignClient(value = "provider-service", fallback = ProviderClientFallBack.class) @SentinelResource: 对于feignClient资源名定义,不可定义, 直接使用:GET:http://[nacos-servicename]/[route] 例:GET:http://provider-service/provider/test/hello ``` ### sleuth + zipkin 1. 基本使用 + pom依赖 ``` org.springframework.cloud spring-cloud-starter-sleuth org.springframework.cloud spring-cloud-starter-zipkin ``` + yml配置 ``` zipkin: base-url: http://localhost:9411 sender: type: web # 可配置web、mq sleuth: sampler: probability: 1.0 ``` 2. 原理介绍[sleuth](sleuth.md) 3. zipkin + 本地启动、访问 ``` 统计数据保存到内存启动方式 java -Xms64m -Xmx256m -jar zipkin-server-2.23.9-exec.jar 统计数据保存到mysql启动方式 java -Xms64m -Xmx256m -jar zipkin-server-2.23.9-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=127.0.0.1 --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=123456 访问地址 http://localhost:9411/zipkin/ ``` + zipkin统计数据保存到mysql - 创建mysql相关表 ``` CREATE DATABASE zipkin; CREATE TABLE IF NOT EXISTS zipkin_spans ( `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit', `trace_id` BIGINT NOT NULL, `id` BIGINT NOT NULL, `name` VARCHAR(255) NOT NULL, `remote_service_name` VARCHAR(255), `parent_id` BIGINT, `debug` BIT(1), `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL', `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query', PRIMARY KEY (`trace_id_high`, `trace_id`, `id`) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds'; ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames'; ALTER TABLE zipkin_spans ADD INDEX(`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames'; ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range'; CREATE TABLE IF NOT EXISTS zipkin_annotations ( `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit', `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id', `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id', `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1', `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB', `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation', `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp', `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null', `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address', `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null', `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null' ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds'; ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames'; ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces and autocomplete values'; ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces and autocomplete values'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job'; CREATE TABLE IF NOT EXISTS zipkin_dependencies ( `day` DATE NOT NULL, `parent` VARCHAR(255) NOT NULL, `child` VARCHAR(255) NOT NULL, `call_count` BIGINT, `error_count` BIGINT, PRIMARY KEY (`day`, `parent`, `child`) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ```