赞
踩
服务限流降级:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。
服务注册与发现:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
分布式配置管理:支持分布式系统中的外部化配置,配置更改时自动刷新。
消息驱动能力:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
分布式事务:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。
阿里云对象存储:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
分布式任务调度:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。
阿里云短信服务:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台
替代Eureka做服务注册中心,替代Config做服务配置中心
Nacos与其他注册中心对比
Nacos支持AP和CP模式的切换
C是所有节点在同一时间看到的数据是一致的;而A的定义是所有的请求都会收到响应。
何时选择使用何种模式?
—般来说,如果不需要存储服务级别的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如Spring cloud和Dubbo服务,都适用于AP模式,AP模式为了服务的可能性而减弱了一致性,因此AP模式下只支持注册临时实例。
如果需要在服务级别编辑或者存储配置信息,那么CP是必须,K8S服务和DNS服务则适用于CP模式。CP模式下则支持注册持久化实例,此时则是以Raft协议为集群运行模式,该模式下注册实例之前必须先注册服务,如果服务不存在,则会返回错误。
引入依赖
<!--父工程引入-- spring cloud alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--子工程引入--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
配置文件
server:
port: 9002
spring:
application:
name: nacos-payment-provider # 微服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
开启配置
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient //将微服务注册进nacos
@SpringBootApplication
public class PaymentMain9002 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9002.class, args);
}
}
查看是否注册成功
引入依赖
<!--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>
</dependency>
配置文件
注:Nacos同springcloud-config一样,在项目初始化时,要保证先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动。springboot中配置文件的加载是存在优先级顺序的,bootstrap.yml优先级高于application.yml,所以对于全局微服务的配置,全部放在bootstrap.yml中,而对于nacosConfig自己的配置则存在于application.yml中。
bootstrap.yml
# nacos配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml #指定所有微服务配置文件的格式
# group: DEV_GROUP
# namespace: 7d8f0f5a-6a53-4785-9686-dd460158e5d4
#Data ID为: nacos-config-client-dev.yaml
application.yml
spring:
profiles:
active: dev # 表示开发环境
#active: test # 表示测试环境
#active: info
Nacos中添加配置信息
Nacos中的Data ID的组成格式
Data ID的配置格式为:${prefix}-${spring-profile.active}.${file-extension}
解析后配置格式为:${spring.application.name)}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
${prefix}.${file-extension}
,不建议为空打开nacos,图中箭头所指即为Data ID,按照图中步骤,在Nacos中添加配置信息
开启配置
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient //将服务注册到nacos
@SpringBootApplication
public class NacosConfigClientMain3377
{
public static void main(String[] args) {
SpringApplication.run(NacosConfigClientMain3377.class, args);
}
}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope //实现配置自动更新
public class ConfigClientController
{
@Value("${config.info}")
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo() {
return configInfo;
}
}
查看是否配置成功
1.访问接口,查看配置信息(http://localhost:3377/config/info),若能返回
上图中配置的 nacos version 1.0,则说明配置成功
2.将上述配置文件中的config.info 改成nacos version 2.0,再次访问http://localhost:3377/config/info,查看有无变化,若改变则配置成功
实际开发中,通常一个系统会准备,dev开发环境,test测试环境,prod生产环境。为了保证指定环境启动时服务能正确读取到Nacos上相应环境的配置文件,所以Nacos通过,
Namespace(命名空间)+Group(分组)+Data lD
,对微服务配置进行管理。
spring:
profiles:
# active: dev #开发环境
active: test # 测试环境
# nacos配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml #指定所有微服务配置文件的格式
group: TEST_GROUP #指定配置文件所处分组
在application.yml中指定开发环境
spring:
profiles:
# active: dev #开发环境
# active: test # 测试环境
active: info # 其他分组配置文件
# nacos配置
server:
port: 3377
spring:
application:
name: nacos-config-client
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
config:
server-addr: localhost:8848 #Nacos作为配置中心地址
file-extension: yaml #指定所有微服务配置文件的格式
namespace: f8c4f405-b29e-4f88-ad3b-553030b02239 #指定命名空间ID
group: DEV_GROUP #指定分组
application.yml中指定开发环境
spring:
profiles:
active: dev #开发环境
Nacos默认使用嵌入式数据库derby实现数据的存储。所以,如果启动多个默认配置下的Nacos节点,数据存储是存在一致性问题的。为了解决这个问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。
持久化切换配置(windos版本)
将原有的Derby数据库切换到mysql
1.在nacos的安装目录nacos-server-1.1.4\nacos\conf下找到nacos-mysql.sql文件,创建名为nacos_config的数据库,在该数据库中执行脚本
2.在nacos的安装目录nacos-server-1.1.4\nacos\conf目录下找到application.properties,添加以下配置,将数据库用户名密码改为自己的
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=1234
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=1234
5.在nacon的安装目录nacos/conf/目录下执行如下命令,将集群配置文件拷贝一份出来
cp cluster.conf.example cluster.conf
6.执行如下命令,复制IP
hostname -i
7.将cluster.conf文件中的内容改为如下配置
#it is ip
#example
#10.10.109.214
#11.16.128.34
#11.16.128.36
################以上为原来的内容,注释掉#########################
#### 这里的172.31.126.115是步骤6的当中复制的IP,端口号可以自定义
172.31.126.115:3333
172.31.126.115:4444
172.31.126.115:5555
8.切换到nacos安装目录/nacos/bin执行如下命令,拷贝startup.sh文件
cp startup.sh startup.sh.info
9.更改startup.sh文件,平时单机版的启动,都是./startup.sh即可
但是,集群启动,我们希望可以类似其它软件的shell命令,传递不同的端口号启动不同的nacos实例,所以需要作出更改
#!/bin/sh
# 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.
cygwin=false
darwin=false
os400=false
case "`uname`" in
CYGWIN*) cygwin=true;;
Darwin*) darwin=true;;
OS400*) os400=true;;
esac
error_exit ()
{
echo "ERROR: $1 !!"
exit 1
}
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=$HOME/jdk/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/java
[ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/opt/taobao/java
[ ! -e "$JAVA_HOME/bin/java" ] && unset JAVA_HOME
if [ -z "$JAVA_HOME" ]; then
if $darwin; then
if [ -x '/usr/libexec/java_home' ] ; then
export JAVA_HOME=`/usr/libexec/java_home`
elif [ -d "/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home" ]; then
export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home"
fi
else
JAVA_PATH=`dirname $(readlink -f $(which javac))`
if [ "x$JAVA_PATH" != "x" ]; then
export JAVA_HOME=`dirname $JAVA_PATH 2>/dev/null`
fi
fi
if [ -z "$JAVA_HOME" ]; then
error_exit "Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better!"
fi
fi
export SERVER="nacos-server"
export MODE="cluster"
export FUNCTION_MODE="all"
while getopts ":m:f:s:p:" opt
do
case $opt in
m)
MODE=$OPTARG;;
f)
FUNCTION_MODE=$OPTARG;;
s)
SERVER=$OPTARG;;
p)
PORT=$OPTARG;;
?)
echo "Unknown parameter"
exit 1;;
esac
done
export JAVA_HOME
export JAVA="$JAVA_HOME/bin/java"
export BASE_DIR=`cd $(dirname $0)/..; pwd`
export DEFAULT_SEARCH_LOCATIONS="classpath:/,classpath:/config/,file:./,file:./config/"
export CUSTOM_SEARCH_LOCATIONS=${DEFAULT_SEARCH_LOCATIONS},file:${BASE_DIR}/conf/
#===========================================================================================
# JVM Configuration
#===========================================================================================
if [[ "${MODE}" == "standalone" ]]; then
JAVA_OPT="${JAVA_OPT} -Xms512m -Xmx512m -Xmn256m"
JAVA_OPT="${JAVA_OPT} -Dnacos.standalone=true"
else
JAVA_OPT="${JAVA_OPT} -server -Xms2g -Xmx2g -Xmn1g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${BASE_DIR}/logs/java_heapdump.hprof"
JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages"
fi
if [[ "${FUNCTION_MODE}" == "config" ]]; then
JAVA_OPT="${JAVA_OPT} -Dnacos.functionMode=config"
elif [[ "${FUNCTION_MODE}" == "naming" ]]; then
JAVA_OPT="${JAVA_OPT} -Dnacos.functionMode=naming"
fi
JAVA_MAJOR_VERSION=$($JAVA -version 2>&1 | sed -E -n 's/.* version "([0-9]*).*$/\1/p')
if [[ "$JAVA_MAJOR_VERSION" -ge "9" ]] ; then
JAVA_OPT="${JAVA_OPT} -cp .:${BASE_DIR}/plugins/cmdb/*.jar:${BASE_DIR}/plugins/mysql/*.jar"
JAVA_OPT="${JAVA_OPT} -Xlog:gc*:file=${BASE_DIR}/logs/nacos_gc.log:time,tags:filecount=10,filesize=102400"
else
JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${JAVA_HOME}/lib/ext:${BASE_DIR}/plugins/cmdb:${BASE_DIR}/plugins/mysql"
JAVA_OPT="${JAVA_OPT} -Xloggc:${BASE_DIR}/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M"
fi
JAVA_OPT="${JAVA_OPT} -Dnacos.home=${BASE_DIR}"
JAVA_OPT="${JAVA_OPT} -Dloader.path=${BASE_DIR}/plugins/health -jar ${BASE_DIR}/target/${SERVER}.jar"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} --spring.config.location=${CUSTOM_SEARCH_LOCATIONS}"
JAVA_OPT="${JAVA_OPT} --logging.config=${BASE_DIR}/conf/nacos-logback.xml"
JAVA_OPT="${JAVA_OPT} --server.max-http-header-size=524288"
if [ ! -d "${BASE_DIR}/logs" ]; then
mkdir ${BASE_DIR}/logs
fi
echo "$JAVA ${JAVA_OPT}"
if [[ "${MODE}" == "standalone" ]]; then
echo "nacos is starting with standalone"
else
echo "nacos is starting with cluster"
fi
# check the start.out log output file
if [ ! -f "${BASE_DIR}/logs/start.out" ]; then
touch "${BASE_DIR}/logs/start.out"
fi
# start
echo "$JAVA ${JAVA_OPT}" > ${BASE_DIR}/logs/start.out 2>&1 &
nohup $JAVA -Dserver.port=${PORT} ${JAVA_OPT} nacos.nacos >> ${BASE_DIR}/logs/start.out 2>&1 &
echo "nacos is starting,you can check the ${BASE_DIR}/logs/start.out"
修改内容如下:
10.配置nginx.conf实现负载均衡,将一下内容添加到nginx.conf的http {}节点下,此处配置的3333,4444,5555端口号为步骤7当中配置的端口号
#配置一组服务,轮询以下节点
upstream cluster {
server 127.0.0.1:3333;
server 127.0.0.1:4444;
server 127.0.0.1:5555;
}
server {
listen 1111;
server_name localhost;
location / {
proxy_pass http://cluster;
}
}
11.重启nginx
12.在nacos安装目录/nacos/bin下执行如下命令,启动nacos
startup.sh - p 3333
startup.sh - p 4444
startup.sh - p 5555
13.测试是否搭建成功,http://172.31.126.115:1111/nacos/#/login
Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统自适应保护等多个维度来帮助您保障微服务的稳定性。
下载安装
下载地址
1.下载对应版本的jar文件
2.使用如下命令直接运行该文件
java -jar sentinel-dashboard-1.7.0.jar
3.访问Sentinel管理界面(localhost:8080),输入账号密码为sentinel
注:启动环境需保证8080端口不被占用,java环境为1.8+
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!--SpringCloud ailibaba 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>
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos服务注册中心地址
sentinel:
transport:
dashboard: localhost:8080 #配置Sentinel dashboard地址
port: 8719 #指定应用与Sentinel控制台交互的端口
management:
endpoints:
web:
exposure:
include: '*'
feign:
sentinel:
enabled: true # 激活Sentinel对Feign的支持
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient // 将当前微服务注册到nacos
@SpringBootApplication
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
@Slf4j
public class FlowLimitController {
@GetMapping("/testA")
public String testA()
{
return "------testA";
}
@GetMapping("/testB")
public String testB()
{
log.info(Thread.currentThread().getName()+"\t"+"...testB");
return "------testB";
}
}
资源名
:唯一名称,默认请求路径,即接口地址,如上文中的 /testA,/testB
针对来源
:Sentinel可以针对调用者进行限流,填写微服务名,默认default(不区分来源)。
阈值类型/单机阈值
:
QPS(每秒钟的请求数量)
︰当调用该API的QPS达到阈值的时候,进行限流。即,每秒钟该接口,只能请求指定次数
线程数
:当调用该API的线程数达到阈值的时候,进行限流。
是否集群
:不需要集群。
流控模式
:
直接
:API达到限流条件时,直接限流。
关联
:当关联的资源达到阈值时,就限流自己。
链路
:只记录指定链路上的流量(指定资源从入口资源进来的流量,如果达到阈值,就进行限流)【API级别的针对来源】。
流控效果
:
快速失败
:直接失败,抛异常。
Warm up
:根据Code Factor(冷加载因子,默认3)的值,从阈值/codeFactor,经过预热时长,才达到设置的QPS阈值。
排队等待
:匀速排队,让请求以匀速的速度通过,阈值类型必须设置为QPS,否则无效。
例1:QPS直接失败
以上配置表示/testA这个接口,1秒钟内查询1次就是OK,若超过次数1,就直接->快速失败,报默认错误
例2:线程数直接失败
以上配置表示/testA这个接口,调用线程数为1就是OK,若超过1,比如有多个线程调用,就直接->快速失败,报默认错
例3:关联快速失败
以上配置表示,1秒内如果请求对 /testB接口的请求次数超过1,触发阀值,那么就会对/testA进行限流,相当于是,a服务调用b服务,如果b服务触发阀值,那么就限流a
例4:链路快速失败
以上配置表示,只有当通过/testB接口来用调用的/testA,每秒超过1个请求才会对/testA触发限流
例5:Warm Up(预热)
以上配置表示系统初始化的阀值为10/ 3约等于3,即系统前5秒阀值刚为3;然后过了5秒后阀值才慢慢升高恢复到10,此处的3为官方默认值(默认coldFactor为3,即请求QPS 从 threshold / 3开始,经预热时长逐渐升至设定的QPS阈值)
例6:排队等待
以上配置表示,对来自/testB的请求实行排队机制,每秒只允许处理1当前面的请求执行时间超过2秒时,那么就会对后面排队的请求抛出超时异常
熔断降级概述:
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
现代微服务架构都是分布式的,由非常多的服务组成。不同服务之间相互调用,组成复杂的调用链路。以上的问题在链路调用中会产生放大的效果。复杂链路上的某一环不稳定,就可能会层层级联,最终导致整个链路都不可用。因此我们需要对不稳定的弱依赖服务调用进行熔断降级,暂时切断不稳定调用,避免局部不稳定因素导致整体的雪崩。熔断降级作为保护自身的手段,通常在客户端(调用端)进行配置。
Sentinel熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。Sentinei在1.8.0版本以上新增了半开状态,半开的状态系统自动去检测是否请求有异常,没有异常就关闭断路器恢复使用,有异常则继续打开断路器不可用。
RT:
(平均响应时间,秒级),平均响应时间 超出阈值 且 在1m内通过的请求>=5,两个条件同时满足后触发降级。窗口期过后关闭断路器。RT最大4900(更大的需要通过-Dcsp.sentinel.statistic.max.rt=XXXX才能生效)。
Sentinel 1.7.0才有平均响应时间(DEGRADE_GRADE_RT),Sentinel 1.8.0的没有这项,取而代之的是慢调用比例 (SLOW_REQUEST_RATIO)。
慢调用比例 (SLOW_REQUEST_RATIO):
选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。
异常比列(秒级):
QPS >= 5且异常比例(秒级统计)超过阈值时,触发降级;时间窗口结束后,关闭降级 。
异常数(分钟级):
异常数(分钟统计)超过阈值时,触发降级;时间窗口结束后,关闭降级
例1:降级策略RT
以上配置表示,对/testD接口的调用如果在200毫秒以内都没有响应,且在1秒内请求数大于等于5,那么将会在时间窗口期触发降级
例2:降级策略-异常比例
以上配置表示,对/testD接口的调用如果有20%的次数都是错误时,且一秒访问接口的数量大于等于5,那么将会在时间窗口期内对服务实行降级
资源名:
唯一名称,@SentinelResource注解中的value配置项
参数索引:
参数索引为@SentinelResource注解的方法参数下标,0代表第一个参数,1代表第二个参数
单机阈值:
表示一秒钟的最大请求量
统计窗口时长:
表示在此窗口时间超过阈值就限流
参数例外项:
参数类型:
表示当前配置的参数类型
参数值:
表示当前参数传入的值为当前配置项时,将会使用例外项配置
限流阀值:
对例外项参数值的限流阀值
- 例1:热点规则-普通参数限制
存在如下接口:
/**
* 此处@SentinelResource注解当中,
* value的配置表示为热点规则配置中的资源名称,必须唯一,名称可随意,一般为接口名
* blockHandler的配置表示当触发阀值将会去执行的方法名称,不配置此项,当触发阀值后将会报500,并抛出异常页面
*/
@GetMapping("/testHotKey/test")
@SentinelResource(value = "testHotKey",blockHandler/*兜底方法*/ = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2) {
//int age = 10/0;
return "------testHotKey";
}
/*触发阀值后执行的兜底方法*/
public String deal_testHotKey (String p1, String p2, BlockException exception) {
return "------deal_testHotKey,o(╥﹏╥)o"; //sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
}
以上配置表示,当访问/testHotKey/test接口时,如果传入下标为0的参数,即p1,那么会对当前请求实施监控,若在一秒内请求次数超过1,那么将会触发阀值,从而执行deal_testHotKey 方法中的内容,例如(http://localhost:8401/testHotKey/test?p1=abc)将会触发监控,而(http://localhost:8401/testHotKey/test?p2=33)就不会触发监控
注:@SentinelResource中配置的blockHandler值,只会在触发限流时会执行,若是程序本省出现的异常,并不会去执行blockHandler中配置的方法
- 例2:热点规则-例外项参数限制
以上配置表示,当访问/testHotKey/test接口时,如果传入下标为0的参数,即p1,那么会对当前请求实施监控,若在一秒内请求次数超过1,那么将会触发阀值,从而执行deal_testHotKey 方法中的内容,例如(http://localhost:8401/testHotKey/test?p1=abc)将会触发监控,而(http://localhost:8401/testHotKey/test?p2=33)就不会触发监控,并且当p1的参数值为5时,那么该接口的限流阀值将不再是1秒1个,而是1秒200个
属性详解:
value:
Sentinel资源的名称,我们不仅可以通过url进行限流,也可以把此值作为资源名配置,一样可以限流。entryType:
条目类型(入站或出站),默认为出站(EntryType.OUT),在配置系统规则时,如果阀值类型选择的是入口QPS那么配置entryType = EntryType.OUT 的接口不会被限流,配置entryType = EntryType.IN的接口将会被限流resourceType:
资源的分类(类型),用于标识资源的分类,0-通用、1-WEB、2-RPC、3-GATEWAY、4-SQL,在 ResourceTypeConstants 类里有定义。blockHandler:
块异常函数的名称,默认为空,正常来说我们如果达到了限流阈值、被熔断了报错之后,是会将500错误页面进行返回的。如果我们配置了blockHandler这个属性,他就会执行名称为这个属性的值的方法。该方法必须在最后面加上一个类型为BlockException的参数
blockHandlerClass:
指定块处理方法所在的类请注意,块处理程序方法必须是静态的。
fallback:
后备函数的名称,默认为空 ,fallback 方法可以针对所有类型的异常(除了 exceptionsToIgnore 参数里面排除掉的异常类型)进行处理。defaultFallback:
默认后备方法的名称,默认为空,如果没有配置fallback的值,默认都会走到这里来。 默认的 fallback 方法名称,可选项,通常用于通用的 fallback 逻辑。 默认 fallback 方法可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。 若同时配置了 fallback 和 defaultFallback,则只有 fallback 会生效。defaultFallback用作默认的通用后备方法。 它不应接受任何参数,并且返回类型应与原始方法兼容fallbackClass:
fallback方法所在的类(仅单个类)请注意,共享的后备方法必须是静态的。
,fallback 方法默认需要和原方法在同一个类中,如果指定blockHandlerClass属性的值,则使用指定类中的方法。如果是其他类中的方法,该方法必须是静态(static)的。实际使用和 blockHandlerClass 几乎一样exceptionsToTrace:
异常类的列表追查,用于指定哪些异常被统计,没有指定的异常,就算发生了,也不会触发fallback方法,与exceptionsToIgnore 相反。可选。默认Throwable。exceptionsToIgnore
要忽略的异常类列表,默认情况下为空,与exceptionsToTrace效果相反例:
编写全局限流处理类
import com.alibaba.csp.sentinel.slots.block.BlockException;
/**
* 全局限流处理类
*/
public class CustomerBlockHandler {
/**
* 全局同一blockHandler配置方法
* 此处的方法必须是静态的,且参数,返回值必须和将要使用的方法参数一致,且在最后添加一个BlockException作为参数
* @param exception
* @return
*/
public static String handlerException(@PathVariable("num") Integer num,BlockException exception) {
return "自定义的限流处理";
}
}
** 编写全局异常处理类 **
/**
* 全局异常处理类
*/
public class FallbackHandler {
/**
* 全局同一fallback配置方法
* 此处的方法必须是静态的,且参数,返回值必须与使用该方法的接口参数保持一致
* @return
*/
public static String handlerFallback(@PathVariable("num") Integer num,Throwable e) {
return "自定义的异常处理"+e.getMessage();
}
}
** 测试接口**
/**
* 测试SentinelResource属性
*
* @return
*/
@GetMapping("/testSentinelResource/{num}")
@SentinelResource(value = "testSentinelResource",//资源名称
blockHandlerClass = CustomerBlockHandler.class,// 自定义限流处理类
blockHandler = "handlerException",//自定义限流处理类当中的方法,注意,此处定义的方法,必须和以下接口参数,返回值保持一致,且参数最后需要有BlockException参数
fallbackClass = FallbackHandler.class,// 自定义全局异常处理类
fallback = "handlerFallback")//自定义全局异常处理方法,注意,此处定义的方法,必须和以下接口参数,返回值保持一致
public String customerBlockHandler(@PathVariable("num") Integer num) {
if(num==1){
int a=10/0;
}
return "测试SentinelResource属性";
}
/**
* 本类自定义限流处理,在本类中写,就可以不添加blockHandlerClass属性
* @param num
* @param exception
* @return
*/
public String handlerException(@PathVariable("num") Integer num,BlockException exception) {
return "自定义限流信息2";
}
/**
* 本类自定义异常处理,在本类中写,就可以不添加fallbackClass属性
* @param num
* @return
*/
public static String handlerFallback(@PathVariable("num") Integer num,Throwable e) {
return "自定义的异常处理2"+e.getMessage();
}
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 达到阈值即触发系统保护。
<!--SpringCloud openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
# 激活Sentinel对Feign的支持
feign:
sentinel:
enabled: true
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableDiscoveryClient //将服务注册到nacos
@SpringBootApplication
@EnableFeignClients //开启Feign
public class OrderNacosMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain84.class, args);
}
}
import com.wf.springcloud.entities.CommonResult;
import com.wf.springcloud.entities.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* value:需要调用的微服务名称
* fallback:服务熔断时调用的全局处理类
*/
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)
public interface PaymentService
{
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
import com.wf.springcloud.entities.CommonResult;
import com.wf.springcloud.entities.Payment;
import org.springframework.stereotype.Component;
/**
* 当PaymentFallbackService的业务出现服务熔断时,由该类进行处理
*/
@Component
public class PaymentFallbackService implements PaymentService {
@Override
public CommonResult<Payment> paymentSQL(Long id)
{
return new CommonResult<>(44444,"服务降级返回,---PaymentFallbackService",new Payment(id,"errorSerial"));
}
}
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.wf.springcloud.alibaba.service.PaymentService;
import com.wf.springcloud.entities.CommonResult;
import com.wf.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
@Slf4j
public class CircleBreakerController {
//==================OpenFeign
@Resource
private PaymentService paymentService;
@GetMapping(value = "/consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id)
{
return paymentService.paymentSQL(id);
}
}
当微服务重启应用时,在sentinel中配置的流控规则,降级规则等规则将消失,生产环境需要将配置规则进行持久化
一次业务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题
Seata是一款开源的解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务
Seata的三大组件
TC (Transaction Coordinator) - 事务协调者:
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器:
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器:
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
分布式事务的处理过程
1.TM向TC申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的XID;
2.XID在微服务调用链路的上下文中传播;
3.RM向TC注册分支事务,将其纳入XID对应全局事务的管辖;
4.TM向TC发起针对XID的全局提交或回滚决议;
5.TC调度XID下管辖的全部分支事务完成提交或回滚请求。
配置安装
安装教程官方文档
建表语句
步骤:
1.修改seata-server-1.4.2\seata\seata-server-1.4.2\conf文件目录下,file.conf文件
将store.mode修改为
mode="db"
2.修改此文件中的store.db项,将数据库用户名密码改为自己的
3.新建数据库seata,在该数据库下执行上文建表语句链接中的SQL脚本
4.修改seata-server-1.4.2\seata\seata-server-1.4.2\conf文件目录下,registry.conf文件中的registry.type项,修改为
type = "nacos"
并将registry.nacos中,nacos的连接地址修改正确
//在调用的方法上添加如下注解
//name:自定义,唯一即可
@GlobalTransactional(name = "create-order",rollbackFor = Exception.class)
注:如存在以下业,下订单-》修改库存——》修改账户金额,那么该注解就应该添加在下订单的微服务方法上
分布式系统中,有一些需要使用全局唯一ID的场景,有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。Twitter的Snowflake 算法就是这种生成器。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。