1、父工程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-insta ...
1、父工程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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<modules>
<module>order</module>
<module>stock</module>
</modules>
<groupId>com.wanqi</groupId>
<artifactId>springCloudAlibaba</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springCloudAlibaba</name>
<description>springCloudAlibaba</description>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
<spring-cloud.alibaba.version>2021.1</spring-cloud.alibaba.version>
<spring-cloud.alibaba.nacos.version>2021.1</spring-cloud.alibaba.nacos.version>
<spring-boot.version>2.7.2</spring-boot.version>
<spring-cloud.loadbalancer.version>3.1.3</spring-cloud.loadbalancer.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- 考慮到企業可能有自己的父工程,spring-boot父工程引用改到dependencyManagement -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring-cloud.alibaba.nacos.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 高版本移除了Ribbon,改用loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
<version>${spring-cloud.loadbalancer.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${spring-cloud.alibaba.nacos.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.1、nacos服務本地部署啟動
1.1.1、github下載nacos服務
1.1.2、解壓後修改啟動文件改為單機nacos/bin/startup.sh
解壓命令
tar -xvf nacos-server-2.1.0.tar.gz
# export MODE="cluster"
export MODE="standalone"
1.1.3、修改啟動埠nacos/conf/application.properties
1.1.4、啟動nacos通過nacos/bin/startup.sh
1.1.5、根據啟動提示找到日誌nacos/logs/start.out
#看到一下提示表示啟動成功
INFO Nacos started successfully in stand alone mode. use embedded storage
1.1.6、找到日誌中啟動logo的Console: http://192.168.110.57:8848/nacos/index.html使用瀏覽器打開,輸入預設用戶名密碼,均為:nacos
2、服務提供方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>springCloudAlibaba</artifactId>
<groupId>com.wanqi</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>stock</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>
2.1、application.yml配置文件
server:
port: 8082
---
spring:
cloud:
nacos:
server-addr: 39.105.101.111:12205
discovery:
ephemeral: true #是否臨時實例,false表示永久實例,掉線也不會消失
application:
name: stock-service
2.2、 編寫測試Controller
@RestController
@RequestMapping("/stock")
public class StockController {
@Value("${server.port}")
private String port;
@RequestMapping("/add")
public String add(){
return "ok---" + port;
}
}
3、 服務調用方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>springCloudAlibaba</artifactId>
<groupId>com.wanqi</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 高版本移除了Ribbon,改用loadbalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
</dependencies>
</project>
3.1、application.yml配置文件
server:
port: 8081
spring:
cloud:
nacos:
server-addr: 39.105.101.111:12205
discovery:
ephemeral: true #是否臨時實例,false表示永久實例,掉線也不會消失
application:
name: order-service
3.2、配置類,調用方註冊RestTemplate
@Configuration
public class AppConfig {
@Bean
@LoadBalanced //解決 java.net.UnknownHostException
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
3.3、controller使用RestTemplate
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/add")
public String add(){
return restTemplate.getForObject("http://stock-service/stock/add",String.class);
}
}
4、Nacos集群部署,並使用nginx實現負載均衡
- 虛擬機準備
- mysql準備,安裝並創建nacos庫
- nginx安裝
4.1、github下載nacos服務
4.1.1、解壓nacos並修改nacos/conf/application.properties文件
- 修改埠號,配置放開並修改資料庫相關數據
server.port=8849
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
db.url.0=jdbc:mysql://39.105.101.111:3306/nacos?characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai
db.user.0=wq
db.password.0=123456
4.2、資料庫表相關sql,nacos/conf/nacos-mysql.sql
4.3、修改啟動腳本nacos/bin/startup.sh
# *根據伺服器實際記憶體情況酌情修改*
JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
#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"
# *新增以下參數設置本機ip地址,雲伺服器如果不修改會預設多一個使用網卡ip註冊的節點*
JAVA_OPT="${JAVA_OPT} -Dnacos.server.ip=114.67.111.165"
4.5、在nacos/conf下新建cluster.conf文件配置集群
- 一臺機器模擬埠最好分散一點不容易翻車,多台的話預設8848即可
#單台機器模擬
114.67.111.165:8849
114.67.111.165:8851
114.67.111.165:8853
#多台虛擬機
172.16.156.134:8848
172.16.156.135:8848
172.16.156.136:8848
4.5、啟動並驗證是否成功參考單機模式
4.6、啟動成功後把這個nacos再複製2個修改埠然後啟動即可
4.7、nginx配置新增在http下
upstream nacosServer{
#nacos集群ip與埠
server 114.67.111.165:8849;
server 114.67.111.165:8851;
server 114.67.111.165:8853;
}
server {
#監聽埠
listen 12205;
#nginx伺服器ip
server_name 39.105.101.111;
location /nacos{
proxy_pass http://nacosServer/nacos;
}
# 靜態資源處理
location ~ .* {
proxy_pass http://nacosServer;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Fonwarded-For $proxy_add_x_forwarded_for;
}
}
4.8、訪問nginx配置的監聽埠與請求
http://39.105.101.111:12205/nacos
5、集成openfeign
5.1、服務調用方導入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
5.2、使用示例
5.2.1、服務提供方Controller
@RestController
public class StockController {
@Autowired
StockMapper stockMapper;
@PostMapping("/add")
public int add(@RequestParam("orderId") Long id) {
Stock select = stockMapper.select();
int x = stockMapper.update(id);
System.out.println(select);
return x;
}
@PostMapping(value = "/sqlFile")
public String genSqlFile(@RequestParam("sqlId")String sqlId,@RequestParam("sql")String sql){
MyTest test = new MyTest(sqlId,sql);
System.out.println(test);
return test.toString();
}
}
5.2.2、調用方Feign介面
/**
* name 指定調用服務提供方介面所對應的服務名
* path 指定調用介面所在StockController指定的@RequestMapping("/stock"),如果沒有則不需要
* contextId 多個feign介面使用@FeignClient註解調用同一個名稱的微服務時增加contextId屬性,
* 確保每個feignclient的contextId唯一
*/
@FeignClient(name = "stock-db",contextId = "stock-db")
public interface StockFeignService {
@PostMapping("/add")
public int add(@RequestParam("orderId") Long orderId);
@PostMapping(value = "/sqlFile")
public String genSqlFile(@RequestParam("sqlId")String sqlId,@RequestParam("sql")String sql);
5.2.3、使用@EnableFeignClients開啟Feign
@SpringBootApplication
@EnableDiscoveryClient //高版本可省略
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class);
}
}
5.2.4、調用方service
@RestController
public class OrderController {
@Autowired
StockFeignService stockFeignService;
@GetMapping("add2/{orderId}")
public int add2(@PathVariable("orderId") Long orderId) {
return stockFeignService.add(orderId);
}
@PostMapping(value = "/sqlFile")
public String genSqlFile(@RequestParam("sqlId")String sqlId,@RequestParam("sql")String sql){
MyTest test = new MyTest(sqlId,sql);
String msg = stockFeignService.genSqlFile(test.getSqlId(),test.getSql());
System.out.println(msg + "調用成功啦!!!");
return msg;
}
}
5.5、openfeign日誌配置示例
5.5.1、使用配置文件
feign:
client:
config:
# 全局使用default,指定feign預設使用feignName,如果配置了contextId必須使用contextId
stock: #@FeignClient(name = "stock-service",path = "/stock",contextId = "stock")
# 連接超時時間,預設2s
connectTimeout: 5000
# 請求處理超時時間,預設5秒
readTimeout: 3000
# 指定feign攔截器
requestInterceptors[0]: com.wanqi.interceptor.FeignInterceptor
# 指定日誌級別,需要開啟spring日誌
loggerLevel: BASIC
# 開啟Spring日誌
logging:
level:
com.wanqi.feign: debug #指定feign包的日誌級別為debug
5.5.2、使用註解
- 全局配置直接使用@Configuration
/**
* 全局配置直接使用 @Configuration
* 局部配置配合@FeignClient使用:configuration = FeignConfig.class
*/
@Configuration
public class FeignConfig {
/**
* NONE【性能最佳,適用於生產】:不記錄任何日誌(預設值)
* BASIC【適用於生產環境追蹤問題】:僅記錄請求方法、URL、響應狀態代碼以及執行時間。
* HEADERS:記錄BASIC級別的基礎上,記錄請求和響應的neader。
* FULL【比較適用於開發及測試環境定位問題】:記錄請求和響應的header、body和元數據。
*/
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
- 局部配置,去掉FeignConfig配置類上的@Configuration註解,在需要指定的FeignClient屬性上加上configuration = FeignConfig.class(FeignConfig配置類不要加@Configuration註解,否則這將是一個全局配置)。
/**
* name 指定調用rest介面所對應的服務名
* path 指定調用介面所在StockController指定的@RequestMapping("/stock")
* contextId 多個feign介面使用@FeignClient註解調用同一個名稱的微服務時增加contextId屬性,
* 確保每個feignclient的contextId唯一
* configuration 引入配置類
*/
@FeignClient(name = "stock-service",path = "/stock"
,contextId = "stock"
,configuration = FeignConfig.class
)
public interface StockFeignService {
// 介面方法對應調用服務的方法,即對應服務Controller裡面的方法名和參數一一對應
@RequestMapping("/add2")
String add2();
/* @RestController
@RequestMapping("/stock")
public class StockController {
@Value("${server.port}")
private String port;
@RequestMapping("/add2")
public String add2(){
System.out.println("add2");
return "add2---" + port;
}
*/
}
5.6、feign自定義攔截器,實現RequestInterceptor介面重寫apply方法,註冊bean
- 自定義攔截器
public class FeignInterceptor implements RequestInterceptor {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void apply(RequestTemplate requestTemplate) {
logger.info("--------------FeignInterceptor---------------" + requestTemplate.path());
logger.info("--------------FeignInterceptor---------------" + requestTemplate.request().url());
logger.info("--------------FeignInterceptor---------------" + requestTemplate.url());
}
}
- 配置類@Bean註冊,或者直接在自定義攔截器上加上@Component註解(全局配置)
/**
* 自定義攔截器
*/
@Bean
public FeignInterceptor feignInterceptor(){
return new FeignInterceptor();
}
- application.yml配置文件註冊(可以指定具體FeignClient)
feign:
client:
config:
# 全局使用default,指定feign預設使用feignName,如果配置了contextId必須使用contextId
stock: #@FeignClient(name = "stock-service",path = "/stock",contextId = "stock")
# 指定feign攔截器
requestInterceptors[0]: com.wanqi.interceptor.FeignInterceptor
- 使用FeignClient屬性configuration配置(可以指定具體FeignClient)
/**
* name 指定調用rest介面所對應的服務名
* path 指定調用介面所在StockController指定的@RequestMapping("/stock")
* contextId 多個feign介面使用@FeignClient註解調用同一個名稱的微服務時增加contextId屬性,
* 確保每個feignclient的contextId唯一
* configuration 引入配置類
*/
@FeignClient(name = "stock-service",path = "/stock"
,contextId = "stock"
,configuration = {FeignConfig.class, FeignInterceptor.class}
)
public interface StockFeignService {
// 介面方法對應調用服務的方法,即對應服務Controller裡面的方法名和參數一一對應
@RequestMapping("/add2")
String add2();
/* @RestController
@RequestMapping("/stock")
public class StockController {
@Value("${server.port}")
private String port;
@RequestMapping("/add2")
public String add2(){
System.out.println("add2");
return "add2---" + port;
}
*/
}
5.7、feign超時時間配置
- 配置類的方式
/**
* 超時時間配置
*/
@Bean
public Request.Options options(){
return new Request.Options(5, TimeUnit.SECONDS,10,TimeUnit.SECONDS,true);
}
- application.yml配置文件
feign:
client:
config:
# 全局使用default,指定feign預設使用feignName,如果配置了contextId必須使用contextId
product:
# 連接超時時間,預設2s
connectTimeout: 5000
# 請求處理超時時間,預設5秒
readTimeout: 3000
6、Nacos-config配置使用
- 建議所有配置文件的名字即data-id都加上文件擴展名如:test.properties,test.yml等
6.1、pom文件導入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- 新版nacos-config要使用bootstrap.yml必須引入 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
6.2、編寫bootstrap.yml文件
6.2.1、推薦模式根據namespace來控制不同環境的配置
spring:
cloud:
nacos:
config:
server-addr: 172.16.156.137:8847
# 指定命名空間,不明確配置的話,預設是public
namespace: dev
# 指定文件名即配置的Data ID
name: com.wanqi.config-mac.yml
username: nacos
password: nacos
# 配置文件類型
file-extension: yml
6.2.2、支持profile粒度的配置
配置文件名com.wanqi.config-mac.yml,必須顯示的指定文件格式
profile粒度的配置dataid即文件名格式:${spring.application.name}-${profile}.${file-extension:properties}
${spring.application.name}對應:com.wanqi.config,屬性名spring.cloud.nacos.config.name
${profile}對應:mac,屬性名springprofiles.active
${file-extension:properties}對應:yml,屬性名spring.cloud.nacos.config.file-extension
spring:
cloud:
nacos:
config:
server-addr: 172.16.156.137:8847
# 指定命名空間,不明確配置的話,預設是public
namespace: dev
#配置文件名即Data ID為com.wanqi.config-mac.yml
# 指定文件名${spring.application.name}=com.wanqi.config
name: com.wanqi.config
username: nacos
password: nacos
# 配置文件類型${file-extension:properties}=yml
file-extension: yml
# profile粒度${profile}=mac
profiles:
active: mac
6.2.3、多配置、共用配置bootstrap.yml
spring:
cloud:
nacos:
config:
server-addr: 172.16.156.137:8847
# 指定命名空間,不明確配置的話,預設是public
namespace: test
# 指定文件名
# name: com.wanqi.config
username: nacos
password: nacos
# 配置文件類型
file-extension: yml
# shared-configs[0]:
# data-id: test.properties
# refresh: true
# shared-configs[1]:
# data-id: test01.properties
# refresh: true
shared-configs:
- data-id: test.properties
# 不明確配置的話,預設是 DEFAULT_GROUP。
# group: DEFAULT_GROUP
# refresh是否支持應用中可動態刷新, 感知到最新的配置值。預設是不支持的。
refresh: true
# - data-id: test01.properties
# refresh: true
extension-configs:
- data-id: test01.properties
refresh: true
6.3、使用
6.3.1、硬編碼
@SpringBootApplication
public class NacosConfigApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(NacosConfigApplication.class, args);
String name = context.getEnvironment().getProperty("wq.name");
System.out.println(name);
}
}
6.3.2、註解@RefreshScope和 @Value
@Controller
@RefreshScope
public class NacosController {
@Value("${wq.name}")
private String name;
@Value("${wq.age}")
private int age;
@Value("${wq.msg}")
private String msg;
@ResponseBody
@RequestMapping("nacos")
public String nacos(){
return name + msg;
}
}
6.4、配置的優先順序
Spring Cloud Alibaba Nacos Config 目前提供了三種配置能力從 Nacos 拉取相關的配置。
A: 通過 spring.cloud.nacos.config.shared-configs[n].data-id 支持多個共用 Data Id 的配置
B: 通過 spring.cloud.nacos.config.extension-configs[n].data-id 的方式支持多個擴展 Data Id 的配置
C: 通過內部相關規則(應用名、應用名+ Profile )自動生成相關的 Data Id 配置
當三種方式共同使用時,他們的一個優先順序關係是:A < B < C
- 多個 Data Id 同時配置時,他的優先順序關係是 spring.cloud.nacos.config.extension-configs[n].data-id 其中 n 的值越大,優先順序越高。
7、sentinel集成
7.1、普通spring boot項目使用sentinel
7.1.1、引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- sentinel核心庫 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.4</version>
</dependency>
<!-- sentinel註解支持 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>1.8.4</version>
</dependency>
<!-- spring aop配合sentinel註解支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
7.1.2、定義規則可使用@PostConstruct註解
private static final String SENTINEL = "sentinel";
private static final String HELLO = "hello";
private static final String QIFENG = "qifeng";
@PostConstruct
private void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
//流控的資源
rule.setResource(SENTINEL);
//流控規則為QPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 設置QPS
rule.setCount(1);
rules.add(rule);
rule = new FlowRule();
//流控的資源
rule.setResource(HELLO);
//流控規則為QPS
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// 設置QPS
rule.setCount(1);
rules.add(rule);
FlowRuleManager.loadRules(rules);
initDegradeRule();
}
private void initDegradeRule() {
List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
rule.setResource(HELLO);
//設置規則,熔斷策略,支持慢調用比例/異常比例/異常數策略
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
// 熔斷時長,單位為 s, 10 ms
rule.setTimeWindow(10);
//熔斷觸發的最小請求數,請求數小於該值時即使異常比率超出閾值也不會熔斷
rule.setMinRequestAmount(2);
//統計時長(單位為 ms),如 60*1000 代表分鐘級
rule.setStatIntervalMs(60*1000);
//慢調用比例模式下為慢調用臨界 RT(超出該值計為慢調用);異常比例/異常數模式下為對應的閾值
rule.setCount(2);
rules.add(rule);
DegradeRuleManager.loadRules(rules);
}
7.1.3、定義資源
7.1.3.1、拋出異常的方式定義資源
//拋出異常的方式定義資源
@GetMapping("sentinel")
@ResponseBody
public String helloSentinel(){
try (Entry entry = SphU.entry(SENTINEL)) {
// 被保護的邏輯
return "hello sentinel";
} catch (BlockException ex) {
// 處理被流控的邏輯
return "請求被流控啦!!!!";
}
}
7.1.3.2、註解方式定義資源
//註解方式定義資源
// 原函數
@GetMapping("qifeng/{age}")
@ResponseBody
@SentinelResource(value = QIFENG, blockHandler = "exceptionHandler", fallback = "helloFallback")
public QiFeng qifeng(@PathVariable("age") int age) {
return new QiFeng("齊豐",age);
}
// Fallback 函數,異常處理,函數簽名與原函數一致或加一個 Throwable 類型的參數.
public QiFeng helloFallback(int age,Throwable throwable) {
throwable.printStackTrace();
return new QiFeng("異常啦!!!!", age);
}
// Block 異常處理函數,參數最後多一個 BlockException,其餘與原函數一致.
public QiFeng exceptionHandler(int age, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return new QiFeng("請求被流控啦!!!",age);
}
//註解方式定義資源
@GetMapping("hello")
@ResponseBody
@SentinelResource(value = HELLO,blockHandler = "hellBlockHandler")
public String hell(String name){
int x = 1/0;
return "hello DegradeRule!!!";
}
// Block 異常處理函數,參數最後多一個 BlockException,其餘與原函數一致.
public String hellBlockHandler(String name, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return "熔斷降級啦!!!!";
}
7.1.3.3、註解需要將SentinelResourceAspect註冊一個為Spring Bean
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
7.2、Spring Cloud整合控制台sentinel、nacos、openfeign
7.2.1、下載控制台jar包並啟動
java -Dserver.port=8080 -jar sentinel-dashboard.jar
7.2.2、引入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 解決 java.net.UnknownHostException -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
7.2.3、yaml配置
server:
port: 7779
spring:
main:
allow-circular-references: true
cloud:
sentinel:
transport:
# sentinel控制臺地址
dashboard: 172.16.156.139:9999
port: 8720
web-context-unify: false # 關閉預設調用鏈路收斂
# sentinel持久化配置
datasource:
flowrule: #可以自定義
nacos:
serverAddr: 172.16.156.139:8847
username: nacos
password: nacos
groupId: SENTINEL_GROUP
# nacos上配置文件的dataId
dataId: order-feign-sentinel-win-flowrule
# 規則分類
RuleType: flow
nacos:
server-addr: 172.16.156.139:8847
discovery:
ephemeral: true #是否臨時實例,false表示永久實例,掉線也不會消失
username: nacos
password: nacos
application:
name: order-feign-sentinel
---
feign:
client:
config:
# 全局使用default,指定feign預設使用feignName,如果配置了contextId必須使用contextId
stock:
# 連接超時時間,預設2s
connectTimeout: 5000
# 請求處理超時時間,預設5秒
readTimeout: 3000
# 指定feign攔截器
# requestInterceptors[0]: com.wanqi.interceptor.FeignInterceptor
loggerLevel: Full
sentinel:
# 打開 Sentinel 對 Feign 的支持
enabled: true
---
logging:
level:
com.wanqi.feign: debug
---
7.2.4、示例代碼
7.2.4.1、feign介面配置與回調
public class FeignConfig {
@Bean
public StockFeignServiceFallback stockFeignServiceImpl(){
return new StockFeignServiceFallback();
}
}
/**
* name 指定調用rest介面所對應的服務名
* path 指定調用介面所在StockController類上指定的@RequestMapping("/stock")
* contextId 多個feign介面使用@FeignClient註解調用同一個名稱的微服務時增加contextId屬性,
* 確保每個feignclient的contextId唯一
* configuration 引入配置類
*/
@FeignClient(name = "stock-service", path = "/stock"
, contextId = "stock"
, fallback = StockFeignServiceFallback.class
, configuration = {FeignConfig.class}
)
public interface StockFeignService {
@RequestMapping("/test1")
String test1();
@RequestMapping("/test2")
String test2();
@RequestMapping("/get/{id}")
public String getById(@PathVariable("id") int id);
}
public class StockFeignServiceFallback implements StockFeignService{
@Override
public String test1() {
return "test1熔斷降級啦!!!!";
}
@Override
public String test2() {
return "test2熔斷降級啦!!!!";
}
@Override
public String getById(int id) {
return "介面異常熔斷降級啦!!!!";
}
}
7.2.4.2、controller層
@RestController
public class OrderController {
@Autowired
private StockFeignService stockFeignService;
@GetMapping("/test1")
public String test1(){
return stockFeignService.test1();
}
@GetMapping("/test2")
public String test2(){
return stockFeignService.test2();
}
/**
* 熱點參數流控須要使用Sentinel
* @param id
* @return
*/
@RequestMapping("/get/{id}")
@SentinelResource(value = "getById",blockHandler = "getByIdBlockHandler")
public String getById(@PathVariable("id") int id){
return stockFeignService.getById(id);
}
public String getByIdBlockHandler(int id, BlockException e){
return "getByIdBlockHandler--熱點異常處理" + id;
}
}
7.2.4.3、啟動類
@SpringBootApplication
// 開啟nacos
@EnableDiscoveryClient
// 開啟feign
@EnableFeignClients
public class OrderFeignSentinelApplication {
public static void main(String[] args) {
SpringApplication.run(OrderFeignSentinelApplication.class,args);
}
}
7.2.4.4、nacos實現動態規則配置
7.2.4.4.1、yaml配置增加
spring:
cloud:
sentinel:
datasource:
flowrule: #可以自定義
nacos:
serverAddr: 172.16.156.139:8847
username: nacos
password: nacos
groupId: SENTINEL_GROUP
dataId: order-feign-sentinel-win-flowrule
RuleType: flow
@GetMapping("/sentinel/nacos")
@SentinelResource(value = "sentinelNacos",blockHandler = "sentinelNacosBlockHandler"
, fallback ="sentinelNacosFallback")
public String sentinelNacos(){
return "sentinelNacos正常返回!!!";
}
// Fallback 函數,異常處理,函數簽名與原函數一致或加一個 Throwable 類型的參數.
public String sentinelNacosFallback(Throwable throwable) {
return "Fallback: "+ throwable.getMessage();
}
// Block 異常處理函數,參數最後多一個 BlockException,其餘與原函數一致.
public String sentinelNacosBlockHandler(BlockException ex) {
// Do some log here.
return "BlockHandler: "+ ex.getMessage();
}
8、Spring Cloud Gateway
- 可以使用nginx實現負載均衡
8.1、引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--spring cloud gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- spring cloud gateway整合sentinel的依賴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!-- sentinel的依賴-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 解決 java.net.UnknownHostException -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
8.2、yaml配置
server:
port: 9527
spring:
application:
name: api-gateway
cloud:
## 整合sentinel,配置sentinel控制台的地址
sentinel:
transport:
## 指定控制台的地址,預設埠8080
dashboard: 172.16.156.139:9999
port: 8722
nacos:
## 註冊中心配置
discovery:
# nacos的服務地址,nacos-server中IP地址:埠號
server-addr: 172.16.156.139:8847
username: nacos
password: nacos
gateway:
## 路由
routes:
## id只要唯一即可,名稱任意
- id: gateway-provider
# 需要轉發的地址
#lb://service-name
#lb:uri 的協議,表示開啟 Spring Cloud Gateway 的負載均衡功能。
#service-name:服務名,Spring Cloud Gateway 會根據它獲取到具體的微服務地址。
uri: lb://stock-service
## 配置斷言
predicates:
# Path Route Predicate Factory斷言,滿足/stock-service/**這個請求路徑的都會被路由到stock-service這個服務中
# 自定義首碼避免介面重覆
- Path=/stock-service/**
# 過濾器,轉發之前過濾掉第一層路徑即stock-service/
# http://localhost:9527/stock-service/stock/get/1 轉發到stock-service服務上,http://stock-service/stock/get/1
filters:
- StripPrefix=1
8.3、訪問http://localhost:9527/stock-service/stock/get/1會自動路由到stock-service服務上
8.4、自定義全局流控異常
@Configuration
public class GatewayConfig {
@PostConstruct
public void init(){
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map<String,Object> map = new HashMap<>();
map.put("code", HttpStatus.TOO_MANY_REQUESTS.value());
map.put("msg", "請求太多,限流了");
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(map);
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
9、seata使用
9.1、部署steata服務並啟動
9.1.1、資料庫準備
- 資源地址
- client
存放client端sql腳本 (包含 undo_log表) ,參數配置
- config-center
各個配置中心參數導入腳本,config.txt(包含server和client,原名nacos-config.txt)為通用參數文件
- server
server端資料庫腳本 (包含 lock_table、branch_table 與 global_table) 及各個容器配置
9.1.2、下載seata服務
9.1.3、執行sql腳本seata/script/server/db/mysql.sql
-- -------------------------------- The script used when storeMode is 'db' --------------------------------
-- the table to store GlobalSession data
CREATE TABLE IF NOT EXISTS `global_table`
(
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`application_id` VARCHAR(32),
`transaction_service_group` VARCHAR(32),
`transaction_name` VARCHAR(128),
`timeout` INT,
`begin_time` BIGINT,
`application_data` VARCHAR(2000),
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`xid`),
KEY `idx_status_gmt_modified` (`status` , `gmt_modified`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- the table to store BranchSession data
CREATE TABLE IF NOT EXISTS `branch_table`
(
`branch_id` BIGINT NOT NULL,
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`resource_group_id` VARCHAR(32),
`resource_id` VARCHAR(256),
`branch_type` VARCHAR(8),
`status` TINYINT,
`client_id` VARCHAR(64),
`application_data` VARCHAR(2000),
`gmt_create` DATETIME(6),
`gmt_modified` DATETIME(6),
PRIMARY KEY (`branch_id`),
KEY `idx_xid` (`xid`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
-- the table to store lock data
CREATE TABLE IF NOT EXISTS `lock_table`
(
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(128),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`status` TINYINT NOT NULL DEFAULT '0' COMMENT '0:locked ,1:rollbacking',
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_status` (`status`),
KEY `idx_branch_id` (`branch_id`),
KEY `idx_xid_and_branch_id` (`xid` , `branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
CREATE TABLE IF NOT EXISTS `distributed_lock`
(
`lock_key` CHAR(20) NOT NULL,
`lock_value` VARCHAR(20) NOT NULL,
`expire` BIGINT,
primary key (`lock_key`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('AsyncCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryCommitting', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('RetryRollbacking', ' ', 0);
INSERT INTO `distributed_lock` (lock_key, lock_value, expire) VALUES ('TxTimeoutCheck', ' ', 0);
9.1.4、修改配置文件seata/conf/application.yml
server:
port: 7091
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: nacos
nacos:
server-addr: 172.16.156.139:8847
namespace: 170ee694-566d-4c3a-b316-fe233d984d43
group: SEATA_GROUP
username: nacos
password: nacos
##if use MSE Nacos with auth, mutex with username/password attribute
#access-key: ""
#secret-key: ""
# data-id: seataServer.properties
registry:
# support: nacos, eureka, redis, zk, consul, etcd3, sofa
type: nacos
# preferred-networks: 30.240.*
nacos:
application: seata-server
server-addr: 172.16.156.139:8847
group: SEATA_GROUP
namespace: 170ee694-566d-4c3a-b316-fe233d984d43
cluster: default
username: nacos
password: nacos
store:
# support: file 、 db 、 redis
mode: db
# server:
# 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,/**/*.ico,/console-fe/public/**,/api/v1/auth/login
9.1.5、修改seata/script/config-center/config.txt文件中資料庫連接
# 改為db
store.mode=db
store.lock.mode=db
store.session.mode=db
# 改為對應的資料庫連接地址
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://114.67.111.175:3306/seata_server?useUnicode=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&useSSL=false
store.db.user=root
store.db.password=123456
9.1.6、進入seata/script/config-center/nacos執行腳本上傳配置到nacos
./nacos-config.sh -h 172.16.156.139 -p 8847 -g SEATA_GROUP -t 170ee694-566d-4c3a-b316-fe233d984d43 -u nacos -w nacos
- -h: 註冊註冊中心的ip
- -p:註冊中心的埠
- -g: 註冊到註冊中心的Group
- -t: 註冊到註冊中心的命名空間
- -u: 註冊中心的賬號
- -w: 註冊中心的密碼
9.1.7、進入seata/bin啟動seata服務
./seata-server.sh -h 127.0.0.1 -p 8091
- -h:seata服務地址
- -p:監聽埠,從8091開始
9.2、客戶端client
- 準備在需要開啟分散式事務的各資料庫新建undo_log表,主要通過 BeforeImage 和 AfterImage 保存前後邏輯 , 用於回退處理,使用at模式
-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT NOT NULL COMMENT 'branch transaction id',
`xid` VARCHAR(128) NOT NULL COMMENT 'global transaction id',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
`rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info',
`log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status',
`log_created` DATETIME(6) NOT NULL COMMENT 'create datetime',
`log_modified` DATETIME(6) NOT NULL COMMENT 'modify datetime',
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
AUTO_INCREMENT = 1
DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
9.2.1、導入依賴
9.2.1.1、服務提供方
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.11</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
9.2.1.2、服務調用方
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.11</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>