nacos(NAming COnfiguration Service):服务注册和配置中心
Nacos = Eureka + Config + Bus
替代Eureka做服务注册中心
替代Config做服务配置中心
github地址: https://github.com/alibaba/Nacos
Nacos 地址: https://nacos.io/zh-cn/
服务注册与服务框架 | CAP模型 | 控制台管理 | 社区活跃度 |
---|---|---|---|
Eureka | AP高可用 | 支持 | 低(2.x版本闭源) |
Zookeeper | CP一致 | 支持 | 中 |
Consul | CP | 支持 | 高 |
Nacos | AP(可以切换) | 支持 高 |
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<junit.version>4.12junit.version>
<log4j.version>1.2.17log4j.version>
<lombok.version>1.16.18lombok.version>
<mysql.version>5.1.47mysql.version>
<druid.version>1.1.16druid.version>
<mybatis.spring.boot.version>1.3.2mybatis.spring.boot.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.2.2.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR1version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.1.0.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>${druid.version}version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>${mybatis.spring.boot.version}version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
<optional>trueoptional>
dependency>
dependencies>
dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
server:
port: 9001 #端口号
spring:
application:
name: nacos-payment-provider #服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置nacos地址
management:
endpoints:
web:
exposure:
include: "*" #监控的东西
@SpringBootApplication
@EnableDiscoveryClient
public class AlibabaProviderPayment9001Application {
public static void main(String[] args) {
SpringApplication.run(AlibabaProviderPayment9001Application.class, args);
}
}
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/payment/nacos/{id}")
public String getPayment(@PathVariable("id") Integer id) {
return "nacos registry, serverPort: " + serverPort + "\t id" + id;
}
}
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
server:
port: 9002 #端口号
spring:
application:
name: nacos-payment-provider #服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置nacos地址
management:
endpoints:
web:
exposure:
include: "*" #监控的东西
@SpringBootApplication
@EnableDiscoveryClient
public class AlibabaProviderPayment9002Application {
public static void main(String[] args) {
SpringApplication.run(AlibabaProviderPayment9001Application.class, args);
}
}
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@GetMapping(value = "/payment/nacos/{id}")
public String getPayment(@PathVariable("id") Integer id) {
return "nacos registry, serverPort: " + serverPort + "\t id" + id;
}
}
结果查看
分别启动nacos、alibaba-provider-payment9001和alibaba-provider-payment9002
在浏览器上输入网址:http://localhost:8848/nacos/
可以看出两个服务的提供者都注册进来了
nacos自带负载均衡机制
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
server:
port: 80
undertow:
decode-url:
spring:
application:
name: consumer-nacos-order
cloud:
nacos:
discovery:
server-addr: localhost:8848
server-url:
nacos-user-service: http://nacos-payment-provider #消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerNacosOrder80 {
public static void main(String[] args) {
SpringApplication.run(ConsumerNacosOrder80.class,args);
}
}
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced //负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
@RestController
@Slf4j
public class NacosOrderController {
@Resource
private RestTemplate restTemplate;
@Value("${server-url.nacos-user-service}")
private String serverUrl; //在yml里面写的提供者服务路径, 值为:http://nacos-provider
@GetMapping(value="consumer/payment/nacos/{id}")
public String paymentInfo(@PathVariable("id") int id)
{
return restTemplate.getForObject(serverUrl+"/payment/nacos/"+id,String.class);
}
}
测试
启动nacos,启动9001,9002和80端口,访问http://localhost/consumer/payment/nacos/111会发现轮询端口9001和端口9002
服务注册与服务框架 | CAP模型 | 控制台管理 | 社区活跃度 |
---|---|---|---|
Eureka | AP高可用 | 支持 | 低(2.x版本闭源) |
Zookeeper | CP一致 | 支持 | 中 |
Consul | CP | 支持 | 高 |
Nacos | AP(可以切换) | 支持 高 |
组件名 | 语言 | CAP | 服务健康检查 | 对外暴露接口 | SpringCloud集合 |
---|---|---|---|---|---|
Eureka | java | AP | 可配支持 | HTTP | 已集成 |
Consul | Go | CP | 支持 | HTTP/DNS | 已集成 |
Zookeeper | java | CP | 支持 | 客户端 | 已集成 |
Nacos的AP和CP
C是所有节点在同一时间看到的数据是一致的,而A的定义是所有的请求都会收到响应
何时选择使用何种模式?
一般来说,如果不需要存储服级的信息且服务实例是通过nacos-client注册,并能够保持心跳上报,那么就可以选择AP模式。当前主流的服务如Spring cloud和Dubbo服务,都适用于AP模式,AP模式为了服务的可用性而减弱了一致性,因此AP模式下只支持注册临时实例。
如果需要在服务级别编辑或者存储配置信息,那么CP是必须,K8S服务和DNS服务则适用于CP模式CP模式下则支持注册持久化实例,此时则是以Raft协议为集群运行模式,该模式下汪册实例之前须先注册服务,如果服务不存在,则会返回错误
切换命令:curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
# 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 # 指定yaml格式的配置
在application.yml中
spring:
profiles:
active: dev
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConfig3377 {
public static void main(String[] args) {
SpringApplication.run(NacosConfig3377.class,args);
}
}
@RestController
@RefreshScope //支持nacos的动态刷新功能
public class ConfigCenterController {
@Value("${config.info}")
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo()
{
return configInfo;
}
}
nacos同springcloud-config一样,在项目初始化时,要先从配置中心进行配置拉取,拉取配置之后,才能保证项目的正常启动。springboot的配置文件的加载是存在优先级的,bootstrap优先级高于application。(bootstrap中放共性,application中放个性)
nacos中的dataid的组成格式及与springboot配置文件中的匹配规则:
在 Nacos Spring Cloud 中,dataId 的完整格式如下:(就是说在nacos端我们怎么命名文件的)
${prefix}-${spring.profiles.active}.${file-extension}
prefix
默认为spring.application.name
的值,也可以通过配置项 spring.cloud.nacos.config.prefix
来配置。spring.profiles.active
即为当前环境对应的 profile
,详情可以参考 Spring Boot文档。 注意:当 spring.profiles.active
为空时,对应的连接符 - 也将不存在,dataId
的拼接格式变成 ${prefix}.${file-extension}
file-exetension
为配置内容的数据格式,可以通过配置项spring.cloud.nacos.config.file-extension
来配置。目前只支持 properties
和 yaml
类型。(注意nacos里必须使用yaml)
综合以上说明,和下面的截图,Nacos 的dataid(类似文件名)应为: nacos-config-client-dev.yaml (必须是yaml)
访问http://localhost:3377/config/info会得到里面的配置信息并且自带刷新功能
namespace类似于就java里面的package名和类名
最外面的namespace是用于分区部署环境,Group和DataID逻辑上区分为两个目标对象
Nacos的默认命名空间是public,Namespace主要用来实现隔离,比如现在我们有三个环境:开发、生产、测试环境,我们可以创建三个namespace,不同的namespace之间的隔离的
Group默认是DEAFULT_GROUP,Group可以把不同的微服务划分到同一分组里面
Service就是微服务,一个service可以包含多个cluster集群,nacos默认cluster是DEFAULT,Cluster是对指定微服务的一个虚拟划分。比方说为了容灾,将service微服务分别部署在了杭州机房和广州机房,这是就可以给杭州机房的service微服务起一个集群名称HZ给广州的service微服务起一个集群名称GZ,还可以尽量让同一个机房的微服务互相调用,以提升虚拟。最后instance就是微服务的实例
默认的nacos使用嵌入式数据库实现数据的存储,所以如果启用多个默认配置下的nacos节点,数据的存储存在一致性问题,Nacos采用了集中式存储的方式来支持集群化部署,目前只支持mysql的存储
单机模式 - 用于测试和单机试用。
集群模式 - 用于生产环境,确保高可用。
多集群模式 - 用于多数据中心场景。
单机模式支持mysql:在0.7版本之前,在单机模式时nacos使用嵌入式数据库(derby,他的pom里有这个依赖)实现数据的存储,不方便观察数据存储的基本情况
# 切换数据库
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456
预计需要1个nginx+3个nacos注册中心+1个mysql
tar -zvxf nacos-server-1.1.4.tar.gz
打开数据库执行nacos\conf目录下的SQL脚本
修改nacos/conf/application.properties文件(切换数据库),增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url、用户名和密码。
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456
集群启动,我们希望类似于其他软件的shell命令,传递不同的端口号启动不同的nacos实例命令: ./startup.sh -p 3333表示启动端口号为3333的nacos服务器实例,和上一步cluster.conf一样
编辑启动文件vim startup.sh
依次执行命令启动3个nacos集群:
./startup.sh -p 3333 表示启动端口号为3333的nacos服务器实例
./startup.sh -p 4444
./startup.sh -p 5555
ps -ef | grep nacos | grep -v grep | wc -
./nginx -c ../conf/nginx.conf
sentinel在 springcloud Alibaba 中的作用是实现熔断和限流。类似于Hystrix豪猪
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
sentinel下载地址为:https://github.com/alibaba/Sentinel/releases
选择相应的版本并进行下载
解决服务使用中的各种问题:服务雪崩,服务降级,服务熔断,服务限流
下载地址:https://github.com/alibaba/Sentinel/releases,下载sentinel-dashboard-1.8.2.jar
前提:java8 运行环境ok,8080端口不能别占用
命令:java -jar sentinel-dashboard-1.8.2.jar
访问网址:localhost:8080,用户名和密码都为sentinel
<dependencies>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
application.yml
server:
port: 8401
spring:
application:
name: alibaba-sentinel-service
cloud:
nacos:
discovery:
# nacos服务注册中心地址
server-addr: localhost:8848
sentinel:
transport:
# 配置sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,加入被占用会自动从8719每次加1扫描,直到扫描未被占用的端口
port: 8719
management:
endpoints:
web:
exposure:
include: '*' # 监控的东西
主启动
@SpringBootApplication
@EnableDiscoveryClient
public class AlibabaSentinelService8401Application {
public static void main(String[] args) {
SpringApplication.run(AlibabaSentinelService8401Application.class, args);
}
}
简单的controller测试sentinel的监控
@RestController
public class FlowLimitController {
@GetMapping("/textA")
public String textA()
{
return "-----------textA";
}
@GetMapping("/textB")
public String textB()
{
return "-----------textB";
}
}
启动sentinel
启动8401
3. 启动8401微服务后台查看sentinel控制台
你会发现什么也没有,因为sentinel采用的是懒加载机制,执行访问一次即可监控到
流控规则
重要属性:
Field | 说明 | 默认值 |
---|---|---|
resource | 资源名,资源名是限流规则的作用对象 | |
count | 限流阈值 | |
grade | 限流阈值类型,QPS 模式(1)或并发线程数模式(0) | QPS 模式 |
limitApp | 流控针对的调用来源 | default,代表不区分调用来源 |
strategy | 调用关系限流策略:直接、链路、关联 | 根据资源本身(直接) |
controlBehavior | 流控效果(直接拒绝/WarmUp/匀速+排队等待),不支持按调用关系限流 | 直接拒绝 |
clusterMode | 是否集群限流 | 否 |
限流表现:当超过阀值,就会被降级。1s内多次刷新网页,localhost:8401/testA,返回Blocked by Sentienl(flow limiting)
QPS和线程数的区别
QPS 类似于银行的保安 :所有的请求到Sentinel 后,他会根据阈值放行,超过报错
线程数类似于银行的窗口:所有的请求会被放进来,但如果阈值设置为1 那么,其他的请求就会报错也就是,银行里只有一个窗口,一个人在办理业务中,其他人跑过来则会告诉你,不行,没到你
当关联的资源达到阈值时就限流自己
设置效果
当关联资源/textB的QPS阀值超过1时,就限流/textA的Rest访问地址,当关联资源到阀值后就限制配置好的资源名
用postman高并发运行/textB运行/textA
直接快速失败,默认的流控处理
Warmup方式,即预热/冷启动的方式,当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉到搞水位可能瞬间把系统压垮,通过“冷启动”,让通过的流量缓慢增加,在一定的时间内逐渐增加到阀值上限,给冷系统一个预热时间,避免冷系统被压垮
公式:阀值除以coldFactor(默认值为3)经过预热时长后会达到阈值
默认coldFactor为3,即请求的QPS从(threshold/3)开始,经过多长的预热时间才逐渐升至设定是QPS阀值
案例:阀值为10 预热时长维护5秒
系统初始化的阀值为10/3约等于3,即阀值刚开始为3,然后经过5秒后阀值才慢慢升高恢复到10
匀速排队(Ru1eConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,即让请求以均匀的速度通过对应的是漏桶算法。详细文档可以参考流量控制-匀速器模式
这种方式主要用于处理间隔性突发的流量,例如消息对列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的月耖则处于空闲状态,我们希系统能够在接下来的空闲期间逐渐处理这些请求,而不是第一秒就拒绝多余的请求
案例
匀速排队,让请求以均匀的速度通过,阀值类型必须设置为QPS,否则无效
设置含义:/terxtB每秒一次请求,超过的话就排队等待,等待的超时时间为20000毫秒
Sentinel熔断降级会在调用链路中某个资源出现不稳定的状态是(例如调用超时或异常比例升高)。对这个资源的调用进行限制,让请求快速失败,避免影响到其他的资源而导致级联错误
当资源降级后,在接下来的降级时间窗口之内,对该资源的调用都会自动熔断
Sentineld的熔断器是没有半开的状态
半开的状态系统自动去检测是否请求异常,没有异常就关闭断路器恢复异常,有异常则继续打开断路器不可用
在controller中加入textC
@GetMapping("/textC")
public String textC()
{
//暂停几秒钟
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("textC 测试RT");
return "---------textC";
}
平均响应时间(DEGRADE_GRADE_RT):当一秒内持续进入5个请求,对应时刻的平均响应时间(秒级)均超过阀值(毫秒为单位)那么在接下来的时间窗口(秒为单位)内对这个方法的调用都会自动熔断。注意:RT最大4900(更大的需要通过-Dcsp.Sentinel.statistic.max.rt=XXXX才能生效)
postman发送请求,浏览器访问
异常比例(DEGRADE-GRADE-EXCEPTION-RATIO):当资源的每秒请求量>=5,并且每秒异常总数占通过的比值超过阈值(DegradeRule中的count)之后,资源进入降级状态,即在接下的时间窗口(DegradeRu1e中的timeWindow,,以s为单位)之内,对这个方法的调用都会自动地返回。异常b阈值范围是[0.0,l.0],代表0%一100%。
@GetMapping("/textD")
public String textD()
{
System.out.println("------textD");
int a=1/0;
return "---------textD";
}
异常数:当资源接近一分钟的异常数目超过超过阀值之后会进行熔断。注意由于统计时间的窗口是分钟级别的,若timeWindow小于60s,则结束熔断后仍可能进入熔断状态(时间窗口一定要大于等于60秒)
测试controller
//测试异常数
@GetMapping("/textE")
public String textE()
{
System.out.println("异常数测试");
int a=1/0;
return "异常数测试";
}
设置规则
当异常超过两处之后进行熔断降级
一次请求
使用postman多次请求
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的TopK数据,并对其访问进行限制。比如:
参数限流会统计传入参数中的参数,并根据配置流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
兜底方法分为系统默认和客户自定义两种,之前的case,限流出问题后,都是用sentinel系统默认的提示Blocked by Sentinel (flow limiting),我们可以定义一个类似于hystrix,某个方法出问题后找到对应的兜底方法
@GetMapping("/testHotkey")
@SentinelResource(value = "testHotkey",blockHandler = "deal_testHotKey") //blockHandler为指定处理降级的方法
//在填写热点限流的 资源名 这一项时,可以填 /testhotkey 或者是 @SentinelResource的value的值
public String testHotkey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2)
{
return "------testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockException exception)
{
return "------deal_textHotkey";
}
上述案例演示了第一个参数p1,当QPS超过一秒一次点击后马上限流,但我们期望p1的参数当他是某个特殊值时,他的限流和平时不一样,假如当p1等于5时,他的阀值可以达到10
@SentinelResource :处理的是Sentine1控制台配置的违规情况,有blockHandler方法配置的兜底处理
@RuntimeException:int age=10/0,这个是java运行时报出的运行时异异常RunTimeException,@Sentine1Resource不管
Sentinel 系统自适应限流从整体维度对应用入口流量进行控制,结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置nacos地址
management:
endpoints:
web:
exposure:
include: "*" #监控的东西
@SpringBootApplication
@EnableDiscoveryClient
public class AlibabaProviderPayment9003Application {
public static void main(String[] args) {
SpringApplication.run(AlibabaProviderPayment9003Application.class, args);
}
}
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
private static HashMap<Integer,Object> hashMap=new HashMap<>();
static {
hashMap.put(1,"1");
hashMap.put(2,"2");
hashMap.put(3,"3");
}
@GetMapping("/paymentSql/{id}")
public String paymentSql(@PathVariable("id") int id)
{
Object o = hashMap.get(id);
System.out.println(o);
return serverPort+"----------------------"+o;
}
}
内容和alibaba-provider-payment9004把端口改成9004即可
<dependencies>
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
server:
port: 84
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
# 配置sentinel dashboard地址
dashboard: localhost:8080
# 默认8719端口,加入被占用会自动从8719每次加1扫描,直到扫描未被占用的端口
port: 8719
server-url:
nacos-user-service: http://nacos-payment-provider
@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerNacosOrder84Application {
public static void main(String[] args) {
SpringApplication.run(ConsumerNacosOrder84Application.class, args);
}
}
@Configuration
public class ApplicationConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate()
{
return new RestTemplate();
}
}
@RestController
@Slf4j
public class cricleBreakerController {
private static final String SERVICE_URL="http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
// @SentinelResource(value = "fallback",fallback = "handlerFallback") //fallback负责业务异常
// @SentinelResource(value = "fallback",blockHandler = "handlerFallback")//blockHandler值赋值控制台sentinel配置违规
@SentinelResource(value = "fallback",fallback ="handlerFallback",blockHandler = "blockHandler")//若block和Handler都进行就配置,则被迫降级而报出的BlockException是只会进入blockHandler处理逻辑
public String fallback(@PathVariable int id)
{
String result = restTemplate.getForObject(SERVICE_URL + "/paymentSql/" + id, String.class, id);
if(id>=4)
{
throw new IllegalArgumentException("非法参数异常");
}
if(result==null)
{
throw new NullPointerException("空指针异常");
}
return result;
}
public String handlerFallback(@PathVariable int id,Throwable e)
{
return "handlerFallback";
}
public String blockHandler(@PathVariable int id, BlockException blockException)
{
return "blockHandler";
}
}
结论
fallback负责业务异常
blockHandler值赋值控制台sentinel配置违规
@SentinelResource(value = “fallback”,fallback =“handlerFallback”,blockHandler = “blockHandler”)若block和Handler都进行就配置,则被迫降级而报出的BlockException是只会进入blockHandler处理逻辑
将限流配置规则持久化建nacos保存,只要刷新某个rest地址,sentinel控制台的流控规则就能看到,只要nacos里面的配置不删除,sentinel的流控规则持久有效
目前的sentinel 当重启以后,数据都会丢失,和 nacos 类似原理。需要持久化。它可以被持久化到nacos 的数据库中。
在alibaba-sentinel-service8401模块
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-datasource-nacosartifactId>
dependency>
spring:
cloud:
sentinel:
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}
group: DEFAULT_GROUP
data-type: json
rule-type: flow
[
{
"resource": "/testA",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
resource:资源名称
limitApp:来源应用
grade:阈值类型,0表示线程数,1表示QPS,
count:单机阈值,
strategy:流控模式,0表示直接,1表示关联,2表示链路
controlBehavior:流控效果,0表示快速失败,1表示Warm Up,2表示排队等待;
cIusterM0de是否集群。
启动应用,发现存在 关于 /testA 请求路径的流控规则。
总结: 就是在 sentinel 启动的时候,去 nacos 上读取相关规则配置信息,实际上它规则的持久化,就是第三步,粘贴到nacos上保存下来,就算以后在 sentinel 上面修改了,重启应用以后也是无效的。
单体应用应用被拆成微服务应用,原来的三个模块被拆成三个独立的应用,分别使用三个独立的数据源,业务操作需要三个服务来完成,此时每个服务内部的一致性有本地事务来保证,但全局事务的一致性无法保证
一次事务操作需要跨多个数据源或需要跨多个系统进行远程调用,就会产生分布式事务问题
Seate是一款开源的分布式事务解决方案,致力于微服务构架下提供高性能和简单易用的分布式事务服务
seate是一个典型的分布式事务处理过程,由一个ID和三组件模型组成
一个ID:全局唯一的事务ID
三组件:
地址https://github.com/seata/seata/releases/download/v1.0.0/seata-server-1.0.0.zip
driver-class-name = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata"
user = "root"
password = "root"
DROP TABLE IF EXISTS `branch_table`;
CREATE TABLE `branch_table` (
`branch_id` bigint(20) NOT NULL,
`xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`transaction_id` bigint(20) NULL DEFAULT NULL,
`resource_group_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`branch_type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`status` tinyint(4) NULL DEFAULT NULL,
`client_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`gmt_create` datetime(0) NULL DEFAULT NULL,
`gmt_modified` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`branch_id`) USING BTREE,
INDEX `idx_xid`(`xid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for global_table
-- ----------------------------
DROP TABLE IF EXISTS `global_table`;
CREATE TABLE `global_table` (
`xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`transaction_id` bigint(20) NULL DEFAULT NULL,
`status` tinyint(4) NOT NULL,
`application_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`transaction_service_group` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`transaction_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`timeout` int(11) NULL DEFAULT NULL,
`begin_time` bigint(20) NULL DEFAULT NULL,
`application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`gmt_create` datetime(0) NULL DEFAULT NULL,
`gmt_modified` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`xid`) USING BTREE,
INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE,
INDEX `idx_transaction_id`(`transaction_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for lock_table
-- ----------------------------
DROP TABLE IF EXISTS `lock_table`;
CREATE TABLE `lock_table` (
`row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`transaction_id` bigint(20) NULL DEFAULT NULL,
`branch_id` bigint(20) NOT NULL,
`resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`gmt_create` datetime(0) NULL DEFAULT NULL,
`gmt_modified` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`row_key`) USING BTREE,
INDEX `idx_branch_id`(`branch_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;