# 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;
```