一、父工程
1、父工厂 pom.xml
<!--spring cloud alibaba 2.1.0.RELEASE-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
二、nacos
下载解压
修改startup.cmd MODE 由cluster改为单机版standalone 启动Nacos
2、访问nacos
默认账号密码 nacos/nacos
三、搭建payment模块
1、pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
applicatiom.yml
server:
port: 9000
spring:
application:
name: nacos-payment
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #配置Nacos地址
# 暴露要监控的
management:
endpoints:
web:
exposure:
include: '*'
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentNacosApp {
public static void main(String[] args) {
SpringApplication.run(PaymentNacosApp.class);
}
}
测试负载均衡策略
@RestController
@RequestMapping("payment")
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/nacos/{id}")
public String getPyment(@PathVariable("id") Integer id){
return "nacos register ,serverPort: "+serverPort+"\t id: "+id;
}
}
启动2个服务,做集群
四、搭建order模块
1、pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
applicatiom.yml
server:
port: 9100
spring:
application:
name: nacos-order
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 #配置Nacos地址
# 消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment
# 暴露要监控的
management:
endpoints:
web:
exposure:
include: '*'
@SpringBootApplication
@EnableDiscoveryClient
public class NacosOrderApp {
public static void main(String[] args) {
SpringApplication.run(NacosOrderApp.class);
}
@Bean
@LoadBalanced// resttemplate 结合ribbon做负载均衡的时候 要加入注解
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
2、负载均衡验证
@RestController
@RequestMapping("order")
public class OrderController {
@Resource
private RestTemplate restTemplate;
@Value("${service-url.nacos-user-service}")
private String serverUrl;
@GetMapping(value = "/paymeentInfo/{id}")
public String paymeentInfo(@PathVariable("id") Long id){
return restTemplate.getForObject(serverUrl+"/payment/nacos/"+id,String.class );
}
}
注册成功
验证:
nacos自带ribbon负载均衡策略
五、config
1、创建config模块
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--nacos-config-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- nacos-discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
2、创建bootstrap.yml
# nacos 配置
server:
port: 3377
spring:
application:
name: nacos-config
profiles:
active: dev #开发环境
cloud:
nacos:
discovery:
server-addr: localhost:8848 # Nacos服务注册中心地址
config:
server-addr: localhost:8848 # Nacos作为配置中心的地址
file-extension: yaml # 指定Yaml格式的配置 3377就可以去配置中心读指定后缀名是yaml的文件
# ${spring.application.name}-${spring.profile.active}.${Spring.cloud.nacos.config.file.extension}
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class ConfigServerApp {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApp.class,args);
}
}
2、测试拉取服务端配置文件信息
@RestController
@RefreshScope //刷新配置
public class ConfigController {
@Value("${lx.lqf}")
String info;
@GetMapping("test")
public String test(){
return info;
}
}
在nacos上创建配置文件
dataid=
${spring.application.name}-${spring.profile.active}.${Spring.cloud.nacos.config.file.extension}
验证:
六、nacos 命名空间、group 、Data Id
1、命名空间(可以区别不同的项目)
类似于java中的package,默认是(public),可以实现服务和配置隔离。
创建命名空间
创建配置进行验证
验证:
取namespaceID
2、group
默认是DEFAULT_GROUP,可以把不同的微服务划分到同一个组中。
修改group
3、DataId
更多是通过环境来进行区分,比如生产、测试、开发等。
七、 Nacos集群和持久化
1、执行sql脚本
2、修改startup.cmd
3、修改配置文件
注意:必须为真实ip,否则在客户端启动会报错
code:500 msg: java.net.SocketTimeoutException: Read timed out
4、修改nacos application.properties配置文件信息
4、复制2个nacos目录
修改 application.properties 端口
5、启动2个nacos服务
出现如下信息说明启动成功
6、修改nacos客户端的配置
验证:
8848
8849
八、nacos同步问题
九、nacos服务上下线、权重、元数据、触发保护阈值
1、nacos服务上下线
缺点:有延迟时间,服务实例依然存在,这个是由于ribbon缓存机制造成的,它维护本地一份服务列表信息,需要本地轮询,或心跳机制服务端推送来更新服务列表
解决:
order application,yml
#拉取nacos中最新服务实例的频率
ribbon:
ServerListRefreshInterval: 3000
eager-load:
enabled: true
nacos服务配置中 可看出
2、权重
nacos因为底层应用ribbon,ribbon本身是不支持权重的,因此需要扩展
nacos的权重配置范围是0-1,其中0服务不可用
server:
port: 9100
spring:
application:
name: nacos-order
cloud:
nacos:
discovery:
server-addr: 10.39.35.101:8848,10.39.35.101:8849 #配置Nacos地址
nacos-payment:
ribbon:
#拉取nacos中最新服务实例的频率
ServerListRefreshInterval: 3000
eager-load:
enabled: true
#引入自定义的负载均衡,注NFLoadBalancerRuleClassName 配置只有设置指定服务才生效
NFLoadBalancerRuleClassName: com.lx.cloud.nacos.config.WeightRuleConfig
# 消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment
# 暴露要监控的
management:
endpoints:
web:
exposure:
include: '*'
重写ribbon负载均衡策略
@Slf4j
public class WeightRuleConfig extends AbstractLoadBalancerRule {
@Autowired
NacosDiscoveryProperties properties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
//集群名称
String name = loadBalancer.getName();
NamingService namingService = properties.namingServiceInstance();
try {
//选取一个健康的实例
//有会支持分组的服务
Instance instance = namingService.selectOneHealthyInstance(name);
log.info("NacosWeightedRule.instance.url:{},port:{}",instance.getIp(),instance.getPort());
return new NacosServer(instance);
} catch (NacosException e) {
e.printStackTrace();
}
return null;
}
}
验证:
3、元数据
通过这样配置可以持久化元数据信息
spring:
application:
name: nacos-payment
cloud:
nacos:
discovery:
server-addr: 10.39.35.101:8848,10.39.35.101:8849 #配置Nacos地址
metadata:
#该实例在客户端上报心跳的间隔时间。(单位:毫秒) 默认5秒钟
preserved.heart.beat.interval: 1000
#该实例在不发送心跳后,从健康到不健康的时间。(单位:毫秒) 默认15秒
preserved.heart.beat.timeout: 3000
#该实例在不发送心跳后,被nacos下掉该实例的时间。(单位:毫秒)默认30秒
preserved.ip.delete.timeout: 3000
4、触发保护阈值
4.1.1 说明
保护阈值先要明确一个前提条件:对于 Nacos 的注册中心功能来说,Nacos 有一个天然的职责,是将服务消费者(Consumer)的请求转发给某个健康的服务提供者(Provider)。但在执行的流程中,可能会出现一种极端的情况,比如某个服务有 100 个实例,其中 99 个实例都宕机了,只剩下一个健康的实例,这个时候如果把所有的请求都转发到这一个健康实例上就会造成雪崩效应,最终导致业务系统崩溃。为了防止这种极端情况,于是就有了“保护阈值”,保护阈值一旦被触发,那么 Nacos 将会把请求转发给所有服务实例,也就是健康实例+非健康实例,这样可能会损失了⼀部分流量,但能保证集群中剩余的健康实例能正常工作。
保护阈值触发条件:(实际健康实例/总服务实例)小于等于设置的保护阈值
4.1.2 验证
模拟客户端服务不发送心跳
设置保护阈值 为0.5,表示当有我一半的机器掉线时(nacos服务接收不到心跳时,将触发保护阈值)
触发了保护阈值
此时进行测试,服务还是可以调用到9001服务,一直在卡死状态
除非等到客户端服务被剔除或客户端发送心跳才会消失
十、nacos鉴权
开启鉴权
重启服务端
然后 直接启动客户端,发现报错
java.lang.IllegalStateException: failed to req API:/nacos/v1/ns/instance after all servers([10.39.35.101:8848, 10.39.35.101:8849]) tried: failed to req API:10.39.35.101:8848/nacos/v1/ns/instance. code:403 msg: <html><body><h1>Whitelabel Error Page</h1><p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p><div id='created'>Tue Jun 07 17:01:30 CST 2022</div><div>There was an unexpected error (type=Forbidden, status=403).</div><div>unknown user!</div></body></html>
解决:
服务发现
spring:
application:
name: nacos-payment
cloud:
nacos:
discovery:
server-addr: 10.39.35.101:8848,10.39.35.101:8849 #配置Nacos地址
username: nacos
password: nacos
namespace: public
config配置也要修改
spring:
application:
name: nacos-config
profiles:
active: dev #开发环境
cloud:
nacos:
discovery:
server-addr: 10.39.35.101:8848 # Nacos服务注册中心地址
username: nacos
password: nacos
config:
server-addr: 10.39.35.101:8848 # Nacos作为配置中心的地址
file-extension: yaml # 指定Yaml格式的配置 3377就可以去配置中心读指定后缀名是yaml的文件
group: test001
namespace: 827278ad-6c3c-4aaa-85c8-82d91b075200
username: nacos
password: nacos
十一、nacos共享配置
在实际项目中,每个子模块中,spring有许多相同配置项,比如Redis,Mysql等等,这些公用配置信息,可以放在同一个配置文件中,方便管理及修改
spring:
application:
name: nacos-config
profiles:
active: dev #开发环境
cloud:
nacos:
discovery:
server-addr: 10.39.35.101:8848 # Nacos服务注册中心地址
username: nacos
password: nacos
config:
server-addr: 10.39.35.101:8848 # Nacos作为配置中心的地址
file-extension: yaml # 指定Yaml格式的配置 3377就可以去配置中心读指定后缀名是yaml的文件
group: test001
namespace: 827278ad-6c3c-4aaa-85c8-82d91b075200
username: nacos
password: nacos
#支持多个共享 Data Id 的配置,优先级小于extension-configs,自定义 Data Id 配置 属性是个集合
#shared-configs优先级,[0] <[1]
shared-configs[0]:
data-id: commom.yaml # 配置文件名-Data Id
group: DEFAULT_GROUP # 默认为DEFAULT_GROUP
refresh: true # 是否动态刷新,默认为false
extension-configs[0]:
data-id: commom2.yaml # 配置文件名-Data Id
group: DEFAULT_GROUP # 默认为DEFAULT_GROUP
refresh: true # 是否动态刷新,默认为false
十二、nacos配置文件说明application.properties
#
# Copyright 1999-2018 Alibaba Group Holding Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#*************** Spring Boot Related Configurations ***************#
### Default web context path:
# 访问路径
server.servlet.contextPath=/nacos
### Default web server port:
# 服务端口
server.port=8848
#*************** Network Related Configurations ***************#
### If prefer hostname over ip for Nacos server addresses in cluster.conf:
# 从cluster.conf配置文件中获取IP(集群模式)
# nacos.inetutils.prefer-hostname-over-ip=false
### Specify local server's IP:
# 设置nacos的ip
# nacos.inetutils.ip-address=
#*************** Config Module Related Configurations ***************#
### If user MySQL as datasource:
# 数据库类型
# spring.datasource.platform=mysql
### Count of DB:
# 数据库数量
# db.num=1
### Connect URL of DB:
# 数据库连接URL
# db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
# 数据库用户
# db.user=nacos
# 数据库密码
# db.password=nacos
#*************** Naming Module Related Configurations ***************#
### Data dispatch task execution period in milliseconds:
# 同步任务生成的周期,单位为毫秒
# nacos.naming.distro.taskDispatchPeriod=200
### Data count of batch sync task:
# 同步任务每批的key的数目
# nacos.naming.distro.batchSyncKeyCount=1000
### Retry delay in milliseconds if sync task failed:
# 同步任务失败的重试间隔,单位为毫秒
# nacos.naming.distro.syncRetryDelay=5000
### If enable data warmup. If set to false, the server would accept request without local data preparation:
# 是否在Server启动时进行数据预热
# nacos.naming.data.warmup=true
### If enable the instance auto expiration, kind like of health check of instance:
# 是否自动摘除临时实例
# nacos.naming.expireInstance=true
#是否自动清理不在线服务
nacos.naming.empty-service.auto-clean=true
# 清理延迟时间
nacos.naming.empty-service.clean.initial-delay-ms=50000
# 清理间隔时间
nacos.naming.empty-service.clean.period-time-ms=30000
#*************** CMDB Module Related Configurations ***************#
### The interval to dump external CMDB in seconds:
# 全量dump的间隔,单位为秒
# nacos.cmdb.dumpTaskInterval=3600
### The interval of polling data change event in seconds:
# 变更事件的拉取间隔,单位为秒
# nacos.cmdb.eventTaskInterval=10
### The interval of loading labels in seconds:
# 标签集合的拉取间隔,单位为秒
# nacos.cmdb.labelTaskInterval=300
### If turn on data loading task:
# 是否打开CMDB
# nacos.cmdb.loadDataAtStart=false
#*************** Metrics Related Configurations ***************#
### Metrics for prometheus
# 监控端点
#management.endpoints.web.exposure.include=*
### Metrics for elastic search
# 是否导出监控数据到ES
management.metrics.export.elastic.enabled=false
# ES地址
#management.metrics.export.elastic.host=http://localhost:9200
### Metrics for influx
# 是否导出监控数据到influxdb(一款时序数据库)
management.metrics.export.influx.enabled=false
# 数据库名
#management.metrics.export.influx.db=springboot
# 数据库地址
#management.metrics.export.influx.uri=http://localhost:8086
# 是否自动创建数据库
#management.metrics.export.influx.auto-create-db=true
# 为每个点编写一致性
#management.metrics.export.influx.consistency=one
# 是否启用发布到Influx的指标批次的GZIP压缩
#management.metrics.export.influx.compressed=true
#*************** Access Log Related Configurations ***************#
### If turn on the access log:
# 是否打印access日志
server.tomcat.accesslog.enabled=true
### The access log pattern:
# 日志打印格式
server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i
### The directory of access log:
# 日志存储目录
server.tomcat.basedir=
#*************** Access Control Related Configurations ***************#
### If enable spring security, this option is deprecated in 1.2.0:
# 开启security框架访问控制
#spring.security.enabled=false
### The ignore urls of auth, is deprecated in 1.2.0:
# 配置security放行路径
nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**
### The auth system to use, currently only 'nacos' is supported:
# 系统授权认证类型
nacos.core.auth.system.type=nacos
### If turn on auth system:
# 是否开启授权
nacos.core.auth.enabled=false
### The token expiration in seconds:
# 令牌失效时间
nacos.core.auth.default.token.expire.seconds=18000
### The default token:
# 默认访问密钥
nacos.core.auth.default.token.secret.key=SecretKey012345678901234567890123456789012345678901234567890123456789
### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay.
# 更新授权信息后的延迟时间
nacos.core.auth.caching.enabled=true
#*************** Istio Related Configurations ***************#
### If turn on the MCP server:
# 是否开启MCP
nacos.istio.mcp.server.enabled=false
###*************** Add from 1.3.0 ***************###
#*************** Core Related Configurations ***************#
### set the WorkerID manually
# 数据的主键雪花ID
# nacos.core.snowflake.worker-id=
### Member-MetaData
# nacos.core.member.meta.site=
# nacos.core.member.meta.adweight=
# nacos.core.member.meta.weight=
### MemberLookup
### Addressing pattern category, If set, the priority is highest
# 寻址模式类型
# nacos.core.member.lookup.type=[file,address-server,discovery]
## Set the cluster list with a configuration file or command-line argument
# 使用配置文件或命令行参数设置群集列表
# nacos.member.list=192.168.16.101:8847?raft_port=8807,192.168.16.101?raft_port=8808,192.168.16.101:8849?raft_port=8809
## for DiscoveryMemberLookup
# If you want to use cluster node self-discovery, turn this parameter on
# 自动寻址
# nacos.member.discovery=false
## for AddressServerMemberLookup
# Maximum number of retries to query the address server upon initialization
# 初始化时查询地址服务器的最大重试次数
# nacos.core.address-server.retry=5
#*************** JRaft Related Configurations ***************#
### Sets the Raft cluster election timeout, default value is 5 second
# 选举超时时间
# nacos.core.protocol.raft.data.election_timeout_ms=5000
### Sets the amount of time the Raft snapshot will execute periodically, default is 30 minute
# 集群以中性数据快照间隔时间
# nacos.core.protocol.raft.data.snapshot_interval_secs=30
### Requested retries, default value is 1
# 请求失败尝试次数
# nacos.core.protocol.raft.data.request_failoverRetries=1
### raft internal worker threads
# 线程个数
# nacos.core.protocol.raft.data.core_thread_num=8
### Number of threads required for raft business request processing
# 客户端线程请求数
# nacos.core.protocol.raft.data.cli_service_thread_num=4
### raft linear read strategy, defaults to index
# 一致性线性读策略
# nacos.core.protocol.raft.data.read_index_type=ReadOnlySafe
### rpc request timeout, default 5 seconds
# RPC 请求超时时间
# nacos.core.protocol.raft.data.rpc_request_timeout_ms=5000
十三、nacos config配置说明
spring:
application:
name: nacos-config
profiles:
active: dev #开发环境
cloud:
nacos:
discovery:
server-addr: 10.39.35.101:8848 # Nacos服务注册中心地址
username: nacos
password: nacos
config:
server-addr: 10.39.35.101:8848 # Nacos作为配置中心的地址
file-extension: yaml # 指定Yaml格式的配置 3377就可以去配置中心读指定后缀名是yaml的文件
group: test001
namespace: 827278ad-6c3c-4aaa-85c8-82d91b075200
username: nacos
password: nacos
#支持多个共享 Data Id 的配置,优先级小于extension-configs,自定义 Data Id 配置 属性是个集合
#shared-configs优先级,[0] <[1]
shared-configs[0]:
data-id: commom.yaml # 配置文件名-Data Id
group: DEFAULT_GROUP # 默认为DEFAULT_GROUP
refresh: true # 是否动态刷新,默认为false
extension-configs[0]:
data-id: commom2.yaml # 配置文件名-Data Id
group: DEFAULT_GROUP # 默认为DEFAULT_GROUP
refresh: true # 是否动态刷新,默认为false
# 当要上阿里云时,阿里云上面的一个云账号名
access-key: accessKey
# 当要上阿里云时,阿里云上面的一个云账号密码
secret-key: secretKey
# Nacos Server 对外暴露的 context path
context-path: nacos
# 文件名前缀 默认为 ${spring.appliction.name}
prefix: prefix
# 客户端获取配置的超时时间(毫秒) 默认3000
timeout: 5000
# 配置成Nacos集群名称
#cluster-name: clusterName
# 长轮询的重试次数 默认3
max-retry: 5
# 长轮询任务重试时间,单位为毫秒
config-retry-time: 1000
# 长轮询的超时时间,单位为毫秒
config-long-poll-timeout: 1000
# 监听器首次添加时拉取远端配置 默认false
enable-remote-sync-config: true
# 地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址
#endpoint: localhost
# 是否开启监听和自动刷新
refresh-enabled: true
13.2 服务端api(context-path)
客户端
spring.cloud.nacos.config.context-path=nacos
服务端
13.3 prefix
spring.cloud.nacos.config.prefix=prefix
13.4 长轮询重试
# 客户端获取配置的超时时间(毫秒) 默认3000
timeout: 5000
# 长轮询的重试次数 默认3
max-retry: 5
# 长轮询任务重试时间,单位为毫秒
config-retry-time: 1000
# 长轮询的超时时间,单位为毫秒
config-long-poll-timeout: 1000
当 Client 向 Nacos Config 服务端发起一个配置查询请求时,服务端并不会立即返回查询结果,而是会将这个请求 hold 一段时间。
如果在这段时间内有配置项数据的变更,那么服务端会触发变更事件,客户端将会监听到该事件,并获取相关配置变更;
如果这段时间内没有发生数据变更,那么在这段“hold 时间”结束后,服务端将释放请求。采用长轮询机制可以降低多次请求带来的网络开销,并降低更新配置项的延迟。
13.5 enable-remote-sync-config: true 不建议开启(todo)
监听器首次添加时拉取远端配置 默认false
根据源码得知首次启动,配置信息就会放入缓存中,然后再和配置信息做绑定。
13.6 cluster-name tod
13.7 endpoint todo
# 地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址
十四、nacos discovery配置
14.1 配置
spring:
application:
name: nacos-config
profiles:
active: dev #开发环境
cloud:
nacos:
discovery:
server-addr: 10.39.35.101:8848 # Nacos服务注册中心地址
username: nacos
password: nacos
# 连接Nacos Server指定的连接点
# endpoint: localhost
# 设置注册时本服务IP地址
# ip: 127.0.0.1
# nacos客户端向服务端发送心跳的时间间隔,单位s
#heart-beat-interval: 5
# 心跳超时时间,单位s
#heart-beat-timeout: 15
# 服务超时时,多少秒后删除
#ip-delete-timeout: 30
# 集群名称
#cluster-name: DEFAULT
# 是否注册服务,默认为true
#register-enabled: false
# 当要上阿里云时,阿里云上面的一个云账号名
#access-key:
# 当要上阿里云时,阿里云上面的一个云账号密码
#secret-key:
# nacos客户端日志名,默认naming.log:
#log-name:
# 服务元数据标签
#metadata:
# 负载均衡权重,默认1,取值范围 0 到 1,数值越大,权重越大
# weight: 1
# 监视延迟,从nacos服务器拉取新服务的持续时间,单位ms
watch-delay: 30000
# 注册服务时的服务名,默认${spring.application.name}
#service: abc
# 服务是否是https
#secure: false
# 注册时本服务的端口,无需设置,自动探测
port: 8088
# 选择固定网卡
#network-interface: eth0
# 是否从本地缓存中,默认false
#naming-load-cache-at-start: false
14.2 ip
ip: 127.0.0.1
控制台查看注册ip从10.39.35.101变为了 127.0.0.1
14.3 是否注册服务
register-enabled: false
14.4 心跳日志
# nacos客户端向服务端发送心跳的时间间隔,单位s
heart-beat-interval: 5
# 心跳超时时间,单位s
heart-beat-timeout: 15
# 服务超时时,多少秒后删除
ip-delete-timeout: 30
14.5 metadata
metadata:
a: b
14.6 注册服务名称
# 注册服务时的服务名,默认${spring.application.name}
service: abc
14.7 注册服务的端口
# 注册时本服务的端口,无需设置,自动探测
port: 8088
14.8 监视延迟 todo
# 监视延迟,从nacos服务器拉取新服务的持续时间,单位ms
watch-delay: 30000
默认为30s。默认为true,客户端在启动时会创建一个线程池,该线程定期去查询服务端的信息列表,该请求不会立刻返回,默认等待30s,若在30s内,服务端信息列表发生变化,则该请求立刻返回,通知客户端拉取服务端的服务信息列表,若30s内,没有变化,则30s时该请求返回响应,客户端服务列表不变,再次发生该请求。
注:推荐该值为30s即可,无需修改
14.9 启动时是否优先读取本地缓存
naming-load-cache-at-start: false
15.0 nacos整合openfeign
15.0.1 启动类
@EnableFeignClients
public class NacosOrderApp
15.0.2 创建feignclient
@FeignClient(value = "nacos-payment",fallback = PaymentFallBackService.class)
public interface PaymentService {
@GetMapping("/payment/test/{id}")
public String payment(@PathVariable("id") Long id);
}
@Component
public class PaymentFallBackService implements PaymentService{
@Override
public String payment(Long id) {
return "服务降级返回,----PaymentFallBackService";
}
}
15.0.3 order controller
@Autowired
PaymentService paymentService;
@GetMapping("/payment")
public String payment(Long id) {
return paymentService.payment(id);
}
15.0.4 payment controller
@GetMapping("/test/{id}")
public String payment(@PathVariable("id") Long id){
return id.toString()+"我是payment";
}
15.0.5 验证超时
# 激活sentinel对feign支持
feign:
client:
config:
nacos-payment:
# 建立链接的超时时长
connectTimeout: 5000
# 读取超时时长
readTimeout: 3000
sentinel:
enabled: true
#拉取nacos中最新服务实例的频率
nacos-payment:
ribbon:
ServerListRefreshInterval: 3000
ReadTimeout: 4000
ConnectTimeout: 5000
eager-load:
enabled: true
#引入自定义的负载均衡
NFLoadBalancerRuleClassName: com.lx.cloud.nacos.config.WeightRuleConfig
如果配置feign超时,则以feign超时时间为准。
15.0.6 超时重试
# 激活sentinel对feign支持
feign:
client:
config:
nacos-payment:
# 建立链接的超时时长
connectTimeout: 5000
# 读取超时时长
readTimeout: 3000
sentinel:
enabled: true
#拉取nacos中最新服务实例的频率
nacos-payment:
ribbon:
ServerListRefreshInterval: 3000
ReadTimeout: 4000
ConnectTimeout: 5000
eager-load:
enabled: true
#引入自定义的负载均衡
NFLoadBalancerRuleClassName: com.lx.cloud.nacos.config.WeightRuleConfig
# 同一实例最大重试次数,不包括首次调用
MaxAutoRetries: 2
# 重试其他实例的最大重试次数,不包括首次所选的server
MaxAutoRetriesNextServer: 3
# 是否所有操作都进行重试
#当OkToRetryOnAllOperations设置为false时,只会对get请求进行重试。
#如果设置为true,便会对所有的请求进行重试,如果是put或post等写操作,
#如果服务器接口没做幂等性,会产生不好的结果,所以OkToRetryOnAllOperations慎用。
OkToRetryOnAllOperations: false
#ribbon 重试计算MaxAutoRetries+MaxAutoRetriesNextServer+(MaxAutoRetries *MaxAutoRetriesNextServer) ,即重试11次 (不包括首次调用)一共产生12次调用。
验证通过 和之前eureka 整合openfeign一样
15.0.7 其他、openfeign配置 如压缩httpclient 等不一一写 和之前博客写的相同
十七、Sentinel
Sentinel以“流”为切入点,并在流控制, 流量整形,电路中断和系统自适应保护等多个领域工作,以确保微服务的可靠性和弹性。
17.1 下载sentinel
下载jar包,下载地址
启动sentinel
访问sentinel
用户密码 sentinel/sentinel
17.2 客户端引入
order模块加入sentinel
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
order application.yml
server:
port: 9100
spring:
application:
name: nacos-order
cloud:
nacos:
discovery:
server-addr: 10.39.35.101:8848,10.39.35.101:8849 #配置Nacos地址
username: nacos
password: nacos
sentinel:
transport:
# sentinel监控服务地址
dashboard: localhost:8080
# 默认8719端口,加入被占用自动+1寻找未被占用的端口
port: 8719
#拉取nacos中最新服务实例的频率
nacos-payment:
ribbon:
ServerListRefreshInterval: 3000
eager-load:
enabled: true
#引入自定义的负载均衡
NFLoadBalancerRuleClassName: com.lx.cloud.nacos.config.WeightRuleConfig
# 消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment
# 暴露要监控的
management:
endpoints:
web:
exposure:
include: '*'
order controller加入
@GetMapping("/testA")
public String testA() {
return "------------------testA";
}
@GetMapping("/testB")
public String testB() {
return "------------------testB";
}
启动order 访问
在sentinel刷新控制台发现
17.3 流控规则
17.3.1 添加qps流控规则
说明:超过阈值(当前配置一秒一次) 就会失败
验证:快速请求此接口,发现已经限流
17.3.2 添加线程数流控规则
说明并发访问接口的线程数如果大于1,则限流。
测试案例:修改流控规则,阈值类型为线程数,单机阈值为1,后台接口设置当前线程睡眠3秒钟。此时,访问快速刷新接口,发现已被限流。
@GetMapping("/testA")
public String testA() throws InterruptedException {
Thread.sleep(3000);
return "------------------testA";
}
postman开启2个tab页面进行访问
第一个tab页面,在等待
此时快速访问第二个tab页的接口,发现已经被限流
17.3.3 流控模式之关联
当指定接口关联的接口达到限流条件时,将对指定接口开启限流。
此方式适合做应用让步,比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的。
说明:当访问/order/testB 的线程数达到1时,则开启/order/testA的限流,但是对 /order/testB并无影响。
修改order controller
@GetMapping("/testA")
public String testA() throws InterruptedException
return "------------------testA";
}
@GetMapping("/testB")
public String testB() throws InterruptedException
Thread.sleep(10000);
return "------------------testB";
}
测试
发现testA被限流
17.3.4 流控模式之链路
链路流控模式: 针对资源链路上的接口进行限流,例如:testA、testB两个接口都调用某一资源test,testA->test、testB->test可以看成两个简单的链路,此时可以针对test配置链路限流,比如限制testA调用test,而testB调用test则不受影响,它的功能有点类似于针对 来源配置项,而链路流控是针对上级接口,它的粒度 更细。
说明:两个测试接口,调用同一service资源,对其中一个调用链路进行流控
sentinel:
transport:
# sentinel监控服务地址
dashboard: localhost:8080
# 默认8719端口,加入被占用自动+1寻找未被占用的端口
port: 8719
# 控制是否收敛context;将其配置为 false 即可根据不同的URL 进行链路限流
web-context-unify: false
配置
web-context-unify:false可能不生效,版本问题
解决:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
<version>1.7.1</version>
</dependency>
package com.lx.cloud.nacos.config;
import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterContextConfig {
/**
* @NOTE 在spring-cloud-alibaba v2.1.1.RELEASE及前,sentinel1.7.0及后,关闭URL PATH聚合需要通过该方式,
* spring-cloud-alibaba v2.1.1.RELEASE后,可以通过配置关闭:spring.cloud.sentinel.web-context-unify=false
* 手动注入Sentinel的过滤器,关闭Sentinel注入CommonFilter实例,修改配置文件中的 spring.cloud.sentinel.filter.enabled=false
* 入口资源聚合问题:https://github.com/alibaba/Sentinel/issues/1024 或 https://github.com/alibaba/Sentinel/issues/1213
* 入口资源聚合问题解决:https://github.com/alibaba/Sentinel/pull/1111
*/
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/*");
// 入口资源关闭聚合
registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration;
}
}
order controller
@Autowired
OrderService orderService;
@GetMapping("/testA")
public String testA() throws InterruptedException {
orderService.test();
return "------------------testA";
}
@GetMapping("/testB")
public String testB() throws InterruptedException {
Thread.sleep(10000);
orderService.test();
return "------------------testB";
}
order service
@Service
public class OrderService {
@SentinelResource(value = "service")
public void test(){
Thread.sleep(5000);
System.out.println("==============");
}
}
注意:必须把Thread.sleep 放在order service,放在testBcontroller不会生效,猜测因为是对service流控。
验证:
No.1 tab 访问testB
No.2 tab 访问testB
No.3 tab 访问testA
不受影响
17.3.5 快速失败
默认配置项, 直接失败,抛出异常,不做任何额外的处理,是最简单的效果。
17.3.5 Warm Up
预热/冷启动:当流量突然增大的时候,我们常常会希望系统从空闲状态到繁忙状态的切换的时间长一些。即如果系统在此之前长期处于空闲的状态,我们希望处理请求的数量是缓步的增多,经过预期的时间以后,到达系统处理请求个数的最大值。Warm Up(冷启动,预热)模式就是为了实现这个目的的。
这个场景主要用于启动需要额外开销的场景,例如建立数据库连接等。
根据codeFactor(冷加载因子,默认为3)的值,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值 。
说明:对service资源进行链路线路,设置阈值为3。则刚访问此接口时,实际限流阈值为3/3,即为1,也就是刚开始的时候阈值只有1,当经过10s后,阈值才慢慢提高到3。
去掉 Thread.sleep()
进行压测,得结果
17.3.6 排队等待
让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的排队等待; 它还会让设置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
说明:对service资源进行链路线路,设置阈值为3。设置流控效果为排队等候,每秒3次请求时,再有请求就排队等候,等待超时时间为10000ms, 超时过后,请求将被踢出排队队列,返回限流异常。
17.4 降级规则
17.4.1 RT
版本不同显示不同
说明:一秒钟打进来10个线程(大于5个了)调用testB,我们希望200毫秒处理完本次任务, 如果超过200毫秒还没处理完,在未来10秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了。后续我停止jmeter,没有这么大的访问量了,断路器关闭(保险丝恢复),微服务恢复OK
@GetMapping("/testB")
public String testB() throws InterruptedException {
Thread.sleep(300);
orderService.test();
return "------------------testB";
}
注意:
Thread.sleep(300); 设置值大小很有意义:
设置过大会不生效,因为服务器要计算1秒内的请求是否超过200ms了,如果设置成3秒,只能在3秒后返回结果才能计算是否降级
jmeter压测,只所以设置为2,是因为线程一秒钟10个有的可能已经执行完todo
请求发现被降级
10秒后发现正常
新版本 RT
熔断策略之慢调用比例
选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
最大 RT:慢调用临界 RT,超出该值计为慢调用。单位毫秒
比例阈值:RT模式下慢速请求比率的阈值。默认1.0d
熔断时长:断路器打开时的恢复超时(以秒为单位)。超时后,断路器将转换为半开状态以尝试一些请求。。单位秒,图中表示触发熔断后,接下来的10秒内请求会自动被熔断,经过10S后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
最小请求数:可以触发熔断中断的最小请求数(在有效的统计时间范围内)。默认值为5
StatIntervalMs: 统计时长(单位为 ms),默认1000(1秒)
说明:1秒内,发送最少5个请求,我们希望5000ms内能处理完任务,如果超过5000毫秒还没处理完,在未来10秒钟的时间窗口内,断路器打开(保险丝跳闸)微服务不可用,保险丝跳闸断电了。
验证:
@GetMapping("/testA")
public String testA() throws InterruptedException {
Thread.sleep(6000);
orderService.test();
return "------------------testA";
}
17.4.2 异常比例
一秒钟打进来10个线程(大于5个了)调用testB, 且 异常比例超过阈值0.5,触发降级,打开断路器,等时间窗口结束 15(秒),再关闭降级
异常比率的阈值范围是 [0.0 至 1.0]
,代表 0% - 100%
@GetMapping("/testB")
public String testB() throws InterruptedException {
int i = 1/ 0;
orderService.test();
return "------------------testB";
}
模拟一秒钟10个线程
新版本 todo
当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
17.4.3 异常数
异常数是按分钟来统计的,所以时间窗口必须大于等于60s
todo异常数阈值:经过测试,阈值设置为16,实际异常数大于(16/2)8则会降级。比如5 实际异常数大于(5/2)2则会降级。而阈值为3时还是超过2生效,原因未知...
一秒钟打进来10个线程(大于5个了)调用testB, 且 异常数量超过阈值10,触发降级,打开断路器,等时间窗口结束 65(秒),再关闭降级
若 timeWindow
小于 60s,则结束熔断状态后仍可能再进入熔断状态。
17.5 热点规则
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
说明:对资源testC进行热点限流, 10秒钟内除了第一个参数值为123的请求达到3次后被限流,10秒后恢复正常。其他的请求阈值都是100。
@GetMapping("/testC")
@SentinelResource(value = "testC")
public String testC(String id) throws InterruptedException {
System.out.println("id=="+id);
return "------------------testC";
}
发现接口被限流
而另一个接口正常
17.6 授权规则
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
说明:当lisi应用访问 /order/testD时,被限流。
ps:当前 流控应用 放在了请求参数里面,可以放到的地方有很多,比如 参数/请求头/session/等等
resource:资源名,即限流规则的作用对象。
limitApp:对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB。
strategy:限制模式,AUTHORITY_WHITE 为白名单模式,AUTHORITY_BLACK 为黑名单模式,默认为白名单模式
import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* 实现RequestOriginParser 来获取来源
*/
public class SentinelRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
String serviceName = httpServletRequest.getParameter("serviceName");
StringBuffer url = httpServletRequest.getRequestURL();
if (url.toString().endsWith("favicon.ico")) {
// 浏览器会向后台请求favicon.ico图标
return serviceName;
}
if (!url.toString().endsWith("/testD")) {
// 浏览器会向后台请求favicon.ico图标
return serviceName;
}
if (StringUtils.isEmpty(serviceName)) {
throw new IllegalArgumentException("serviceName must not be null");
}
return serviceName;
}
}
注意这里面有个大坑!!!
RequestOriginParser必须为com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser,否则不生效!!!
下面代码必须加,否则不生效
@PostConstruct
public void init(){
WebCallbackManager.setRequestOriginParser(new SentinelRequestOriginParser());
}
@GetMapping("/testD")
public String testD(String serviceName) {
System.out.println("serviceName"+serviceName);
return "------------------testD";
}
测试:
17.7 系统规则
系统自适应限流,Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
支持的模式:
Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5。
CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
验证:
17.8 Sentinel之@SentinelResource注解
17.8.1 blockHandler
使用blockHandler指定发生BlockException时进入的方法,方法需满足以下条件
- 必须是 public 修饰
- 返回类型、参数与原方法一致,并在最后加BlockException 类型的参数
- 默认需和原方法在同一个类中
配置流控规则
@GetMapping("/testE")
@SentinelResource(value = "abc",blockHandler = "myblock")
public String testE() {
return "testE";
}
public String myblock(BlockException blockException) {
return "被限流了";
}
验证结果
17.8.2 blockHandlerblockHandlerClass
将限流和降级方法外置到单独的类中,需满足以下条件:
- 方法必须 使用public static 修饰
- 满足blockHandler处理条件
@GetMapping("/testE")
@SentinelResource(value = "abc",blockHandler = "myblock",blockHandlerClass = OrderBlockHandler.class)
public String testE() {
return "testE";
}
public class OrderBlockHandler {
public static String myblock(BlockException blockException){
return "被限流了哦!!!by orderBlockHandler";
}
}
17.8.3 fallback
用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理,满足条件同blockHandler大致一致(BlockException替换为Throwable)。
不需要配置规则
@GetMapping("/testE")
@SentinelResource(value = "abc",fallback = "myfallback")
public String testE() {
int a = 1/0;
return "testE";
}
public String myfallback(Throwable throwable) {
return throwable.getMessage();
}
17.8.4 fallbackClass
@GetMapping("/testE")
@SentinelResource(value = "abc",fallback = "myfallback",fallbackClass = OrderFallbackHandler.class)
public String testE() {
int a = 1/0;
return "testE";
}
public class OrderFallbackHandler {
public static String myfallback(Throwable throwable){
return "orderFallbackHandler"+throwable.getMessage();
}
}
验证:
17.8.5 defaultFallback
用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。
@GetMapping("/testE")
@SentinelResource(value = "abc",defaultFallback = "myfallback")
public String testE() {
int a = 1/0;
return "testE";
}
public String myfallback(Throwable throwable) {
return throwable.getMessage();
}
17.8.6 exceptionsToIgnore
指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。
@GetMapping("/testE")
@SentinelResource(value = "abc",defaultFallback = "myfallback",exceptionsToIgnore = ArithmeticException.class)
public String testE() {
int a = 1/0;
return "testE";
}
public String myfallback(Throwable throwable) {
return throwable.getMessage();
}
验证:已经抛出原有异常。
17.9 扩展篇:发现个问题
使用默认的接口作为资源限流时,返回结果如下
Blocked by Sentinel (flow limiting)
添加SentinelResource时
17.10 Sentinel使用 Nacos 配置规则
17.10.1 描述
在实际使用过程中,设置规则之后,保存在内存中,应用重启后,规则失效,而目前sentinel并没有直接提供持久化保存规则的功能,需要自己实现。
Sentinel 提供两种方式修改规则:
- 通过 API 直接修改 (loadRules)
- 通过 DataSource 适配不同数据源修改
api:
FlowRuleManager.loadRules(List<FlowRule> rules); // 修改流控规则
DegradeRuleManager.loadRules(List<DegradeRule> rules); // 修改降级规则
datasource扩展
DataSource 扩展常见的实现方式有:
拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件,甚至是 VCS 等。这样做的方式是简单,缺点是无法及时获取变更;
推模式:规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。
Sentinel 目前支持以下数据源扩展:
- Pull-based: 动态文件数据源、Consul, Eureka
- Push-based: ZooKeeper, Redis, Nacos, Apollo, etcd
推模式:使用 Nacos 配置规则:
Nacos 是阿里中间件团队开源的服务发现和动态配置中心。Sentinel 针对 Nacos 作了适配,底层可以采用 Nacos 作为规则配置数据源。
17.10.2 order模块中加入依赖
<!--持久化规则-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--sentinel datasource nacos 持久化会用到-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
注意:这里面为什么排除到原有的 <artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>,因为在spring-cloud-alibaba-sentinel-datasource2.2.1版本中不支持权限认证:
spring.cloud.sentinel.datasource.flow.nacos.usernmae
spring.cloud.sentinel.datasource.flow.nacos.password
会导致nacos服务端开启了权限认证,而sentinel整合nacos持久化不生效。
17.10.3 nacos中添加流控规则
sentinel-demo-sentinel文件流控规则和降级规则:
[
{
"resource": "/**",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
sentinel-demo-sentinel-degrade文件降级规则:
[
{
"count":500,
"grade":0,
"limitApp":"default",
"minRequestAmount":5,
"resource":"/test",
"slowRatioThreshold":1,
"statIntervalMs":1000,
"timeWindow":100
}
]
application.yml
spring:
application:
name: nacos-order
cloud:
nacos:
discovery:
server-addr: 10.39.35.101:8848 #配置Nacos地址
username: nacos
password: nacos
sentinel:
transport:
# sentinel监控服务地址
dashboard: localhost:8080
# 默认8719端口,加入被占用自动+1寻找未被占用的端口
port: 8719
# 控制是否收敛context;将其配置为 false 即可根据不同的URL 进行链路限流
web-context-unify: false
datasource:
flow:
nacos:
# nacos地址
server-addr: ${spring.cloud.nacos.discovery.server-addr}
# nacos中配置文件的data-id
data-id: ${spring.application.name}-sentinel
# nacos 分组
group-id: DEFAULT_GROUP
# 规则类型 流控
rule-type: flow
data-type: json
username: nacos
password: nacos
degrade:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
data-id: ${spring.application.name}-sentinel-degrade
group-id: DEFAULT_GROUP
#规则类型 降级
rule-type: degrade
data-type: json
username: nacos
password: nacos
规则类型:
public enum RuleType {
FLOW("flow", FlowRule.class),
DEGRADE("degrade", DegradeRule.class),
PARAM_FLOW("param-flow", ParamFlowRule.class),
SYSTEM("system", SystemRule.class),
AUTHORITY("authority", AuthorityRule.class),
GW_FLOW("gw-flow", "com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"),
GW_API_GROUP("gw-api-group", "com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");
}
sentinel控制台查看已经生效:
修改nacos配置,sentinel规则自动刷新
验证是否生效
流程:
17.11 sentinel持久化欠缺问题
基于以上方案,可以实现在Nacos中持久化规则,但是实际开发,我们更需要的是在控制台中直接添加规则,然后持久化到Nacos中,实现双向同步。
todo
17.13 sentinel监控
17.13.1 簇点监控
获取簇点列表 API: GET /clusterNode
http://localhost:8719/clusterNode
结果字段说明:
[
{"averageRt":0.0, //平均响应时间
"blockRequest":0, //每分钟拦截的请求个数
"blockedQps":0.0, //每秒拦截个数
"curThreadNum":0, //并发个数
"passQps":1.0, // 每秒成功通过请求
"passReqQps":1.0, //每秒到来的请求
"resourceName":"/registry/machine", 资源名称
"timeStamp":1529905824134, //时间戳
"totalQps":1.0, // 每分钟请求数
"totalRequest":193},
....
]
17.13.2 查询某个簇点的详细信息
可以用下面命令模糊查询该簇点的具体信息,其中 id 对应 resource name,支持模糊查询:
对应order application.yml中的
spring:
application:
name: nacos-order
17.13.3 簇点调用者统计信息
可以用下列命令查询该簇点的调用者统计信息:
17.13.4 链路监控
我们可以通过以下api来查询链路入口的链路树形结构:
http://localhost:8719/tree
17.13.5 历史资源数据
资源的秒级日志
所有资源的秒级监控日志(metric 日志)在 h o m e / l o g s / c s p / {home}/logs/csp/home/logs/csp/{appName}-metrics.log.${date}.xx。
nacos-order-metrics.log.2022-06-09.21
日志内容:
内容解析:
1654758807000|2022-06-09 15:13:27|/order/testA|4|0|4|0|4|0|0|1
1654758807000:时间戳
2022-06-09 15:13:27 日期
/order/testA 资源名称
4 每秒通过的资源请求个数 (pass QPS)
0 每秒资源被拦截的个数 (block QPS)
4 每秒完成调用的资源个数 (complete QPS),包括正常结束和异常结束的情况
0 每秒资源的异常个数 (error QPS)
4 资源平均响应时间(ms)
0 该秒占用未来请求的数目(since 1.5.0)
0 预留用
1 资源分类(since 1.7.0)
17.13.6 被拦截的秒级日志
同样的,每秒的拦截日志(block 日志)也会出现在 <用户目录>/logs/csp/sentinel-block.log 文件下。如果没有发生拦截,则该日志不会出现。
日志内容
2022-06-08 14:34:54|1|/order/testA,FlowException,default,|1,0
2022-06-08 14:34:54 日期
1 该秒发生的第一个资源
/order/testA 资源名称
FlowException 拦截的原因, 通常 FlowException 代表是被限流规则拦截,DegradeException 则表示被降级,SystemBlockException 则表示被系统保护拦截
default 生效规则的调用来源(参数限流中代表生效的参数)
1 被拦截的数量
0 无意义可忽略
2022-06-09 11:04:51|1|test0001,AuthorityException,lisi,lisi|1,0
AuthorityException 权限拦截
lisi 代表生效的参数
lisi 被拦截资源的调用者,可以为空
17.13.6 实时查询
http://localhost:8719/metric?identity=XXX&startTime=XXXX&endTime=XXXX&maxLines=XXXX
identity:资源名称 startTime:开始时间(时间戳) endTime:结束时间 maxLines:监控数据最大行数
17.14 整合openfeign
17.14.1 order模块修改application.yml
# 激活sentinel对feign支持
feign:
sentinel:
enabled: true
@EnableFeignClients
public class NacosOrderApp
注意:可能存在如下问题com.alibaba.cloud.sentinel.feign.SentinelContractHolder.parseAndValidatateMetadata(Ljava/lang/Class;)Ljava/util/List;
在2.2.0.RELEASE里有一行注释描述,接口方法名拼写错误,在2.2.2.RELEASE方法已修正了,即方法名发生了改变。
在spring-cloud-alibaba-sentinel中的SentinelContractHolder类,用到了该接口的这个方法(feign2.2.0.RELEASE版本):
解决方式1:
在工程下新建包 com.alibaba.cloud.sentinel.feign
重新修改 SentinelContractHolder
解决方式2:
修改版本依赖
17.14.2 创建feignclient
@FeignClient(value = "nacos-payment",fallback = PaymentFallBackService.class)
public interface PaymentService {
@GetMapping("/payment/test/{id}")
public String payment(@PathVariable("id") Long id);
}
@Component
public class PaymentFallBackService implements PaymentService{
@Override
public String payment(Long id) {
return "服务降级返回,----PaymentFallBackService";
}
}
17.14.3 order controller
@Autowired
PaymentService paymentService;
@GetMapping("/payment")
public String payment(Long id) {
return paymentService.payment(id);
}
17.14.4 payment controller
@GetMapping("/test/{id}")
public String payment(@PathVariable("id") Long id){
return id.toString()+"我是payment";
}
设置入口规则
返回sentinel限流
设置链路规则
测试发现返回的是feign的托底方法
17.15 sentinel懒加载
懒加载:只有访问该微服务才会在sentinel控制台监控到
sentinel:
#取消懒加载
eager: true
十八、Seata处理分布式事务
18.1、下载安装包并解压
下载地址
18.2、导入seata数据库
18.3、配置数据库
18.4、修改配置文件
在源码目录下打开Git Bash,输入sh nacos-config.sh,导入完成后,可在nacos中查看到所有配置。
todo...
18.5 新建三个数据库
分别在各自的库中导入表:
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for account_tbl
-- ----------------------------
DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`money` int(11) NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of account_tbl
-- ----------------------------
INSERT INTO `account_tbl` VALUES (1, '2', 1000);
SET FOREIGN_KEY_CHECKS = 1;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for order_tbl
-- ----------------------------
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`commodity_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`count` int(11) NULL DEFAULT 0,
`money` int(11) NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1006 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for storage_tbl
-- ----------------------------
DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`commodity_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`count` int(11) NULL DEFAULT 0,
`create_time` datetime(0) NULL DEFAULT NULL,
`update_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 77 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of storage_tbl
-- ----------------------------
INSERT INTO `storage_tbl` VALUES (1, 'iphone11', 100, '2020-09-28 16:55:51', '2020-10-14 16:13:21');
SET FOREIGN_KEY_CHECKS = 1;
18.6 order和payment客户端
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.0</version>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
18.1 order模块
application.yml
spring:
application:
name: nacos-order
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3308/db_account?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
username: root
password: 123
hikari:
minimum-idle: 5
idle-timeout: 600000
maximum-pool-size: 10
auto-commit: true
pool-name: MyHikariCP
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
seata:
#启用seata
enabled: true
#添加服务名称
application-id: seata-service-account
#分组
tx-service-group: my_test_tx_group
config:
type: nacos
nacos:
namespace:
server-addr: 10.39.35.101:8848
group: SEATA_GROUP
username: nacos
password: nacos
registry:
type: nacos
nacos:
application: seata-server
server-addr: 10.39.35.101:8848
group: SEATA_GROUP
namespace:
username: nacos
password: nacos
启动类
@MapperScan("com.lx.cloud.nacos.mapper")
mapper
public interface AccountTblMapper extends BaseMapper<Account> {
}
pojo
@Data
@TableName("account_tbl")
public class Account {
@TableId(type = IdType.AUTO)
private Long id;
private String user_id;
private Integer money;
}
controller
@Autowired
AccountTblMapper accountTblMapper;
@GetMapping("/test")
public Object test() {
paymentService.test();
Account account = new Account();
account.setMoney(700);
account.setUser_id("123");
return accountTblMapper.insert(account);
}
18.2 payment 模块
spring:
application:
name: nacos-payment
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3308/db_storage?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
username: root
password: 123
hikari:
minimum-idle: 5
idle-timeout: 600000
maximum-pool-size: 10
auto-commit: true
pool-name: MyHikariCP
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1
seata:
#启用seata
enabled: true
#添加服务名称
application-id: seata-service-storage
#分组
tx-service-group: my_test_tx_group
config:
type: nacos
nacos:
namespace:
server-addr: 10.39.35.101:8848
group: SEATA_GROUP
username: nacos
password: nacos
registry:
type: nacos
nacos:
application: seata-server
server-addr: 10.39.35.101:8848
group: SEATA_GROUP
namespace:
username: nacos
password: nacos
mapper
public interface StorageTblMapper extends BaseMapper<Storage> {
}
pojo
@Data
@TableName("storage_tbl")
public class Storage {
@TableId(type = IdType.AUTO)
private Long id;
private String commodity_code;
private Integer count;
private Date createTime;
private Date updateTime;
}
controller
@Autowired
StorageTblMapper storageTblMapper;
@GetMapping("testa")
public void testa(){
Storage entity = new Storage();
entity.setCount(1);
entity.setCommodity_code("123");
entity.setCreateTime(new Date());
entity.setUpdateTime(new Date());
storageTblMapper.insert(entity);
System.out.println(entity);
}
测试通过
模拟异常