https://spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now
说明
是什么
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
去哪下:
https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md
能干嘛:
怎么玩
官网:https://spring.io/projects/spring-cloud-alibaba#overview
英文
https://github.com/alibaba/spring-cloud-alibaba
英文文档:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html
中文
官网:https://nacos.io/zh-cn/index.html
下载地址:https://github.com/alibaba/nacos/releases
spring-cloud-alibaba在GitHub上面的总文档:使用Nacos
https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_nacos_discovery
也可以直接在Nacos官网中查看文档:
据说 Nacos 在阿里巴巴内部有超过 10 万的实例运行,已经过了类似双十一等各种大型流量的考验
GitHub官网查看各个组件对应的版本:
https://github.com/alibaba/spring-cloud-alibaba
老版本的直接启动即可
新版本需要修改配置文件才能启动成功:
官方文档说明:
原因:老版本的鉴权有默认值,新版本为了安全去掉了默认值所以需要手动配置
如何修改查看:B站动力节点SpringCloud视频
下载之后解压压缩包
本地Java8+Maven环境已经OK
在bin目录中输入cmd,进入到命令行窗口
startup.cmd -m standalone
命令运行成功后直接访问:http://localhost:8848/nacos
<!--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>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.angenin.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.angenin.springcloud.alibaba</groupId>
<artifactId>cloudalibaba-provider-payment9001</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
<scope>test</scope>
</dependency>
</dependencies>
</project>
server:
port: 9001
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*' #暴露监控所有的端点
package com.angenin.springcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient //这个注解和之前的不一样,开启服务注册功能
@SpringBootApplication
public class PaymentMain9001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9001.class, args);
}
}
package com.angenin.springcloud.alibaba.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@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;
}
}
启动Nacos8848注册中心,启动9001生产者
访问控制层方法:http://localhost:9001/payment/nacos/1
nacos控制台:http://localhost:8848/nacos
nacos服务注册中心+服务提供者9001都OK了
nacos自带负载均衡功能:为了下一章节演示nacos的负载均衡,参照9001新建9002
方式一:取巧不想新建重复体力劳动,直接拷贝虚拟端口映射
方式二:手动创建新建 cloudalibaba-provider-payment9002
创建步骤:略
效果:
启动9001,9002查看后台管理界面:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.angenin.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.angenin.springcloud.alibaba</groupId>
<artifactId>cloudalibaba-consumer-nacos-order83</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.angenin.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
<scope>test</scope>
</dependency>
</dependencies>
</project>
server:
port: 83
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848 #Nacos注册中心的地址
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
#之前直接写的是生产者集群服务的名称 写死了,现在是写在配置文件中通过注解@Value读取获得
service-url:
nacos-user-service: http://nacos-payment-provider
package com.angenin.springcloud.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain83 {
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain83.class,args);
}
}
package com.angenin.springcloud.alibaba.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextBean {
@Bean
@LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
public RestTemplate getRestTemplate() {
//RestTemplate提供了多种便捷访问远程http访问的方法
return new RestTemplate();
}
}
package com.angenin.springcloud.alibaba.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
public class OrderNacosController {
@Resource
private RestTemplate restTemplate;
//之前直接写的是生产者集群服务的名称 写死了,现在是写在配置文件中通过注解@Value读取获得
@Value("${service-url.nacos-user-service}")
private String serverURL;
@GetMapping("/consumer/payment/nacos/{id}")
public String paymentInfo(@PathVariable("id") Long id) {
//getForObject两个参数:请求地址,返回的对象类型----读操作
return restTemplate.getForObject(serverURL+"/payment/nacos/"+id,String.class);
}
}
启动nacos,9001生产者,9002生产者,83消费者
nacos控制台
http://localhost:83/consumer/payment/nacos/13
83访问9001/9002,轮询负载OK
Nacos全景图所示
Nacos和CAP
Nacos 支持AP和CP模式的切换
C是所有节点在同一时间看到的数据是一致的;而A的定义是所有的请求都会收到响应。
何时选择使用何种模式?
curl -X PUT '$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP'
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.angenin.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-config-nacos-client3377</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--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>
<!--web + actuator-->
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
<scope>test</scope>
</dependency>
</dependencies>
</project>
bootstrap优先级高于application
# 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格式的配置(3377就可以到8848上去读取,后缀名指定为yaml格式的文件)
# ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
# nacos-config-client-dev.yaml (不识别yml)
spring:
profiles:
active: dev #表示开发环境
# 这样bootstrap+application结合起来就相当于:3377到8848配置中心上去读取,一个什么样的yml文件
package com.angenin.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class NacosConfigClientMain3377
{
public static void main(String[] args) {
SpringApplication.run(NacosConfigClientMain3377.class, args);
}
}
通过Spring Cloud 原生注解@RefreshScope实现配置自动更新:
package com.angenin.springcloud.controller;
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 //在控制器类加入@RefreshScope注解使当前类下的配置支持Nacos的动态刷新功能。
public class ConfigClientController {
/**
* 和之前学习SpringCloud Config分布式配置中心一样:
* 分布式配置中心可以,将配置信息以REST接口的形式暴露:post、curl访问刷新均可......
* 既然配置信息暴漏了,那么3355就可以通过REST风格读取到3344配置中心的消息和内容的配置。
*/
@Value("${config.info}")
private String configInfo;
@GetMapping("/config/info")
public String getConfigInfo() {
return configInfo;
}
}
理论:
Nacos中的data id的组成格式及与SpringBoot配置文件中的匹配规则
官网:https://nacos.io/zh-cn/docs/v2/ecology/use-nacos-with-spring-cloud.html
最后公式:
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
nacos-config-client-dev.yaml
实操:
配置新增:nacos-config-client-dev
Nacos界面配置对应
config:
info: nacos config center,version = 1
点击发布:配置列表就多了一行配置信息
总结:设置DataId
${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
历史配置
配置管理
命名空间
是什么
三者情况
默认情况:
Namespace=public,Group=DEFAULT_GROUP, 默认Cluster是DEFAULT
指定spring.profile.active和配置文件的DataID来使不同环境下读取不同的配置
默认空间+默认分组+新建dev和test两个DataID
通过spring.profile.active属性就能进行多环境下配置文件的读取
测试
重启3377
http://localhost:3377/config/info
配置是什么就加载什么:test
通过Group实现环境区分:新建Group
在nacos图形界面控制台上面新建配置文件DataID
bootstrap+application
测试
流程:nacos-config-client + TEST_GROUP + info + yaml
新建dev/test的Namespace
回到服务管理-服务列表查看
按照域名配置填写
YML
bootstrap
application
测试:
启动Nacos,3377
http://localhost:3377/config/info
此时找的是:dev命名空间下的+TEST_GROUP分组下的+dev前缀+yaml后缀文件
官方文档2.x:https://nacos.io/zh-cn/docs/v2/guide/admin/cluster-mode-quick-start.html
官网架构图(写的o(╥﹏╥)o)(vip:虚拟ip)
上图官网翻译,真实情况
说明:
集中式存储的方式来支持集群化部署,目前只支持MySQL的存储。
https://nacos.io/zh-cn/docs/v2/guide/admin/deployment.html
重点说明
步骤1:nacos-server-1.1.4\nacos\conf目录下找到sql脚本
nacos-mysql.sql
执行脚本:先创建一个数据库nacos_config(从脚本文件中查看,名字自己随便写一个也行)
步骤2:nacos-server-1.1.4\nacos\conf目录下找到application.properties(需要多加个时区否则报错:startup.cmd -m standalone
)
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?&serverTimezone=UTC&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=root
在bin目录中输入cmd,进入到命令行窗口
startup.cmd -m standalone
命令运行成功后直接访问:http://localhost:8848/nacos
效果:重启Nacos,可以看到是个全新的空记录界面,以前是记录进derby
测试:
这个虚拟机SpringCloud之前配置的有
条件有限只有一台电脑,如果配置成3个虚拟机占用内存太大。所以我们配置成伪集群,在当前一台虚拟机上配置成3个Nacos节点。
官网集群环境要求:
详情查看:我写的另一篇博客:https://blog.csdn.net/aa35434/article/details/124853852
进入到安装后的sbin目录:cd /usr/local/src/Nginx/sbin/
./nginx 启动
./nginx -s stop 快速停止
./nginx -v 查看 nginx 版本号
详情查看:我写的另一篇博客:https://blog.csdn.net/aa35434/article/details/124716035
设置的有开机自动启动
安装完成后使用sqlyog远程登录连接
下载步骤
https://github.com/alibaba/nacos/releases
nacos-server-1.4.6.tar.gz
上传到目录:/usr/local/src/Nacos/
进入到此目录,解压后安装:tar -zxvf nacos-server-1.4.6.tar.gz
查看目录:windows启动使用的是startup.cmd,linux执行的是startup.sh
在配置之前,先保存一份原始的配置,以防以后改错恢复配置时麻烦。(要养成这个习惯)
问题:原先只有一个Nacos,直接使用命令启动即可。现在有3个Nacos组成的集群,那么使用命令该如何区分启动的是集群中的哪一个Nacos呢???
解决:修改linux里面的nacos脚本,可以通过端口号进行区分。
第一个:和之前windows一样,在linux也需要进行Nacos持久化配置,目的是把数据由默认的derby迁移到MySQL。
查看linux下的Nacos中的SQL脚本:在安装后的conf目录下
把此文件的内容复制到Mysql中,执行此脚本:先创建好数据库
我这个地方使用sqlyog远程工具连接的linux下的mysql8.0
同样在conf目录下修改application.properties 配置
改之前先把这个文件备份下
#127.0.0.1代表的是linux系统下的本机
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos_config?&serverTimezone=UTC&characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=root
第二个:Linux服务器上nacos的集群配置cluster.conf
梳理出3台nacos集器的不同服务端口号
复制cluster.conf.example文件为cluster.conf:
cp cluster.conf.example cluster.conf
我们修改的是这个新复制的文件cluster.conf
192.168.10.140: 3333
192.168.10.140: 4444
192.168.10.140: 5555
hostname -i
能够识别的IP第三个:编辑Nacos的启动脚本startup.sh,使它能够接受不同的启动端口
/mynacos/nacos/bin 目录下有startup.sh
在什么地方,修改什么,怎么修改
思考
修改内容
59行:添加参数p,表示会执行p)分支
最后:明确告诉Nacos启动的是那台节点
新版:p已经被占用了,随便改一个字母为o
PORT=$OPTARG;;
-Dserver.port=${PORT}
执行方式:bin目录下
./startup.sh -o 3333
第一个:Nginx的配置,由它作为负载均衡器
修改nginx的配置文件:nginx安装路径的conf目录下的nginx.conf
文件
nginx.conf:修改之前
nginx.conf:修改之后
Nginx服务器指定启动配置文件命令:
截止到此处,1个Nginx+3个nacos注册中心+1个mysql
启动3台Nacos:/usr/local/src/Nacos/nacos/bin/
ps -ef|grep nacos | grep -v grep | wc -l
Nginx服务器指定启动配置文件命令:在/usr/local/nginx/sbin/目录下
./nginx -c /usr/local/nginx/conf/nginx.conf
查看进程:ps -ef | grep nginx
启动mysql:开机自动启动
测试通过nginx访问nacos:http://192.168.10.140:1111/nacos/#/login
默认账号密码都是nacos
新建一个配置测试
linux服务器的mysql插入一条记录
微服务cloudalibaba-provider-payment9002启动注册进nacos集群
#server-addr: localhost:8848 #配置Nacos地址 配置在windows本机上的
server-addr: 192.168.10.140:1111 #配置在linux上的nginx1111,由nginx在转发给3333 4444 5555Nacos
https://github.com/alibaba/Sentinel
中文:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
文档:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html#_spring_cloud_alibaba_sentinel
服务使用中的各种问题
Sentinel 分为两个部分:
下载
运行命令
注意tomact默认端口号也是8080
)java -jar
运行即可。java -jar sentinel-dashboard-1.8.6.jar
访问sentinel管理界面
http://localhost:8080
登录账号密码均为sentinel
启动
访问:http://localhost:8848/nacos/#/login
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.angenin.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-sentinel-service8401</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.angenin.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--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>
<!-- SpringBoot整合Web组件+actuator -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.6.3</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>
<scope>test</scope>
</dependency>
</dependencies>
</project>
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinal-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址(即:把此服务注册到注册中心8848?)
server-addr: 127.0.0.1:8848
sentinel:
transport:
#配置Sentin dashboard地址(配置sentinel8080监控8401服务)
dashboard: 127.0.0.1:8080
# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口
port: 8719 #指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer
management:
endpoints:
web:
exposure:
include: '*'
package com.angenin.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class, args);
}
}
package com.angenin.springcloud.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class FlowLimitController {
@GetMapping("/testA")
public String testA() {
return "------testA";
}
@GetMapping("/testB")
public String testB() {
return "------testB";
}
}
nacos已启动
sentinel控制台已启动
启动8401微服务后查看sentienl控制台
空空如也,啥都没有
Sentinel采用的懒加载机制,需要执行一次访问才能监控到。
http://localhost:8401/testB
多点几次testA查看效果:
结论:sentinel8080正在监控微服务8401
在图形化界面可以看到每一个微服务的restful风格的接口调用的访问情况。
属于流量限制控制规则,不是流程控制。
位置:
解释:
系统默认:直接->快速失败
即:那个资源触发了阈值,我就对那个资源进行限流。
配置及说明:
表示1秒钟内查询1次就是OK,若超过次数1,就直接-快速失败,报默认错误
测试1:QPS
快速点击访问http://localhost:8401/testA
结果:Blocked by Sentinel (flow limiting)
思考???
测试2:并发线程数
多次点击发现结果不变。
QPS和并发线程数的区别:QPS是请求没有进来之前就被阻挡了,并发线程数是随便进来但是里面能处理的只有一个线程。
在A方法中加上暂停时间:
多次点击,会出现报错:1秒钟内只允许一个线程进来
是什么
恢复代码:
配置A
postman模拟并发密集访问testB
访问testB成功
postman里新建多线程集合组
将访问地址添加进新新线程组
Run:大批量线程高并发访问B,导致A失效了
运行后发现testA挂了
等这20个线程跑完,再次访问恢复正常。
说明
官网:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8
源码
WarmUp配置
多次点击http://localhost:8401/testB,刚开始不行,后续慢慢OK
应用场景:
匀速排队,阈值必须设置为QPS
官网:https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6
源码:com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController
测试
https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7
没有半开
状态的
新版本1.8.0开始有半开状态了。
慢调用:
名词解释:
熔断降级:熔断降级是解决雪崩问题的重要手段。其思路是由断路器
统计服务调用的异常比例、慢请求比例如果超出阈值则会熔断
该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。
熔断策略:断路器想要从open变为close状态,需要判断服务有没有触发熔断的条件,而熔断条件的判断就是依据熔断策略完成的。
慢调用比例:看的是响应的时间,如果响应时间RT(response time)过长超过了指定时间,那么你这个调用就是慢调用,请求很慢就会调用额外的资源 会拖慢整个服务。
最大RT:响应时间,表示超过500毫秒的响应都算是慢调用。
比例阈值:慢调用的比超过了0.5达到一半以上就触发阈值了。
熔断时长:一旦熔断,熔断时长持续5秒,5秒后进入Half-Open半熔断状态。
最小请求数,统计时长:表示我会统计最近1秒内的至少10次请求,那么10次里面超过500ms的这种慢调用比例达到了一半以上,那么我就触发熔断,而熔断时间为5秒钟。
异常比例、异常数:
测试
@GetMapping("/testD")
public String testD() {
//暂停几秒钟线程
try {
TimeUnit.MILLISECONDS.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("testD 测试RT");
return "------testD";
}
配置:表示我会统计最近1秒内的至少5次请求,那么5次里面超过50ms的这种慢调用比例达到了2次以上,那么我就触发熔断,而熔断时间为5秒钟。
测试:不需要使用压测工具,自己点就可以,只要1秒钟5次请求只需要有2次触发就行了。
测试
@GetMapping("/testE")
public String testE() {
log.info("testD 测试RT");
int age = 10/0;
return "------testD";
}
@GetMapping("/testF")
public String testF() {
log.info("testD 测试RT");
int age = 10/0;
return "------testD";
}
https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81
兜底方法:分为系统默认和客户自定义,两种
@GetMapping("/testHotKey")
//value:资源的唯一标识,名字任意一般和上面的地址值保持一致。
@SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1,
@RequestParam(value = "p2",required = false) String p2){
return "------testHotKey";
}
public String dealHandler_testHotKey(String p1, String p2, BlockException exception) {
//sentinel系统默认的提示:Blocked by Sentinel (flow limiting)
return "-----dealHandler_testHotKey";
}
多次点击测试:方法testHotKey里面第一个参数只要QPS超过每秒1次,马上降级处理。用了我们自己定义的
error:http://localhost:8401/testHotKey?p1=abc
error:http://localhost:8401/testHotKey?p1=abc&p2=33
right:http://localhost:8401/testHotKey?p2=abc
如果使用默认的提示:异常打到了前台用户界面看到,不友好
上述案例演示了第一个参数p1,当QPS超过1秒1次点击后马上被限流
特例情况
配置:添加按钮不能忘
测试:多次点击
http://localhost:8401/testHotKey?p1=5
http://localhost:8401/testHotKey?p1=3
当p1等于5的时候,阈值变为200
当p1不等于5的时候,阈值就是平常的1
前提条件:热点参数的注意点,参数必须是基本类型或者String
手贱添加异常看看…/(ㄒoㄒ)/~~
效果:发现并没有执行自定义的提示方法。
@SentinelResource
处理的是Sentinel控制台配置的违规情况,有blockHandler方法配置的兜底处理;
RuntimeException
int age = 10/0,这个是java运行时报出的运行时异常RunTimeException,@SentinelResource不管
总结
@SentinelResource主管配置出错,运行出错该走异常走异常
后面讲:还有其它的配置来解决这个问题
是什么:之前是细粒度的从控制层的方法进行限流,现在是粗粒度的进行限流。
https://github.com/alibaba/Sentinel/wiki/%E7%B3%BB%E7%BB%9F%E8%87%AA%E9%80%82%E5%BA%94%E9%99%90%E6%B5%81
各项配置参数说明
配置全局QPS
多次访问:不管是访问p1还是p2,都会报错。(之前配置的是只有p1会报错)
pom:之前已引入
yml:没有改变
业务类RateLimitController
package com.angenin.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RateLimitController {
//既可以使用url地址来配置:/byResource
//也可以使用@SentinelResource注解中的value 属性来配置
@GetMapping("/byResource")
@SentinelResource(value = "byResource",blockHandler = "handleException")
public CommonResult byResource() {
return new CommonResult(200,"按资源名称限流测试OK",new Payment(2020L,"serial001"));
}
public CommonResult handleException(BlockException exception) {
return new CommonResult(444,exception.getClass().getCanonicalName()+"\t 服务不可用");
}
}
配置步骤
图形配置和代码关系
表示1秒钟内查询次数大于1,就跑到我们自定义的处流,限流
@GetMapping("/rateLimit/byUrl")
@SentinelResource(value = "byUrl")
public CommonResult byUrl() {
return new CommonResult(200,"按url限流测试OK",new Payment(2020L,"serial002"));
}
访问一次:http://localhost:8401/rateLimit/byUrl
Sentinel控制台配置
测试
package com.angenin.springcloud.myhandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.angenin.springcloud.entities.CommonResult;
public class CustomerBlockHandler {
public static CommonResult handleException1(BlockException exception){
return new CommonResult(2020,"自定义的限流处理信息......CustomerBlockHandler1");
}
public static CommonResult handleExceptio2(BlockException exception){
return new CommonResult(2020,"自定义的限流处理信息......CustomerBlockHandler2");
}
}
/**
* 自定义通用的限流处理逻辑,
* blockHandlerClass = CustomerBlockHandler.class
* blockHandler = handleException2
* 上述配置:找CustomerBlockHandler类里的handleException2方法进行兜底处理
*/
@GetMapping("/rateLimit/customerBlockHandler")
//指定哪一个全局类中的的哪一个方法
@SentinelResource(value = "customerBlockHandler",
blockHandlerClass = CustomerBlockHandler.class, blockHandler = "handleExceptio2")
public CommonResult customerBlockHandler() {
return new CommonResult(200,"按客户自定义限流处理逻辑");
}
启动微服务后先调用一次:http://localhost:8401/rateLimit/customerBlockHandler
Sentinel控制台配置
多次刷新后我们自定义的出来了
进一步说明
多说一句:除了默认的系统提示,和使用注解编写自定义提示外,他还支持使用代码的方式编写自定义提示规则。
Sentinel主要有三个核心Api
新建cloudalibaba-provider-payment9003/9004两个一样的做法
POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.angenin.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-provider-payment9003</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.angenin.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
<scope>test</scope>
</dependency>
</dependencies>
</project>
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
package com.angenin.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003
{
public static void main(String[] args) {
SpringApplication.run(PaymentMain9003.class, args);
}
}
package com.angenin.springcloud.controller;
import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
public static HashMap hashMap = new HashMap();
//模拟连接一个数据库
static {
hashMap.put(1L,new Payment(1L,"28a8c1e3bc2742d8848569891fb42181"));
hashMap.put(2L,new Payment(2L,"bba8c1e3bc2742d8848569891ac32182"));
hashMap.put(3L,new Payment(3L,"6ua8c1e3bc2742d8848569891xt92183"));
}
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult paymentSQL(@PathVariable("id") Long id) {
Payment payment = (Payment)hashMap.get(id);
CommonResult result = new CommonResult(200,"from mysql,serverPort: "+serverPort,payment);
return result;
}
}
新建cloudalibaba-consumer-nacos-order84
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.angenin.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloudalibaba-consumer-nacos-order84</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--SpringCloud ailibaba nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--SpringCloud ailibaba sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.angenin.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- SpringBoot整合Web组件 -->
<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>
<!--日常通用jar包配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
<scope>test</scope>
</dependency>
</dependencies>
</project>
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
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
package com.angenin.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class OrderNacosMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain84.class, args);
}
}
package com.angenin.springcloud.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
package com.angenin.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
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 {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
@SentinelResource(value = "fallback")//没有配置
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id, CommonResult.class,id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
}
测试地址:http://localhost:84/consumer/fallback/1
通过84消费者可以以负载均衡轮训的方式,访问9003/9004
没有任何配置:给客户error页面,不友好(在控制层方法中设置的访问1、2、3正常,4、5异常)
package com.angenin.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
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 {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback")//没有配置
@SentinelResource(value = "fallback", fallback = "handlerFallback") //fallback只负责业务异常
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id, CommonResult.class,id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
//本例是fallback
public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}
}
package com.angenin.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
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 {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback")//没有配置
//@SentinelResource(value = "fallback", fallback = "handlerFallback") //fallback只负责业务异常
@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id, CommonResult.class,id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
//本例是fallback
/* public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}*/
//本例是blockHandler
public CommonResult blockHandler(@PathVariable Long id, BlockException blockException) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}
}
配置
测试:http://localhost:84/consumer/fallback/4
package com.angenin.springcloud.controller;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
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 {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@RequestMapping("/consumer/fallback/{id}")
//@SentinelResource(value = "fallback")//没有配置
//@SentinelResource(value = "fallback", fallback = "handlerFallback") //fallback只负责业务异常
//@SentinelResource(value = "fallback",blockHandler = "blockHandler") //blockHandler只负责sentinel控制台配置违规
@SentinelResource(value = "fallback",fallback = "handlerFallback",blockHandler = "blockHandler") //都配置
public CommonResult<Payment> fallback(@PathVariable Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/"+id, CommonResult.class,id);
if (id == 4) {
throw new IllegalArgumentException ("IllegalArgumentException,非法参数异常....");
}else if (result.getData() == null) {
throw new NullPointerException ("NullPointerException,该ID没有对应记录,空指针异常");
}
return result;
}
//本例是fallback
public CommonResult handlerFallback(@PathVariable Long id,Throwable e) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(444,"兜底异常handlerFallback,exception内容 "+e.getMessage(),payment);
}
//本例是blockHandler
public CommonResult blockHandler(@PathVariable Long id, BlockException blockException) {
Payment payment = new Payment(id,"null");
return new CommonResult<>(445,"blockHandler-sentinel限流,无此流水: blockException "+blockException.getMessage(),payment);
}
}
配置
测试1:http://localhost:84/consumer/fallback/1
(访问正常)
第一次点击:正常访问
快速点击:超过阈值,会访问自定义的blockHandler方法
测试2:http://localhost:84/consumer/fallback/4
(访问异常)
第一次点击:返回fallback属性标注的自定义方法
快速点击:超过阈值,此时既满足fallback业务异常又满足blockHandler控制台配置违规,那么此时返回的结果是blockHandler属性指定的方法。
结论:若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出 BlockException 时只会进入 blockHandler 处理逻辑。
exceptionsToIgnore = {IllegalArgumentException.class}
84消费者调用提供者9003
Feign组件一般是消费侧
<!--SpringCloud openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
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
#消费者将要去访问的微服务名称(注册成功进nacos的微服务提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
# 激活Sentinel对Feign的支持
feign:
sentinel:
enabled: true
package com.angenin.springcloud.service;
import com.angenin.springcloud.entities.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* value:找到注册中心上的微服务接口名
* 过程:fallback:相当于去找nacos-payment-provider这个微服务的名字,去调用下面已有的方法,
* 假如出事了去调用PaymentFallbackService里面的方法
*/
@FeignClient(value = "nacos-payment-provider",fallback = PaymentFallbackService.class)//调用中关闭9003服务提供者
public interface PaymentService {
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult paymentSQL(@PathVariable("id") Long id);
}
package com.angenin.springcloud.service;
import com.angenin.springcloud.entities.CommonResult;
import com.angenin.springcloud.entities.Payment;
import org.springframework.stereotype.Component;
@Component
public class PaymentFallbackService implements PaymentService {
@Override
public CommonResult paymentSQL(Long id) {
return new CommonResult(444,"服务降级返回,没有该流水信息",new Payment(id, "errorSerial......"));
}
}
//==================OpenFeign
@Resource
private PaymentService paymentService;
@GetMapping(value = "/consumer/openfeign/{id}")
public CommonResult paymentSQL(@PathVariable("id") Long id) {
if(id == 4) {
throw new RuntimeException("没有该id");
}
return paymentService.paymentSQL(id);
}
package com.angenin.springcloud;
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
@SpringBootApplication
@EnableFeignClients //启用 激活
public class OrderNacosMain84 {
public static void main(String[] args) {
SpringApplication.run(OrderNacosMain84.class, args);
}
}
启动消费者84,启动生产者9003
http://localhost:84/consumer/openfeign/1(正确访问)
测试84调用9003,此时故意关闭9003微服务提供者
,看84消费侧自动降级,不会被耗死
Sentinel | Hystrix | resilience4j | |
---|---|---|---|
隔离策略 | 信号量隔离(并发线程数限流) | 线程池隔商/信号量隔离 | 信号量隔离 |
熔断降级策略 | 基于响应时间、异常比率、异常数 | 基于异常比率 | 基于异常比率、响应时间 |
实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(基于RxJava) | Ring Bit Buffer |
动态规则配置 | 支持多种数据源 | 支持多种数据源 | 有限支持 |
扩展性 | 多个扩展点 | 插件的形式 | 接口的形式 |
基于注解的支持 | 支持 | 支持 | 支持 |
限流 | 基于QPS,支持基于调用关系的限流 | 有限的支持 | Rate Limiter |
流量整形 | 支持预热模式匀速器模式、预热排队模式 | 不支持 | 简单的Rate Limiter模式 |
系统自适应保护 | 支持 | 不支持 | 不支持 |
控制台 | 提供开箱即用的控制台,可配置规则、查看秒级监控,机器发观等 | 简单的监控查看 | 不提供控制台,可对接其它监控系统 |
一旦我们重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化
将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效
<!--SpringCloud ailibaba sentinel-datasource-nacos 后续做持久化用到-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinal-service
cloud:
nacos:
discovery:
#Nacos服务注册中心地址(即:把此服务注册到注册中心8848?)
server-addr: 127.0.0.1:8848
sentinel:
transport:
#配置Sentin dashboard地址(配置sentinel8080监控8401服务)
dashboard: 127.0.0.1:8080
# 默认8719端口,假如被占用了会自动从8719端口+1进行扫描,直到找到未被占用的 端口
port: 8719 #指定应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer
datasource: #<---------------------------关注点,添加Nacos数据源配置
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
[
{
"resource": "/rateLimit/byUrl",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
控制层方法:之前的方法
配置流控规则
启动8401
乍一看还是没有,稍等一会儿
多次调用:http://localhost:8401/rateLimit/byUrl
刷新sentinel页面:重新配置出现了,持久化验证通过
阳哥讲的版本太老了,这里我已黑马的版本为例进行学习。
待发布
# Copyright 1999-2019 Seata.io Group.
#
# 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.
server:
port: 7091 #它是UI界面的端口号,不是对外提供服务的端口号
spring: #服务名
application:
name: seata-server
logging: #日志
config: classpath:logback-spring.xml
file:
path: ${user.home}/logs/seata
extend:
logstash-appender:
destination: 127.0.0.1:4560
kafka-appender:
bootstrap-servers: 127.0.0.1:9092
topic: logback_to_logstash
console: #平台界面的用户名密码
user:
username: seata
password: seata
seata:
config: #配置中心
# support: nacos, consul, apollo, zk, etcd3
type: file
registry: #注册中心
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: file
store: #存储中心
# support: file 、 db 、 redis
mode: file
# server: #这才是对外提供的端口号,默认是7091+1000=8091.所以不用配置
# service-port: 8091 #If not configured, the default is '${server.port} + 1000'
security: #安全
secretKey: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
tokenValidityInMilliseconds: 1800000
ignore:
urls: /,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.jpeg,/**/*.ico,/api/v1/auth/login
说明:此文件的修改都可以参考示例文件的内容。
修改之前先复制一份原始文件
修改存储中心
store:
# support: file 、 db 、 redis
mode: db
session:
mode: db
lock:
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.jdbc.Driver #如果是mysql8.0驱动为:com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true
user: root
password: root
min-conn: 10
max-conn: 100
global-table: global_table
branch-table: branch_table
lock-table: lock_table
distributed-lock-table: distributed_lock
query-limit: 1000
max-wait: 5000
修改配置中心
拷贝示例文件内容
修改application.yml
seata:
config:
# support: nacos, consul, apollo, zk, etcd3
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace:
group: fsp_tx_group #分组名称
username: nacos #连接Nacos平台的用户名密码默认都是nacos
password: nacos
context-path:
##if use MSE Nacos with auth, mutex with username/password attribute
#access-key:
#secret-key:
data-id: seataServer.properties #之后需要再nacos配置中心创建此文件。
修改注册中心
拷贝示例文件内容
修改application.yml
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
nacos:
application: seata-server #启动后注册到注册中心的服务名
server-addr: 127.0.0.1:8848
group: fsp_tx_group #组名保持一致
namespace:
cluster: default
username: nacos
password: nacos
context-path:
##if use MSE Nacos with auth, mutex with username/password attribute
#access-key:
#secret-key:
之前在application.yml文件中配置的是使用数据库来管理全局事务与分支事务,所以要创建库,在库中新建表。
脚本位置:注意不推荐从官网上找脚本,因为官网上更新的速度比较慢可能会导致版本不一致。推荐从下载的安装包查找。
创建seata数据库,并执行脚本
需要在nacos平台,配置此文件
配置内容来源:
复制一份原始文件,修改config.txt文件内容。
#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.enableTcServerBatchSendResponse=false
transport.rpcRmRequestTimeout=30000
transport.rpcTmRequestTimeout=30000
transport.rpcTcRequestTimeout=30000
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
transport.serialization=seata
transport.compressor=none
#Transaction routing rules configuration, only for the client
service.vgroupMapping.default_tx_group=default
#If you use a registry, you can ignore it
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=true
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.rm.sagaJsonParser=fastjson
client.rm.tccActionInterceptorOrder=-2147482648
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
client.tm.interceptorOrder=-2147482648
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
#For TCC transaction mode
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h
#Log rule configuration, for client and server
log.exceptionRate=100
#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.
store.mode=db
store.lock.mode=db
store.session.mode=db
#Used for password encryption
#store.publicKey=
#If `store.mode,store.lock.mode,store.session.mode` are not equal to `file`, you can remove the configuration block.
#store.file.dir=file_store/data
#store.file.maxBranchSessionSize=16384
#store.file.maxGlobalSessionSize=512
#store.file.fileWriteBufferCacheSize=16384
#store.file.flushDiskMode=async
#store.file.sessionReloadReadSize=100
#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=root
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.distributedLockTable=distributed_lock
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
#These configurations are required if the `store mode` is `redis`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `redis`, you can remove the configuration block.
#store.redis.mode=single
#store.redis.single.host=127.0.0.1
#store.redis.single.port=6379
#store.redis.sentinel.masterName=
#store.redis.sentinel.sentinelHosts=
#store.redis.maxConn=10
#store.redis.minConn=1
#store.redis.maxTotal=100
#store.redis.database=0
#store.redis.password=
#store.redis.queryLimit=100
#Transaction rule configuration, only for the server
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.distributedLockExpireTime=10000
server.xaerNotaRetryTimeout=60000
server.session.branchAsyncQueueSize=5000
server.session.enableBranchAsyncRemove=false
server.enableParallelRequestHandle=false
#Metrics configuration, only for the server
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
先启动Nacos端口号8848,再启动seata-server
seata-server.bat -m db
,默认是file所以要指定db。如果不是本机还要指定ip和端口号。