SpringCloudAlibaba的學(xué)習(xí)筆記
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
76套java從入門到精通實(shí)戰(zhàn)課程分享
Nacos學(xué)習(xí)
Naming Configuration 前兩個(gè)字母, Service
是什么?
一個(gè)更易于構(gòu)建云原生應(yīng)用的動(dòng)態(tài)服務(wù)發(fā)現(xiàn),配置管理和服務(wù)管理中心
Dynamic Naming and Configuration Service
Nacos就是注冊(cè)中心+配置中心的組合 Eureka+Config+Bus
能干嗎?
替代Eureka做服務(wù)注冊(cè)中心
替代Config做服務(wù)配置中心
去哪下?
https://github.com/alibaba/Nacos
https://nacos.io/zh-cn/docs/
各種注冊(cè)中心比較
| 服務(wù)注冊(cè)與發(fā)現(xiàn)框架 | CAP模型 | 控制臺(tái)管理 | 社區(qū)活躍度 |
|---|---|---|---|
| Eureka | AP | 支持 | 低 |
| Zookeeper | CP | 不支持 | 中 |
| Consul | CP | 支持 | 高 |
| Nacos | AP | yes | high |
安裝并允許Nacos
本地Jdk 8 + Maven環(huán)境
從官網(wǎng)去下載?https://github.com/alibaba/Nacos
在windows環(huán)境下,解壓安裝包,直接允許bin目錄下的 startup.cmd
運(yùn)行成功之后直接訪問 http://localhost:8848/nacos 就可以看到
基于Nacos的服務(wù)提供者
新建一個(gè)項(xiàng)目,由于這個(gè)是一個(gè)微服務(wù),所以我們可以創(chuàng)建一個(gè)父項(xiàng)目來進(jìn)行統(tǒng)一的版本管理
"1.0"?encoding="UTF-8"?>
"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">
????4.0.0
????com.atguigu
????cloud2021
????pom
????1.0-SNAPSHOT
????
????????cloudalibaba-provider-payment9001
????????cloudalibaba-provider-payment9002
????????cloudalibaba-consumer-nacos-order83
????????cloudalibaba-config-nacos-client3377
????
????
????
????????UTF-8
????????1.8
????????1.8
????????4.12
????????1.2.17
????????1.16.18
????????8.0.18
????????1.1.16
????????1.3.0
????
????
????
????????
????????????
????????????
????????????????org.springframework.boot
????????????????spring-boot-dependencies
????????????????2.2.2.RELEASE
????????????????<type>pomtype>
????????????????import
????????????
????????????
????????????
????????????????org.springframework.cloud
????????????????spring-cloud-dependencies
????????????????Hoxton.SR1
????????????????<type>pomtype>
????????????????import
????????????
????????????
????????????
????????????????com.alibaba.cloud
????????????????spring-cloud-alibaba-dependencies
????????????????2.1.0.RELEASE
????????????????<type>pomtype>
????????????????import
????????????
????????????
????????????
????????????????mysql
????????????????mysql-connector-java
????????????????${mysql.version}
????????????
????????????
????????????
????????????????com.alibaba
????????????????druid-spring-boot-starter
????????????????${druid.verison}
????????????
????????????
????????????
????????????????org.mybatis.spring.boot
????????????????mybatis-spring-boot-starter
????????????????${mybatis.spring.boot.verison}
????????????
????????????
????????????
????????????????org.projectlombok
????????????????lombok
????????????????${lombok.version}
????????????
????????????
????????????
????????????????junit
????????????????junit
????????????????${junit.version}
????????????
????????????
????????????
????????????????log4j
????????????????log4j
????????????????${log4j.version}
????????????
????????
????
????
????????
????????????
????????????????org.springframework.boot
????????????????spring-boot-maven-plugin
????????????????
????????????????????true
????????????????????true
????????????????
????????????
????????
????
需要導(dǎo)入nacos的包,聲明一下可以被發(fā)現(xiàn)
????
????????
????????????com.alibaba.cloud
????????????spring-cloud-starter-alibaba-nacos-discovery
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-web
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-actuator
????????
????????
????????????org.springframework.boot
????????????spring-boot-devtools
????????????runtime
????????????true
????????
????????
????????????org.projectlombok
????????????lombok
????????????true
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-test
????????????test
????????
????????
????????????com.alibaba
????????????fastjson
????????????1.2.62
????????
????
需要寫配置文件,聲明這是一個(gè)需要被管理的服務(wù),需要被哪個(gè)地址進(jìn)行管理,需要被管理哪些東西
server:
??port:?9001
spring:
??application:
????name:?nacos-payment-provider
??cloud:
????nacos:
??????discovery:
????????server-addr:?localhost:8848?#配置Nacos地址
management:
??endpoints:
????web:
??????exposure:
????????include:?'*'
主啟動(dòng)類也要使用注解聲明一下,這個(gè)服務(wù)可以作為一個(gè)客戶端被Nacos發(fā)現(xiàn)
@EnableDiscoveryClient
@SpringBootApplication
public?class?PaymentMain9001?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(PaymentMain9001.class,?args);
????}
}
業(yè)務(wù)類我們可以寫一個(gè)方法進(jìn)行測(cè)試,將本機(jī)的端口暴露出去
@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;
????}
}
啟動(dòng)這個(gè)服務(wù)提供者,發(fā)現(xiàn)nacos控制臺(tái)可以看到,安裝同樣的方式,我們?cè)賮硪粋€(gè)9002的服務(wù)提供者,它們的服務(wù)名字都一樣,但是端口卻不一樣,也就是說這是兩個(gè)端口,這兩個(gè)端口都可以提供同一個(gè)服務(wù),
接下來是服務(wù)消費(fèi)者
????
????????
????????
????????????com.alibaba.cloud
????????????spring-cloud-starter-alibaba-nacos-discovery
????????
????????
????????????com.atguigu.springcloud
????????????cloud-api-commons
????????????${project.version}
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-web
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-actuator
????????
????????
????????????org.springframework.boot
????????????spring-boot-devtools
????????????runtime
????????????true
????????
????????
????????????org.projectlombok
????????????lombok
????????????true
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-test
????????????test
????????
????
配置文件
server:
??port:?83
spring:
??application:
????name:?nacos-order-consumer
??cloud:
????nacos:
??????discovery:
????????server-addr:?localhost:8848
service-url:
??nacos-user-service:?http://nacos-payment-provider
這個(gè)主啟動(dòng)類和提供者差不多
@EnableDiscoveryClient
@SpringBootApplication
public?class?OrderNacosMain83?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(OrderNacosMain83.class,?args);
????}
}
在服務(wù)調(diào)用者這里,我們可以使用一個(gè)模板進(jìn)行調(diào)用,這個(gè)模板去調(diào)用提供者的方法,首先它們應(yīng)該知道提供者的服務(wù)名稱,然后還有它們的端口,自己需要準(zhǔn)備參數(shù),這些我們都可以獲取到,我們還需要得到返回值,
由于提供者有很多個(gè),我們需要確定到底使用的是哪一個(gè),這也是一個(gè)問題,這個(gè)時(shí)候需要使用到負(fù)載均衡策略,
@Configuration
public?class?ApplicationContextConfig?{
????@Bean
????@LoadBalanced
????public?RestTemplate?getRestTemplate()?{
????????return?new?RestTemplate();
????}
}
往容器中注入的這個(gè)Bean上面又聲明了LoadBalanced注解,這表明我們的RestTemplate使用了一種負(fù)載均衡策略
然后我們?cè)谙M(fèi)者端就可以輪流調(diào)用提供者提供的服務(wù)了
@RestController
@Slf4j
public?class?OrderNacosController?{
????@Resource
????private?RestTemplate?restTemplate;
????@Value("${service-url.nacos-user-service}")
????private?String?serverURL;
????@GetMapping(value?=?"/consumer/payment/nacos/{id}")
????public?String?paymentInfo(@PathVariable("id")?Long?id)?{
????????return?restTemplate.getForObject(serverURL?+?"/payment/nacos/"?+?id,?String.class);
????}
}
Nacos 其實(shí)是可以支持CP或者AP的,這兩者之間可以相互切換
C是所有節(jié)點(diǎn)在同一時(shí)間看到的數(shù)據(jù)是一致的,而A的定義是所有的請(qǐng)求都會(huì)收到響應(yīng)
如果不需要存儲(chǔ)服務(wù)級(jí)別的信息且服務(wù)實(shí)例是通過nacos-client注冊(cè),并能夠保持心跳上報(bào),那么就可以選擇AP默認(rèn)當(dāng)前主流的服務(wù)如SPring Cloud和Dubbo服務(wù),都適合用于AP模式,AP模式為了服務(wù)的可能性而減弱了一致性,因此AP模式下只支持注冊(cè)臨時(shí)實(shí)例
如果需要在服務(wù)級(jí)別編寫或存儲(chǔ)配置信息,那么CP是必須的, k8s和DNS服務(wù)則適用于CP模式
CP模式下支持注冊(cè)持久化實(shí)例,此時(shí)則是以Raft協(xié)議為集群運(yùn)行模式該模式下注冊(cè)實(shí)例之前需先注冊(cè)服務(wù),如果服務(wù)不存在,則會(huì)返回錯(cuò)誤
curl -X PUT ‘$NACOS_SERVER:8848/nacos/v1/ns/operator/switches?entry=serverMode&value=CP’
Nacos作為服務(wù)配置中心
我們可以在Nacos上搭建自己的配置文件,在自己的服務(wù)中聲明自己想要使用哪個(gè)即可
依賴引入
????
????????
????????
????????????com.alibaba.cloud
????????????spring-cloud-starter-alibaba-nacos-config
????????
????????
????????
????????????com.alibaba.cloud
????????????spring-cloud-starter-alibaba-nacos-discovery
????????
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-web
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-actuator
????????
????????
????????
????????????org.springframework.boot
????????????spring-boot-devtools
????????????runtime
????????????true
????????
????????
????????????org.projectlombok
????????????lombok
????????????true
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-test
????????????test
????????
????
配置文件需要配置兩個(gè)
為什么需要兩個(gè)呢?
Nacos 和SpringCloud-config一樣,在項(xiàng)目初始化時(shí),需要先去配置中心進(jìn)行配置拉取,然后才能保證項(xiàng)目的正常啟動(dòng)
配置文件的加載存在優(yōu)先順序, bootstrap優(yōu)先級(jí)高于application
在遠(yuǎn)程配置的配置文件的命名格式都需要遵循一定的規(guī)則,要不然時(shí)無法獲取到的
server:
??port:?3377
spring:
??application:
????name:?nacos-config-client
??cloud:
????nacos:
??????discovery:
????????server-addr:?localhost:8848?#服務(wù)注冊(cè)中心地址
??????config:
????????server-addr:?localhost:8848?#配置中心地址
????????file-extension:?yaml?#指定yaml格式的配置
?
#?application.yaml
spring:
??profiles:
????active:?dev
#?${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
#?nacos-config-client-dev.yml
主啟動(dòng)類無變化
@EnableDiscoveryClient
@SpringBootApplication
public?class?NacosConfigClientMain3377?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(NacosConfigClientMain3377.class,?args);
????}
}
然后我們暴露一個(gè)端口,就可以看到在遠(yuǎn)程配置文件上面的信息,并且我們也可以設(shè)置自動(dòng)刷新,這樣當(dāng)遠(yuǎn)程修改的時(shí)候,我們這個(gè)端口可以立即看到
@RestController
@RefreshScope
public?class?ConfigClientController?{
????@Value("${config.info}")
????private?String?configInfo;
????@GetMapping("/config/info")
????public?String?getConfigInfo()?{
????????return?configInfo;
????}
}
主要有以下一些方面可以可以更換配置文件,
spring.application.name 項(xiàng)目的名稱
spring.profile.active 項(xiàng)目的環(huán)境,一般有dev test等
spring.cloud.nacos.config.file-extension 項(xiàng)目的擴(kuò)展文件名
此外,還有g(shù)roup,namespace等
server:
??port:?3377
spring:
??application:
????name:?nacos-config-client
??cloud:
????nacos:
??????discovery:
????????server-addr:?localhost:8848?#服務(wù)注冊(cè)中心地址
??????config:
????????server-addr:?localhost:8848?#配置中心地址
????????file-extension:?yaml?#指定yaml格式的配
????????group:?TEST_GROUP
????????namespace:?76fce2f9-7d68-4729-a7d5-b6861295483d
#?${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}
#?nacos-config-client-dev.yml
以上的這些信息可以唯一確定一個(gè)配置文件
Nacos持久化配置
這樣一個(gè)微服務(wù)項(xiàng)目的配置文件一般都比較重要,如果萬一掛掉了呢,所以我們需要非常謹(jǐn)慎的去處理配置信息,Nacos默認(rèn)自帶的嵌入式數(shù)據(jù)庫,derby,
我們也可以使用MySQL對(duì)配置文件進(jìn)行持久化
在config配置文件下有sql腳本,專門建立配置文件的數(shù)據(jù)表信息,
修改application.properties ,咋i最后面加上一些信息
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=123456
下次就可以去mysql數(shù)據(jù)庫里面找響應(yīng)的消息了,
如果要做持久化,我們僅僅需要一個(gè)mysql可能還不夠,我們還需要高可用,把Nacos做一個(gè)集群,一個(gè)掛了另外一個(gè)可以頂上,它們共同使用一個(gè)mysql或者一個(gè)mysql的集群,
我們模擬一下使用場(chǎng)景, 1個(gè)Nginx,+3個(gè)Nacos注冊(cè)中心 + 1個(gè)Mysql
我們先確定3臺(tái)Nacos機(jī)器的不同服務(wù)端口號(hào)
然后修改cluster.conf, 上面列出這個(gè)集群中所有的Nacos機(jī)器,
再編寫 Nacos的啟動(dòng)腳本 startup.sh ,使我們可以以不同的端口啟動(dòng)
修改內(nèi)容有些亂七八糟的,這里就不想再羅列出來了,等會(huì)兒說一個(gè)更加合適的方法
另外我們的nginx也需要進(jìn)行配置一下,然后暴露出來一個(gè)端口,監(jiān)聽集群里面的端口,然后負(fù)載均衡的分配給這些端口里面某一個(gè)確定的端口
upstream?cluster{????????????????????????????????????????????????????????
?
????server?127.0.0.1:3333;
????server?127.0.0.1:4444;
????server?127.0.0.1:5555;
}
server{
??????????????????????????
????listen?1111;
????server_name?localhost;
????location?/{
?????????proxy_pass?http://cluster;
????????????????????????????????????????????????????????
????}
啟動(dòng)nginx和我們的集群里面的端口即可
我們?cè)诳蛻舳诵薷囊幌路?wù)器的ip,修改為我們nginx暴露出來的端口即可
或者我們復(fù)制三個(gè)nacos,然后它們的cluster.xml 都是一樣的,然后再把各自的端口修改一下,全部啟動(dòng)即可
Sentinel學(xué)習(xí)
Sentinel 主要提供 流量控制,熔斷降級(jí),系統(tǒng)負(fù)載保護(hù),
可以理解為和Hystrix 的功能一致,但是這個(gè)比較優(yōu)秀的是它可以 進(jìn)行配置,
我們都知道,一般都是約定大于配置,配置大于編碼,這就是它比較優(yōu)秀的地方,可以再很多場(chǎng)景下面進(jìn)行詳細(xì)的配置
服務(wù)使用中的各種問題:
服務(wù)雪崩
服務(wù)降級(jí)
服務(wù)熔斷
服務(wù)限流
Sentinel分為兩個(gè)部分
核心庫,不依賴任何框架庫,能夠運(yùn)行于所有Java運(yùn)行環(huán)境,同時(shí)對(duì)Dubbo、 SpringCloudd等框架也有比較好的支持
控制臺(tái),基于SpringBoot開發(fā),打包后可以直接運(yùn)行,不需要web服務(wù)器
我們?nèi)ス倬W(wǎng)上面下載jar包,就可以使用java命令進(jìn)行運(yùn)行了,不過運(yùn)行時(shí)需要注意,確保jdk8,8080端口不能被占用,然后就可以訪問了
假設(shè)現(xiàn)在nacos和sentinel都已經(jīng)啟動(dòng)了,現(xiàn)在我們每創(chuàng)建一個(gè)微服務(wù)里面的接口都需要被監(jiān)控到,
需要做如下配置,我們現(xiàn)在創(chuàng)建一個(gè)8401端口的服務(wù),看一下具體情況,
pom
????
????????
????????????com.atguigu.springcloud
????????????cloud-api-commons
????????????${project.version}
????????
????????
????????????com.alibaba.cloud
????????????spring-cloud-starter-alibaba-nacos-discovery
????????
????????
????????????com.alibaba.csp
????????????sentinel-datasource-nacos
????????
????????
????????????com.alibaba.cloud
????????????spring-cloud-starter-alibaba-sentinel
????????
????????
????????????org.springframework.cloud
????????????spring-cloud-starter-openfeign
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-web
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-actuator
????????
????????
????????????org.springframework.boot
????????????spring-boot-devtools
????????????runtime
????????????true
????????
????????
????????????cn.hutool
????????????hutool-all
????????????4.6.3
????????
????????
????????????org.projectlombok
????????????lombok
????????????true
????????
????????
????????????org.springframework.boot
????????????spring-boot-starter-test
????????????test
????????
????
配置文件
server:
??port:?8401
spring:
??application:
????name:?cloudalibaba-sentinel-service
??cloud:
????nacos:
??????discovery:
????????server-addr:?localhost:8848
????sentinel:
??????transport:
????????dashboard:?localhost:8080
????????port:?8719??#默認(rèn)8719,假如被占用了會(huì)自動(dòng)從8719開始依次+1掃描。直至找到未被占用的端口
management:
??endpoints:
????web:
??????exposure:
????????include:?'*'
主啟動(dòng)類
@EnableDiscoveryClient
@SpringBootApplication
public?class?MainApp8401?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(MainApp8401.class,?args);
????}
}
然后我們建立一個(gè)測(cè)試的接口類進(jìn)行測(cè)試
@Slf4j
@RestController
public?class?FlowLimitController?{
????@GetMapping("/testA")
????public?String?testA()?{
????????return?"------testA";
????}
????@GetMapping("/testB")
????public?String?testB()?{
????????return?"------testB";
????}
????@GetMapping("/testD")
????public?String?testD()?{
????????try?{
????????????TimeUnit.SECONDS.sleep(1);
????????}?catch?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????log.info("testD?測(cè)試");
????????return?"------testD";
????}
}
由于Sentinel采用的懶加載的模式,我們需要被管理的接口除了再配置文件上聲明過以外,還需要自己訪問一次才可以進(jìn)行管理,這樣也是為了節(jié)省資源吧

流控模式的詳細(xì)信息
資源名,也就是相當(dāng)于我們?cè)L問的接口的名字
針對(duì)來源:sentinel可以針對(duì)調(diào)用者進(jìn)行限流,填寫微服務(wù)名字,默認(rèn)是default,不區(qū)分來源
閾值類型 :
QPS 每秒鐘請(qǐng)求數(shù)量,當(dāng)調(diào)用該api的QPS到達(dá)閾值的時(shí)候就會(huì)進(jìn)行限流
線程數(shù):當(dāng)調(diào)用該api的線程數(shù)到達(dá)閾值的時(shí)候,進(jìn)行限流
這兩個(gè)怎么區(qū)分呢, 一個(gè)主機(jī)可以有很多個(gè)線程,這些線程可以同時(shí)調(diào)用一個(gè)api,QPS相當(dāng)于在外部進(jìn)行攔截,線程數(shù)相當(dāng)于在系統(tǒng)的內(nèi)部進(jìn)行攔截
是否集群:現(xiàn)在先不考慮集群
流控模式:
直接:api達(dá)到限流條件時(shí),直接限流
關(guān)聯(lián):當(dāng)關(guān)聯(lián)的資源達(dá)到閾值時(shí),會(huì)限流自己
鏈路:只記錄指定鏈路上的流量
流控效果
直接:直接失敗,拋出異常
預(yù)熱:閾值除以 cold Factor(默認(rèn)為3),經(jīng)過預(yù)熱時(shí)長(zhǎng)后才會(huì)達(dá)到閾值,默認(rèn)每秒鐘可以接收3個(gè)請(qǐng)求,逐步增加,經(jīng)過預(yù)熱時(shí)長(zhǎng)之后,逐步升至設(shè)定的QPS閾值
預(yù)熱比較適合的應(yīng)用場(chǎng)景時(shí)在秒殺系統(tǒng)開啟的瞬間,會(huì)有很多流量上來,很有可能把系統(tǒng)打死,預(yù)熱方式就是為了保護(hù)系統(tǒng),慢慢的把流量放進(jìn)來,慢慢把閾值增長(zhǎng)到設(shè)置的閾值
排隊(duì)等待:讓請(qǐng)求以均勻1的速度通過,對(duì)應(yīng)的是漏桶算法,該方式主要用于處理間隔性突發(fā)的流量,例如消息隊(duì)列,,在某一秒有大量的請(qǐng)求到來,而接下來幾秒則處于空閑狀態(tài),我們希望系統(tǒng)能夠在接下來的空閑時(shí)間逐漸處理這些請(qǐng)求,而不是一下子拒絕掉

降級(jí)規(guī)則
降級(jí)策略 RT 平均響應(yīng)時(shí)間,超出閾值,且在時(shí)間窗口內(nèi)通過的請(qǐng)求大于5同時(shí)滿足時(shí)觸發(fā)
異常比例:QPS大于5 且異常比例(秒級(jí)統(tǒng)計(jì)) 超過閾值,觸發(fā)降級(jí),時(shí)間窗口結(jié)束后關(guān)閉降級(jí)
異常數(shù),異常數(shù)超過閾值(分鐘統(tǒng)計(jì)) 觸發(fā)降級(jí),時(shí)間窗口結(jié)束后,關(guān)閉降級(jí)
我們?cè)趯W(xué)習(xí)Hystrix的時(shí)候,知道它時(shí)是有一個(gè)半開的狀態(tài)的,半開的狀態(tài)系統(tǒng)自動(dòng)去檢車是否請(qǐng)求有異常,沒有異常就關(guān)閉斷路器恢復(fù)使用,有異常則繼續(xù)打開斷路器不可以用,但是這里是沒有半開這個(gè)狀態(tài)的

熱點(diǎn)參數(shù)限流
熱點(diǎn),就是我們經(jīng)常訪問的數(shù)據(jù),很多時(shí)候我們希望統(tǒng)計(jì)某一個(gè)熱點(diǎn)數(shù)據(jù)中訪問頻次最高的數(shù)據(jù),并對(duì)其訪問進(jìn)行限制,
熱點(diǎn)參數(shù)限流會(huì)統(tǒng)計(jì)傳入?yún)?shù)中的熱點(diǎn)參數(shù),并根據(jù)配置的限流閾值與模式,對(duì)包含熱點(diǎn)參數(shù)的資源調(diào)用進(jìn)行限流,熱點(diǎn)參數(shù)限流可以看作是一種特殊的流量控制,僅對(duì)包含熱點(diǎn)參數(shù)的資源調(diào)用生效
Sentinel利用LRU策略統(tǒng)計(jì)最佳最常訪問的熱點(diǎn)參數(shù),結(jié)合令牌桶算法來進(jìn)行參數(shù)級(jí)別的流量防控,熱點(diǎn)參數(shù)限流支持集群模式
????@GetMapping("/testHotKey")
????@SentinelResource(value?=?"testHotKey")
????public?String?testHotKey(@RequestParam(value?=?"p1",required?=?false)?String?p1,
?????????????????????????????@RequestParam(value?=?"p2",required?=?false)?String?p2)?{
????????//int?age?=?10/0;
????????return?"------testHotKey";
????}
我們這樣寫我們的接口,然后配置熱點(diǎn)規(guī)則,當(dāng)?shù)谝粋€(gè)參數(shù)的QPS超過1的時(shí)候就會(huì)進(jìn)行報(bào)錯(cuò),這個(gè)時(shí)候如果還是要繼續(xù)訪問就會(huì)返回錯(cuò)誤頁面,這個(gè)錯(cuò)誤頁面看著很不美觀,我們也可以自己設(shè)置
????@GetMapping("/testHotKey")
????@SentinelResource(value?=?"testHotKey",blockHandler?=?"deal_testHotKey")
????public?String?testHotKey(@RequestParam(value?=?"p1",required?=?false)?String?p1,
?????????????????????????????@RequestParam(value?=?"p2",required?=?false)?String?p2)?{
????????//int?age?=?10/0;
????????return?"------testHotKey";
????}
????//兜底方法
????public?String?deal_testHotKey?(String?p1,?String?p2,?BlockException?exception){
????????return?"------deal_testHotKey,o(╥﹏╥)o";
????}
這個(gè)時(shí)候如果訪問的頻率違背了我們定義的熱點(diǎn)規(guī)則,就會(huì)按照我們自定義的兜底方法來進(jìn)行處理
但是如果我們不違背自己在Sentinel上面定義的熱點(diǎn)規(guī)則,那么就什么事情也沒有
我們定義的熱點(diǎn)規(guī)則還可以支持高級(jí)配置,在原本的規(guī)則基礎(chǔ)上,當(dāng)參數(shù)類型和參數(shù)值確定了,再使用另外一套規(guī)則,使用不同的限流閾值
熱點(diǎn)參數(shù)需要注意:參數(shù)類型必須是基本類型或者String
@SentinelResource(value =?“testHotKey”,blockHandler =?“deal_testHotKey”) 這個(gè)處理的是Sentinel控制臺(tái)配置違規(guī)的情況,有blockHandler方法配置的兜底方法,但是如果是方法內(nèi)部運(yùn)行時(shí)異常是無法控制的
系統(tǒng)規(guī)則

Sentinel 系統(tǒng)自適應(yīng)限流從整體維度對(duì)應(yīng)用入口流量進(jìn)行控制,結(jié)合應(yīng)用的 Load、CPU 使用率、總體平均 RT、入口 QPS 和并發(fā)線程數(shù)等幾個(gè)維度的監(jiān)控指標(biāo),通過自適應(yīng)的流控策略,讓系統(tǒng)的入口流量和系統(tǒng)的負(fù)載達(dá)到一個(gè)平衡,讓系統(tǒng)盡可能跑在最大吞吐量的同時(shí)保證系統(tǒng)整體的穩(wěn)定性。
系統(tǒng)規(guī)則是從應(yīng)用基本的入口流量進(jìn)行控制,從load ,CPU使用率, 平均RT,入口QPS,和并發(fā)線程數(shù)等幾個(gè)維度監(jiān)控應(yīng)用指標(biāo),讓系統(tǒng)盡可能跑在最大吞吐量的同時(shí)保證系統(tǒng)整體的穩(wěn)定性
load是一個(gè)結(jié)果,如果根據(jù)load的情況來調(diào)節(jié)流量的通過率,那么就始終有延遲,也就意味著通過率的任何調(diào)整,都會(huì)過一段時(shí)間才能看到結(jié)果,當(dāng)前通過率是使load惡化的一個(gè)動(dòng)作,那么也至少要過1s之后才可以觀測(cè)到,同理,如果當(dāng)前通過率調(diào)整時(shí)讓load好轉(zhuǎn)的當(dāng)作,也需要1s之后才能繼續(xù)調(diào)整,這樣就浪費(fèi)了系統(tǒng)的處理能力,所以我們看到的曲線,總是會(huì)有抖動(dòng)
總的來說,load不適合我們做及時(shí)的調(diào)整,,我們應(yīng)該根據(jù)系統(tǒng)能夠處理的請(qǐng)求,和允許進(jìn)來的請(qǐng)求,來做平衡,而不是根據(jù)一個(gè)間接的指標(biāo)來 load 做限流,最終我們追求的目標(biāo)是,在系統(tǒng)不被拖垮的情況下,提高系統(tǒng)的吞吐率,而不是load一定要低于某個(gè)閾值,
CPU usage 當(dāng)系統(tǒng)CPU使用率超過閾值即觸發(fā)系統(tǒng)保護(hù) ,比較靈敏
平均RT:當(dāng)單臺(tái)機(jī)器上所有入口流量的平均RT達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù),單位是毫秒
并發(fā)線程數(shù):所有入口流量的并發(fā)線程數(shù)達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)
入口QPS:所有入口流量的QPS達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)
按資源名稱限流和按照Url地址進(jìn)行限流
@GetMapping("/byResource")
????@SentinelResource(value?=?"byResource",?blockHandler?=?"handleException")
????public?CommonResult?byResource()?{
????????return?new?CommonResult(200,?"按資源名稱限流測(cè)試OK",?new?Payment(2020,?"serial001"));
????}
????@GetMapping("/rateLimit/byUrl")
????@SentinelResource(value?=?"byUrl")
????public?CommonResult?byUrl()
????{
????????return?new?CommonResult(200,"按url限流測(cè)試OK",new?Payment(2020,"serial002"));
????}
這兩個(gè)方式其實(shí)都一樣
自定義限流邏輯處理
原來的限流邏輯有一些缺點(diǎn):
系統(tǒng)默認(rèn)的,沒有體現(xiàn)我們的業(yè)務(wù)要求
依照現(xiàn)有的條件我們自定義處理方法和業(yè)務(wù)方法都在一個(gè)類里面,耦合度太高,不太好
每個(gè)業(yè)務(wù)方法都有一個(gè)兜底方法,不太合適
全局統(tǒng)一的處理方法沒有體現(xiàn)
????@GetMapping("/byResource")
????@SentinelResource(value?=?"byResource",
????????????blockHandlerClass?=?CustomerBlockHandler.class,
????????????blockHandler?=?"handleException")
????public?CommonResult?byResource()?{
????????return?new?CommonResult(200,?"按資源名稱限流測(cè)試OK",?new?Payment(2020,?"serial001"));
????}
public?class?CustomerBlockHandler?{
????public?static?CommonResult?handleException(BlockException?e){
????????return?new?CommonResult(2000,"客戶自定義限流處理信息");
????}
}
按照上面的方法,我們就可以在一個(gè)統(tǒng)一的類里面寫一些靜態(tài)的方法,然后所有的兜底都在里面
需要注意的是,如果原來的方法有參數(shù),那么兜底的方法也一定要有參數(shù)
Sentinel整合ribbon+openFeign+fallback
創(chuàng)建兩個(gè)服務(wù)提供者項(xiàng)目,
然后消費(fèi)者項(xiàng)目創(chuàng)建,
@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只負(fù)責(zé)業(yè)務(wù)異常
????//@SentinelResource(value?=?"fallback",blockHandler?=?"blockHandler")?//blockHandler只負(fù)責(zé)sentinel控制臺(tái)配置違規(guī)
????@SentinelResource(value?=?"fallback",
????????????fallback?=?"handlerFallback",
????????????blockHandler?=?"blockHandler",
????????????exceptionsToIgnore?=?{IllegalArgumentException.class})
????public?CommonResult?fallback(@PathVariable?Long?id)?{
????????CommonResult?result?=?restTemplate.getForObject(SERVICE_URL?+?"/paymentSQL/"?+?id,?CommonResult.class,?id);
????????if?(id?==?4)?{
????????????throw?new?IllegalArgumentException("IllegalArgumentException,非法參數(shù)異常....");
????????}?else?if?(result.getData()?==?null)?{
????????????throw?new?NullPointerException("NullPointerException,該ID沒有對(duì)應(yīng)記錄,空指針異常");
????????}
????????return?result;
????}
????//fallback
????public?CommonResult?handlerFallback(@PathVariable?Integer?id,?Throwable?e)?{
????????Payment?payment?=?new?Payment(id,?"null");
????????return?new?CommonResult<>(444,?"兜底異常handlerFallback,exception內(nèi)容??"?+?e.getMessage(),?payment);
????}
????//blockHandler
????public?CommonResult?blockHandler(@PathVariable?Integer?id,?BlockException?blockException)?{
????????Payment?payment?=?new?Payment(id,?"null");
????????return?new?CommonResult<>(445,?"blockHandler-sentinel限流,無此流水:?blockException??"?+?blockException.getMessage(),?payment);
????}
}
原理上是 fallback會(huì)管理服務(wù)內(nèi)部出現(xiàn)異常的回執(zhí),被限流降級(jí)而拋出的異常只會(huì)進(jìn)入blockHandler處理邏輯
具體的套路就是我們?cè)L問規(guī)則出錯(cuò),使用blockHandler里面的方法,訪問內(nèi)部出錯(cuò),使用fallback里面的回執(zhí)方法,如果一直訪問內(nèi)部都出錯(cuò),那么就用 blockHandler的方法,因?yàn)樗紫仁沁`反了我們定義的訪問規(guī)則,然后再在內(nèi)部出錯(cuò)的
我們之前學(xué)習(xí)過Feign,現(xiàn)在再來回顧一遍,順便將這個(gè)項(xiàng)目改造一下
在服務(wù)消費(fèi)者引入feign
????
????????????org.springframework.cloud
????????????spring-cloud-starter-openfeign
????????
在配置文件上面開啟對(duì)feign的支持
#對(duì)Feign的支持
feign:
??sentinel:
????enabled:?true
遠(yuǎn)程調(diào)用,并聲明降級(jí)方法
@FeignClient(value?=?"nacos-payment-provider",fallback?=?PaymentFallbackService.class)
public?interface?PaymentService
{
????@GetMapping(value?=?"/paymentSQL/{id}")
????public?CommonResult?paymentSQL(@PathVariable("id")?Integer?id);
}
@Component
public?class?PaymentFallbackService?implements?PaymentService?{
????@Override
????public?CommonResult?paymentSQL(Integer?id)?{
????????return?new?CommonResult<>(44444,?"服務(wù)降級(jí)返回,---PaymentFallbackService",?new?Payment(id,?"errorSerial"));
????}
}
在主啟動(dòng)類聲明啟用Feign遠(yuǎn)程調(diào)用
@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public?class?OrderNacosMain84?{
????public?static?void?main(String[]?args)?{
????????SpringApplication.run(OrderNacosMain84.class,?args);
????}
}
熔斷框架的比較
| | Sentinel | Hystrix | resilience4j |
| — | — | — | — |
| 隔離策略 | 信號(hào)量隔離(并發(fā)線程數(shù)限流) | 線程池隔離/信號(hào)量隔離 | 信號(hào)量隔離 |
| 熔斷降級(jí)策略 | 基于響應(yīng)時(shí)間,異常比率,異常數(shù) | 基于異常比率 | 基于異常比率,響應(yīng)時(shí)間 |
| 實(shí)時(shí)統(tǒng)計(jì)實(shí)現(xiàn) | 滑動(dòng)窗口 LeapArray | 滑動(dòng)窗口,基于 RxJava | Ring Bit Buffer |
| 動(dòng)態(tài)規(guī)劃配置 | 支持多種數(shù)據(jù)源 | 支持多種數(shù)據(jù)源 | 有限支持 |
| 擴(kuò)展性 | 多個(gè)擴(kuò)展點(diǎn) | 插件的形式 | 的接口形式 |
| 基于注解的支持 | 支持 | 支持 | 支持 |
| 限流 | 基于QPS,支持基于調(diào)用關(guān)系的限流 | 有限的支持 | Rate Limiter |
| 流量整形 | 支持預(yù)熱模式,勻速器模式,預(yù)熱排隊(duì)模式 | 不支持 | 簡(jiǎn)單的Rate Limiter模式 |
| 系統(tǒng)自適應(yīng)保護(hù) | 支持 | 不支持 | 不支持 |
| 控制臺(tái) | 提高開箱即用的控制臺(tái),可配置規(guī)則,查看秒級(jí)監(jiān)控,機(jī)器發(fā)現(xiàn) | 簡(jiǎn)單的監(jiān)控查看 | 不提高控制臺(tái),可對(duì)接其他監(jiān)控系統(tǒng) |
規(guī)則持久化
一旦我們重啟應(yīng)用,Sentinel規(guī)則就會(huì)消失,生產(chǎn)環(huán)境需要將配置規(guī)則進(jìn)行持久化,
可以將限流配置規(guī)則持久化進(jìn) Nacos 保存,只要刷新 8401 某個(gè)rest地址,sentinel控制臺(tái)的流控規(guī)則就能看到,只要Nacos里面的配置不刪除,針對(duì) 8401 上 Sentinel上的 流控規(guī)則持續(xù)有效
我們發(fā)現(xiàn)我們指定的規(guī)則在外面的服務(wù)停止之后再上來就沒有了,針對(duì)這種情況,外面可以將外面的配置作為一個(gè)配置文件存儲(chǔ)到nacos上面,而nacos外面前面已經(jīng)學(xué)習(xí)過,它將信息存儲(chǔ)到了數(shù)據(jù)庫里面,所以外面就相當(dāng)于間接的把配置信息存儲(chǔ)到了數(shù)據(jù)庫上面
下次登錄就會(huì)發(fā)現(xiàn)以前配置過的規(guī)則
??????
????????????com.alibaba.csp
????????????sentinel-datasource-nacos
????????
配文件,需要聲明外面存儲(chǔ)到了哪個(gè)nacos聲明,存儲(chǔ)的格式,名字,組名,規(guī)則
server:
??port:?8401
spring:
??application:
????name:?cloudalibaba-sentinel-service
??cloud:
????nacos:
??????discovery:
????????server-addr:?localhost:8848
????sentinel:
??????transport:
????????dashboard:?localhost:8080
????????port:?8719??#默認(rèn)8719,假如被占用了會(huì)自動(dòng)從8719開始依次+1掃描。直至找到未被占用的端口
??????datasource:
????????ds1:
??????????nacos:
????????????server-addr:?localhost:8848
????????????dataId:?cloudalibaba-sentinel-service
????????????groupId:?DEFAULT_GROUP
????????????data-type:?json
????????????rule-type:?flow
management:
??endpoints:
????web:
??????exposure:
????????include:?'*'
??????
??????
spring:
???cloud:
????sentinel:
????datasource:
?????ds1:
??????nacos:
????????server-addr:localhost:8848
????????dataid:${spring.application.name}
????????groupid:DEFAULT_GROUP
????????data-type:json
????????????rule-type:flow?????
外面配置過一個(gè)規(guī)則之后,配置的格式大致如此
[
????{
?????????"resource":?"/retaLimit/byUrl",???
?????????"limitApp":?"default",
?????????"grade":???1,
?????????"count":???1,
?????????"strategy":?0,
?????????"controlBehavior":?0,
?????????"clusterMode":?false????
????}
]
resource:?資源名稱,可以看作是我們的接口什么的
limitApp:?來源應(yīng)用
grade:?閾值類型,0表示線程數(shù),1表示QPS
count:?單機(jī)閾值
strategy:?流控模式:0表示直接,1表示關(guān)聯(lián),2表示鏈路
controlBehavior:?流控效果,0表示快速失敗,1表示預(yù)熱,2表示排隊(duì)
clusterMode:?表示是否集群
分布式事務(wù)問題
Seata是一款開源的分布式事務(wù)解決方案,致力于提供高性能和簡(jiǎn)單易用的分布式事務(wù)服務(wù),Seata將為用戶提供了AT,TCC,SAGA和XA事務(wù)模式
AT模式
前提是基于本地ACID事務(wù)的關(guān)系型數(shù)據(jù)庫,Java應(yīng)用,通過JDBC訪問數(shù)據(jù)庫
通過兩個(gè)階段提交協(xié)議:
一階段:業(yè)務(wù)數(shù)據(jù)和回滾日志記錄在同一個(gè)本地事務(wù)中提交,釋放本地鎖和連接資源
二階段,提交異步化,回滾通過一階段的回滾日志進(jìn)行反向補(bǔ)償
寫隔離:
一階段本地事務(wù)提交之前,需要確保先拿到全局鎖,
拿不到全局鎖就無法提交本地事務(wù),
拿全局鎖的嘗試被限制在一定范圍內(nèi),超過范圍將放棄,并回滾本地事務(wù),釋放本地鎖,
例子 一階段:
解析SQL,得到SQL的類型,表,條件等相關(guān)信息,
查詢前鏡像:根據(jù)解析得到的條件信息,生成查詢語句,定位數(shù)據(jù),得到要更新這一行的數(shù)據(jù)的鏡像
執(zhí)行業(yè)務(wù)SQL,更新這條記錄,
查詢后鏡像,根據(jù)前鏡像的結(jié)果,通過主鍵定位數(shù)據(jù),得到之后的鏡像,
插入回滾日志,把前后鏡像數(shù)據(jù)以及業(yè)務(wù)SQL相關(guān)的信息組成一條回滾日志記錄,插入到UNDO_LOG表中,
提交之前,向TC注冊(cè)分支,去申請(qǐng)要修改記錄的全局鎖,
本地事務(wù)提交,業(yè)務(wù)數(shù)據(jù)的更新和前面步驟中生成的UNDO_LOG 一并提交
將本地事務(wù)提交的結(jié)果上報(bào)給TC
二階段-回滾
收到TC的分支回滾請(qǐng)求,開啟一個(gè)本地事務(wù),執(zhí)行如下操作
通過XID和BranchID查找相應(yīng)的UNDO_LOG 記錄
數(shù)據(jù)校驗(yàn):拿UNDO_LOG中的后鏡像與當(dāng)前數(shù)據(jù)進(jìn)行比較,如果有不同,說明數(shù)據(jù)被當(dāng)前全局事務(wù)之外的動(dòng)作做了修改,這種情況需要根據(jù)配置策略來做處理
根據(jù)UNDO_LOG 中的前鏡像和業(yè)務(wù)SQL的相關(guān)信息生成執(zhí)行回滾的語句
然后提交本地事務(wù),并把本地事務(wù)的執(zhí)行結(jié)果上報(bào)給TC
二階段-提交
收到TC的分支提交請(qǐng)求,把請(qǐng)求放入一個(gè)異步任務(wù)的隊(duì)列中,馬上返回提交成功的結(jié)果給TC,
異步任務(wù)階段的分支提交請(qǐng)求將異步和批量的刪除相應(yīng)UNDO_LOG記錄
TCC模式
上一步AT模式的全局事務(wù)整體式兩階段提交,全局事務(wù)時(shí)由若干分支事務(wù)組成,分支事務(wù) 要滿足兩階段提交的模型要求,即每個(gè)分支事務(wù)都具備自己的兩個(gè)階段
一階段 prepare行為 :在本地事務(wù)中,一并提交業(yè)務(wù)數(shù)據(jù)更新和相應(yīng)回滾日志記錄
二階段 commit 或 rollback行為 :commit行為,馬上成功結(jié)束,自動(dòng)異步批量清理回滾日志
二階段 rollback :通過回滾日志,自動(dòng)生成補(bǔ)償操作,完成數(shù)據(jù)回滾
分布式事務(wù)的處理過程:ID+三組件模型
全局唯一的事務(wù)ID
TC:Transaction Coordinator 事務(wù)協(xié)調(diào)器,維護(hù)全局事務(wù)的運(yùn)行狀態(tài),負(fù)責(zé)協(xié)調(diào)并驅(qū)動(dòng)全局事務(wù)的提交或回滾
TM:Transaction Manager 控制全局事務(wù)的邊界,負(fù)責(zé)開啟一個(gè)全局事務(wù),并最終發(fā)起全局提交或全局回滾的決議
RM:Resource Manager 控制分支事務(wù),負(fù)責(zé)分支注冊(cè),狀態(tài)匯報(bào),并接收事務(wù)協(xié)調(diào)器的指令,驅(qū)動(dòng)分支(本地)事務(wù)dd1提交和回滾
處理過程
TM向TC申請(qǐng)開啟一個(gè)全局事務(wù),全局事務(wù)創(chuàng)建成功,并生成一個(gè)全局唯一的XID
XID在微服務(wù)調(diào)用鏈路的上下文中傳播
RM向TC注冊(cè)分支事務(wù),將其納入XID對(duì)應(yīng)全局事務(wù)的管轄,
TM向TC發(fā)起針對(duì)XID的全局提交或回滾決議
TC調(diào)度XID管轄的全部分支事務(wù)完成提交或回滾請(qǐng)求
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循?CC 4.0 BY-SA?版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。
本文鏈接:
https://blog.csdn.net/weixin_42755322/article/details/113829196
鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布
??????
??長(zhǎng)按上方微信二維碼?2 秒
感謝點(diǎn)贊支持下哈?
