新聞中心
在上一篇中,我們講解了?? Sentinel 限流詳解??,其中詳細講解了各個規(guī)則下的限流是如何操作,有興趣的小伙伴可以了解一下,有不少小伙伴在后臺留言說,想了解一下 sentinel中如何使用@SentinelResource和openFeign來進行服務(wù)熔斷和降級的操作,大家知道小農(nóng)對于小伙伴的要求,那都是盡量滿足,今天我們就來好好說一下,@SentinelResource和openFeign。

成都創(chuàng)新互聯(lián)公司客戶idc服務(wù)中心,提供重慶服務(wù)器托管、成都服務(wù)器、成都主機托管、成都雙線服務(wù)器等業(yè)務(wù)的一站式服務(wù)。通過各地的服務(wù)中心,我們向成都用戶提供優(yōu)質(zhì)廉價的產(chǎn)品以及開放、透明、穩(wěn)定、高性價比的服務(wù),資深網(wǎng)絡(luò)工程師在機房提供7*24小時標準級技術(shù)保障。
SentinelResource
在上一節(jié)中,我們也使用到過這個注解,我們需要了解的是其中兩個屬性:
value:資源名稱,必填且唯一。
@SentinelResource(value = "test/get")
entryType:非必填,entry類型,標記流量的方向,指明是出口流量,還是入口流量;取值 IN/OUT ,默認是OUT。
@SentinelResource(value = "test/get",entryType = EntryType.IN)
blockHandler:處理異常(BlockException)的函數(shù)名稱,不必填,使用時注意兩點:
- 函數(shù)訪問的方法需要為public。
- 返回類型和入?yún)⑿枰妥饔迷谠椒ㄉ弦恢虑倚枰~外加一個(BlockException)類型的參數(shù)。
blockHandlerClass:非必填,存放blockHandler的類。對應(yīng)的處理函數(shù)必須static修飾,否則無法解析,必須是public,返回類型與原方法一致,參數(shù)類型需要和原方法相匹配,并在最后加上BlockException類型的參數(shù)。
fallback:非必填,用于在拋出異常的時候提供fallback處理邏輯。fallback函數(shù)可以針對所有類型的異常(除了execptionsToIgnore 里面排除掉的異常類型)進行處理。
exceptionsToIgnore:非必填,指定排除掉哪些異常。排除的異常不會計入異常統(tǒng)計,也不會進入fallback邏輯,而是原樣拋出。
默認限流
今天我們就針對于上面的幾個點詳細的展開介紹,在實際應(yīng)用中我們?nèi)绾芜M行操作。我們先來編寫一個新的控制器類型,這里我們使用cloud-alibaba-sentinel-8006項目進行操作,對應(yīng)源碼已經(jīng)放在開頭位置,需要請自取。
@SentinelResource 既可以配置資源名稱也可以配置URL,當我們配置了blockHandler屬性時,如果達到閾值時,會調(diào)用對應(yīng)的方法提示限流信息,如果沒有配置blockHandler屬性,系統(tǒng)會走默認的限流信息(Blocked by Sentinel (flow limiting))。
首先我們使用默認的@SentinelResource注解,系統(tǒng)會針對對應(yīng)的地址調(diào)用默認的異常處理方法。
@GetMapping("/restUrl")
@SentinelResource(value = "restUrl")
public String restUrl(){
return " restUrl";
}注意:我們重啟項目之后,要先訪問,才能去設(shè)置對應(yīng)的限流規(guī)則。
先訪問http://localhost:8006/restUrl,在添加流控規(guī)則。
此時如果沒有自己定義限流處理方法,會走系統(tǒng)默認的。
blockHandler
使用@SentinelResource注解同時使用blockHandler屬性。
@GetMapping("resourceTest")
@SentinelResource(value = "resourceTest",blockHandler = "handler_resource")
public String resourceTest(){
return "resourceTest";
}
public String handler_resource(BlockException exception){
return "系統(tǒng)繁忙,請稍后再試";
}先訪問http://localhost:8006/resourceTest,在添加流控規(guī)則。
再去快速的去訪問http://localhost:8006/resourceTest 就會出現(xiàn)我們在代碼中配置的限流異常處理信息,如下圖所示:
上面就展示了我們使用blockHandler屬性時,出現(xiàn)的我們自己設(shè)置的異常提示,但是當我們使用上面兩種方案的時候,會出現(xiàn)一些問題,如果我們的業(yè)務(wù)邏輯比較復(fù)雜,熔斷的業(yè)務(wù)場景比較多,上面的顯然不能夠滿足我們的應(yīng)用,而且這種自定義方法是和我們的業(yè)務(wù)代碼耦合在一起的,在實際開發(fā)中,會顯得不夠優(yōu)雅,每個業(yè)務(wù)方法對添加一個對應(yīng)的限流處理方法,會讓代碼顯得臃腫,而且無法實現(xiàn)統(tǒng)一處理。在這里我們就需要提到我們另外一個屬性—blockHandlerClass。
blockHandlerClass
此屬性中設(shè)置的方法必需為 static 函數(shù),否則無法解析。首先我們需要創(chuàng)建一個類用于專門處理自定義限流處理邏輯,這里記住,方法一定要是靜態(tài),否則無法解析,如下所示:
import com.alibaba.csp.sentinel.slots.block.BlockException;
/**
* Sentinel限流自定義邏輯
*/
public class SentinelExptioinHandler {
public static String handlerMethodError(BlockException exception){
return "handlerMethodError:服務(wù)異常,請稍后重試!";
}
public static String handlerMethodNetwork(BlockException exception){
return "handlerMethodNetwork:網(wǎng)絡(luò)錯誤,連接超時,請稍后重試!";
}
}
同時我們添加一個可訪問的接口方法,設(shè)置@SentinelResource注解和blockHandlerClass屬性對應(yīng)的類型和這個類型中對應(yīng)的處理方法。
/**
* 此方法用到了自定義限流處理類型CustomerBlockHandler
* 中的handlerException1方法來處理限流邏輯。
*/
@GetMapping("/buildExption")
@SentinelResource(value = "buildExption",
blockHandlerClass = SentinelExptioinHandler.class,blockHandler = "handlerMethodError")
public String buildExption(){
return "hello buildExption";
}
然后我們先訪問http://localhost:8006/buildExption后,來給它添加限流規(guī)則。
我們再次訪問http://localhost:8006/buildExption后,這個時候我們來看一下如果超過閾值之后使用的處理方法是否是我們的SentinelExptioinHandler.handlerMethodError(),當我們頻繁的訪問地址,就會看到出現(xiàn)了我們在異常處理類中設(shè)置的方法。
如果我們想要體現(xiàn),網(wǎng)絡(luò)異常的操作,我們只需要替換blockHandler中的handlerMethodError改為handlerMethodNetwork,重啟項目后,重復(fù)上面的步驟,再來看一下,就會出現(xiàn)下面的提示:
服務(wù)熔斷
在微服務(wù)中,由于業(yè)務(wù)的拆分,一般會出現(xiàn)請求鏈路過程的情況,當一個用戶發(fā)起一個請求,通常需要幾個微服務(wù)才能完成,在高并發(fā)的場景下,這種服務(wù)之間的依賴對系統(tǒng)的穩(wěn)定性影響比較大,如果其中一個環(huán)節(jié)出現(xiàn)網(wǎng)絡(luò)延遲或者請求超時等問題會導(dǎo)致其他服務(wù)的不可用并形成阻塞,從而導(dǎo)致雪崩,服務(wù)熔斷就是用來解決這種情況,當一個服務(wù)提供在無法提供正常服務(wù)時,為了放在雪崩的方式,會將當前接口和外部隔離,觸發(fā)熔斷,在熔斷時間內(nèi),請求都會返回失敗,直到服務(wù)提供正常,才會結(jié)束熔斷。簡單來說,服務(wù)熔斷就是應(yīng)對微服務(wù)雪崩的一種鏈路保護機制。
為了模擬實際的應(yīng)用場景,我們需要整合Ribbon+openFeign,來搭建真實的應(yīng)用場景。首先我們需要利用Ribbon進行負載均衡的調(diào)用,我們先來創(chuàng)建消費者(cloud-alibab-consumer-8083)和兩個服務(wù)提供者(cloud-alibaba-provider-9003/9004)。
我們先來搭建服務(wù)提供者。
服務(wù)提供者
pom文件:
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.muxiaonong
cloud-alibaba-commons
0.0.1-SNAPSHOT
yml文件:
server:
port: 9003
spring:
application:
name: nacos-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
主啟動類添加@EnableDiscoveryClient注解。
@SpringBootApplication
@EnableDiscoveryClient
public class CloudAlibabaProvider9003Application {
public static void main(String[] args) {
SpringApplication.run(CloudAlibabaProvider9003Application.class, args);
}
}
添加商品信息請求類。
@RestController
public class GoodsController {
@Value("${server.port}")
private String serverPort;
//模仿數(shù)據(jù)庫存儲數(shù)據(jù)
public static HashMaphashMap = new HashMap<>();
static {
hashMap.put(1l,"面膜");
hashMap.put(2l,"哈密瓜");
hashMap.put(3l,"方便面");
}
@GetMapping("queryGoods/{id}")
public ResponsequeryGoods(@PathVariable("id") Long id){
Responseresponse = new Response(200,"成功請求:"+serverPort,hashMap.get(id));
return response;
}
}
到這里服務(wù)提供者就搭建完成了。
注意:另外一個服務(wù)提供者一樣,只需要端口不一樣即可,在這里就不做重復(fù)性的演示。
服務(wù)消費者
pom:
org.springframework.boot
spring-boot-starter-web
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-devtools
runtime
true
com.muxiaonong
cloud-alibaba-commons
0.0.1-SNAPSHOT
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
org.springframework.cloud
spring-cloud-starter-openfeign
2.2.6.RELEASE
yml文件:
server:
port: 8083
spring:
application:
name: nacos-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默認8719端口,假如被占用會自動從8719開始依次+1掃描,直至找到未被占用的端口
port: 8719
#消費者將要去訪問的微服務(wù)名稱(注冊成功進nacos的微服務(wù)提供者)
service-url:
nacos-user-service: http://nacos-provider
主啟動類添加@EnableDiscoveryClient。
@SpringBootApplication
@EnableDiscoveryClient
public class CloudAlibabConsumer8083Application {
public static void main(String[] args) {
SpringApplication.run(CloudAlibabConsumer8083Application.class, args);
}
}
訪問類:
/**
* @program: spring-cloud-alibaba
* @ClassName DemoController
* @description:
* @author: 牧小農(nóng)
* @create: 2022-06-04 23:10
* @Version 1.0
**/
@RestController
public class DemoController {
@Autowired
private RestTemplate restTemplate;
/**
* 消費者去訪問具體服務(wù),這種寫法可以實現(xiàn)
* 配置文件和代碼的分離
*/
@Value("${service-url.nacos-user-service}")
private String serverURL;
@GetMapping("/consumer/goods/{id}")
public Responsefallback(@PathVariable Long id){
//通過Ribbon發(fā)起遠程訪問,訪問9003/9004
if(id <= 3) {
Responseresult = restTemplate.getForObject(serverURL + "/queryGoods/" + id, Response.class);
return result;
}else {
throw new NullPointerException("未查詢到對應(yīng)的數(shù)據(jù)");
}
}
}
我們先啟動9003/9004,在啟動8083,然后訪問http://localhost:8083/consumer/goods/2,就可以看到在瀏覽器中,如果9003/9004相互切換,說明我們搭建成功。
fallback
SentinelResource的fallback屬性,是一個可選項,主要用于拋出異常的時候提供處理邏輯,該函數(shù)可以針對所有的異常類型(除了exceptionsToIgnore排除的異常類型,等下會講解)進行處理,對于fallback的函數(shù)簽名和位置要求:
- 返回值需和原函數(shù)返回在一致。
- 方法參數(shù)列表需要和原函數(shù)一致,可以額外多一個Throwbale類型的參數(shù)用來接收對應(yīng)的異常。
- fallback 函數(shù)默認需要和原方法在同一個類中,如果希望使用其他類的函數(shù),則可以指定fallbackClass? 為對應(yīng)的類的Class 對象,注意對應(yīng)的函數(shù)必需為 static 函數(shù),否則無法解析。
案例:
@Autowired
private RestTemplate restTemplate;
@GetMapping("/consumer/goods/{id}")
//如果不設(shè)置這個注解和fallback參數(shù),異常會原樣彈出
//如果設(shè)置SentinelResource注解的fallback屬性,會按照設(shè)置的方法處理Java異常
@SentinelResource(value = "falllback",fallback = "fallbackHandler")//被標注的異常將會被 原樣拋出
public Responsefallback(@PathVariable Long id){
//通過Ribbon發(fā)起遠程訪問,訪問9003/9004
if(id <= 3) {
Responseresult = restTemplate.getForObject(serverURL + "/queryGoods/" + id, Response.class);
return result;
}else {
throw new NullPointerException("未查詢到對應(yīng)的數(shù)據(jù)");
}
}
//保證方法簽名基本保持一致,但是要添加異常類型參數(shù)
public ResponsefallbackHandler(Long id,Throwable e){
Responseresult = new Response<>(500,"出現(xiàn)未知商品id","商品不存在");
return result;
}
在這里如果我們?nèi)ピL問id超過3的數(shù)字的時候請求時(http://localhost:8083/consumer/goods/6 ),如果我們沒有設(shè)置fallback屬性,會彈出NullPointerException的錯誤。
現(xiàn)在當我們?nèi)ピL問設(shè)置了 fallback屬性的時http://localhost:8083/consumer/goods/6 會出現(xiàn)我們設(shè)置的參數(shù)。
fallback屬性和blockHandler有點類似,也可以設(shè)置fallbackClass屬性,用來指定對應(yīng)類型,來處理對應(yīng)的異常類型,但是方法也是需要為靜態(tài)方法,否則無法解析。
那么既然fallback屬性和blockHandler都能進行限流,那么他們有什么不同,哪一個的優(yōu)先級更高?首先我們要知道blockHandler屬性 是針對于Sentinel異常,blockHandler 對應(yīng)處理 BlockException 的函數(shù)名稱,而fallback屬性針對于Java異常,如果我們同時設(shè)置blockHandler和fallback,會執(zhí)行哪個方法呢?我們來看一下。
@GetMapping("/consumer/goods/{id}")
//如果不設(shè)置這個注解和fallback參數(shù),異常會原樣彈出
//如果設(shè)置SentinelResource注解的fallback屬性,會按照設(shè)置的方法處理Java異常
@SentinelResource(value = "falllback",fallback = "fallbackHandler",blockHandler = "blockHandler")
public Response fallback(@PathVariable Long id){
//通過Ribbon發(fā)起遠程訪問,訪問9003/9004
if(id <= 3) {
Response result = restTemplate.getForObject(serverURL + "/queryGoods/" + id, Response.class);
return result;
}else {
throw new NullPointerException("未查詢到對應(yīng)的數(shù)據(jù)");
}
}
//保證方法簽名基本保持一致,但是要添加異常類型參數(shù)
public Response fallbackHandler(Long id,Throwable e){
Response result = new Response<>(500,"出現(xiàn)未知商品id","商品不存在");
return result;
}
//處理Sentinel限流
public Response blockHandler(Long id, BlockException e){
Response result = new Response<>(501,"sentinel限流操作","blockHandler 限流");
return result;
} 添加熔斷規(guī)則,在一秒內(nèi)最小請求次數(shù)為5,如果異常超過2個時,觸發(fā)熔斷規(guī)則。
這個時候我們再來訪問http://localhost:8083/consumer/goods/6時,沒有觸發(fā)熔斷之前出現(xiàn)異常,由fallback進行處理。
當我們快速點擊,觸發(fā)熔斷規(guī)則時,這是時候則由blockHandler進行處理。
當我們介紹上面的操作后,我們再給大家介紹關(guān)于sentinel的另外一個屬性 exceptionsToIgnore。
exceptionsToIgnore
用于指定哪些異常被排除,不會計入異常統(tǒng)計中,也不會進入 fallback屬性處理的方法,會原樣拋出。
@GetMapping("/consumer/goods/{id}")
//添加SentinelResource注解的fallback屬性,同時設(shè)置方法來解決Java異常
@SentinelResource(value = "falllback",fallback = "fallbackHandler",blockHandler = "blockHandler",
exceptionsToIgnore = {NullPointerException.class})//被標注的異常將會被 原樣拋出
public Response fallback(@PathVariable Long id){
//通過Ribbon發(fā)起遠程訪問,訪問9003/9004
if(id <= 3) {
Response result = restTemplate.getForObject(serverURL + "/queryGoods/" + id, Response.class);
return result;
}else {
throw new NullPointerException("未查詢到對應(yīng)的數(shù)據(jù)");
}
} 啟動項目,當我們再去訪問http://localhost:8083/consumer/goods/6的時候,出現(xiàn)原有異常。
在這一節(jié)中,我們主要講解了sentinel服務(wù)熔斷的這些事,包括@SentinelResource注解的使用方式和場景,以及ribbon實現(xiàn)負載均衡的使用,服務(wù)熔斷場景我們主要講解兩個,一個是ribbon實現(xiàn)的,一個是openFeign實現(xiàn)。下面我們就來了解一下基于openFeign如何實現(xiàn)負載均衡和服務(wù)熔斷。
openFeign
OpenFeign是一種聲明
當前標題:Sentinel與OpenFeign服務(wù)熔斷那些事
URL地址:http://fisionsoft.com.cn/article/dppgdcc.html


咨詢
建站咨詢
