新聞中心
章節(jié)概述
今天,我們就使用Sentinel實(shí)現(xiàn)接口的限流,并使用Feign整合Sentinel實(shí)現(xiàn)服務(wù)容錯的功能,讓小伙伴們體驗(yàn)下微服務(wù)使用了服務(wù)容錯功能的效果。因?yàn)槲覀冋麄€專欄的內(nèi)容僅僅圍繞著SpringCloud Alibaba技術(shù)棧展開,所以,這里我們使用的服務(wù)容錯組件是阿里開源的Sentinel。

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項(xiàng)目有:域名注冊、網(wǎng)絡(luò)空間、營銷軟件、網(wǎng)站建設(shè)、鄂溫克網(wǎng)站維護(hù)、網(wǎng)站推廣。
當(dāng)然,能夠?qū)崿F(xiàn)服務(wù)容錯功能的組件不僅僅有Sentinel,比如:Hystrix和Resilience4J也能夠?qū)崿F(xiàn)服務(wù)容錯的目的,關(guān)于Hystrix和Resilience4J不是本專欄的重點(diǎn),冰河就不再贅述了,小伙伴們可以自行了解。
關(guān)于Sentinel
隨著微服務(wù)的流行,服務(wù)和服務(wù)之間的穩(wěn)定性變得越來越重要。Sentinel 以流量為切入點(diǎn),從流量控制、熔斷降級、系統(tǒng)負(fù)載保護(hù)等多個維度保護(hù)服務(wù)的穩(wěn)定性。
Sentinel的特征
- 豐富的應(yīng)用場景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發(fā)流量控制在系統(tǒng)容量可以承受的范圍)、消息削峰填谷、集群流量控制、實(shí)時熔斷下游不可用應(yīng)用等。
- 完備的實(shí)時監(jiān)控:Sentinel 同時提供實(shí)時的監(jiān)控功能。您可以在控制臺中看到接入應(yīng)用的單臺機(jī)器秒級數(shù)據(jù),甚至 500 臺以下規(guī)模的集群的匯總運(yùn)行情況。
- 廣泛的開源生態(tài):Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相應(yīng)的依賴并進(jìn)行簡單的配置即可快速地接入 Sentinel。同時 Sentinel 提供 Java/Go/C++ 等多語言的原生實(shí)現(xiàn)。
- 完善的 SPI 擴(kuò)展機(jī)制:Sentinel 提供簡單易用、完善的 SPI 擴(kuò)展接口。您可以通過實(shí)現(xiàn)擴(kuò)展接口來快速地定制邏輯。例如定制規(guī)則管理、適配動態(tài)數(shù)據(jù)源等。
Sentinel的主要特性
Sentinel的開源生態(tài)
Sentinel 分為兩個部分:
- 核心庫(Java 客戶端)不依賴任何框架/庫,能夠運(yùn)行于所有 Java 運(yùn)行時環(huán)境,同時對 Dubbo / Spring Cloud 等框架也有較好的支持。
- 控制臺(Dashboard)基于 Spring Boot 開發(fā),打包后可以直接運(yùn)行,不需要額外的 Tomcat 等應(yīng)用容器
注意:上述內(nèi)容來自Sentinel官方文檔,鏈接地址為:
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
項(xiàng)目整合Sentinel
在微服務(wù)項(xiàng)目中整合Sentinel是非常簡單的,只需要在項(xiàng)目的pom.xml文件中引入Sentinel的依賴即可,不過在使用Sentinel時,需要安裝Sentinel的控制臺。
安裝Sentinel控制臺
Sentinel 提供一個輕量級的控制臺, 它提供機(jī)器發(fā)現(xiàn)、單機(jī)資源實(shí)時監(jiān)控以及規(guī)則管理等功能。
(1)到鏈接
https://github.com/alibaba/Sentinel/releases
下載Sentinel控制臺,如下所示,我這里下載的Sentinel控制臺是1.8.4版本。
(2)Sentinel控制臺下載完成后,在本地啟動Sentinel控制臺,如下所示。
java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888
-Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.4.jar
小伙伴們?nèi)绻朐贑entOS服務(wù)器上以后臺進(jìn)程方式啟動Sentinel控制臺,可以使用如下命令
nohup java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.4.jar >> /dev/null &
啟動后在瀏覽器中輸入 http://localhost:8888 訪問Sentinel控制臺,如下所示。
輸入默認(rèn)的用戶名sentinel和密碼sentinel,登錄Sentinel控制臺,如下所示。
至此,Sentinel控制臺下載并啟動成功。
項(xiàng)目集成Sentinel
(1)在訂單微服務(wù)的shop-order的pom.xml文件中添加Sentinel的相關(guān)依賴,如下所示。
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
(2)在訂單微服務(wù)的shop-order的application.yml文中加入Sentinel相關(guān)的配置,如下所示。
spring:
cloud:
sentinel:
transport:
port: 9999 #指定和Sentinel控制臺交互的端口,任意指定一個未使用的端口即可
dashboard: 127.0.0.1:8888 #Sentinel控制臺服務(wù)地址
(3)為了讓大家直觀的感受到Sentinel的功能,這里我們先在訂單微服務(wù)的io.binghe.shop.order.controller.OrderController類中新增一個測試接口,如下所示。
@GetMapping(value = "/test_sentinel")
public String testSentinel(){
log.info("測試Sentinel");
return "sentinel";
}
(4)啟動訂單微服務(wù),在瀏覽器中輸入http://localhost:8080/order/test_sentinel訪問在訂單微服務(wù)中新增的接口,如下所示。
(5)刷新Sentinel頁面,會發(fā)現(xiàn)已經(jīng)顯示了訂單微服務(wù)的菜單,如下所示。
注意:直接啟動訂單微服務(wù)和Sentinel,會發(fā)現(xiàn)Sentinel中沒有訂單微服務(wù)的數(shù)據(jù),因?yàn)镾entinel是懶加載機(jī)制,所以需要訪問一下接口,再去訪問Sentinel 就有數(shù)據(jù)了。
至此,訂單微服務(wù)成功集成了Sentinel。
集成Sentinel限流功能
這里,我們使用Sentinel為http://localhost:8080/order/test_sentinel接口限流,步驟如下所示。
(1)在Sentinel控制臺找到server-order下的簇點(diǎn)鏈路菜單,如下所示。
(2)在簇點(diǎn)鏈路列表中找到/test_sentinel,在右側(cè)的操作中選擇流控,如下所示。
點(diǎn)擊流控按鈕會顯示 新增流控規(guī)則 的彈出框,如下所示。
這里,我們在單機(jī)閾值后直接填寫1,如下所示。
配置好之后點(diǎn)擊新增按鈕。上述配置表示http://localhost:8080/order/test_sentinel接口的QPS為1,每秒訪問1次。如果每秒訪問的次數(shù)超過1次,則會被Sentinel限流。
(3)在瀏覽器上不斷刷新http://localhost:8080/order/test_sentinel接口,當(dāng)每秒中訪問的次數(shù)超過1次時,會被Sentinel限流,如下所示。
對提交訂單的接口限流
在提交訂單的接口 http://localhost:8080/order/submit_order上實(shí)現(xiàn)限流,步驟如下。
(1)首先訪問下提交訂單的接口 http://localhost:8080/order/submit_order,使得Sentinel中能夠捕獲到提交訂單的接口,并點(diǎn)擊操作中的流控按鈕,如下所示。
這里的注意點(diǎn)還是:直接啟動訂單微服務(wù)和Sentinel,會發(fā)現(xiàn)Sentinel中沒有訂單微服務(wù)的數(shù)據(jù),因?yàn)镾entinel是懶加載機(jī)制,所以需要訪問一下接口,再去訪問Sentinel 就有數(shù)據(jù)了。
(2)在新增流控規(guī)則顯示框中的QPS單機(jī)閾值設(shè)置為1,點(diǎn)擊新增按鈕,如下所示。
(3)在瀏覽器中不斷刷新 http://localhost:8080/order/submit_order?userId=1001&productId=1001&count=1 使得每秒訪問的頻率超過1次,會被Sentinel限流,如下所示。
至此,項(xiàng)目中集成了Sentinel并使用Sentinel實(shí)現(xiàn)了接口的限流。
Feign整合Sentinel實(shí)現(xiàn)容錯
我們之前在項(xiàng)目中集成了Sentinel,并使用Sentinel實(shí)現(xiàn)了限流,如果訂單微服務(wù)的下游服務(wù),比如用戶微服務(wù)和商品微服務(wù)出現(xiàn)故障,無法訪問時,那訂單微服務(wù)該如何實(shí)現(xiàn)服務(wù)容錯呢?使用Sentinel就可以輕松實(shí)現(xiàn)。
添加依賴并開啟支持
(1)在訂單微服務(wù)的shop-order的pom.xml文件中添加Sentinel的相關(guān)依賴,如下所示。
com.alibaba.cloud
spring-cloud-starter-alibaba-sentinel
注意:這一步是為了整個案例的完整性加上的,如果小伙伴們按照文章實(shí)現(xiàn)了項(xiàng)目整合Sentinel,并在訂單微服務(wù)的shop-order的pom.xml文件中添加了上述配置,則可忽略此步驟。
(2)在訂單微服務(wù)的application.yml文件中添加如下配置開啟Feign對Sentinel的支持。
feign:
sentinel:
enabled: true
為遠(yuǎn)程調(diào)用實(shí)現(xiàn)容錯
(1)需要在訂單微服務(wù)shop-order中,為遠(yuǎn)程調(diào)用接口實(shí)現(xiàn)容錯方法。這里,先為用戶微服務(wù)實(shí)現(xiàn)容錯。在訂單微服務(wù)中新建io.binghe.shop.order.Feign.fallback 包,并在 io.binghe.shop.order.Feign.fallback包下創(chuàng)建UserServiceFallBack類實(shí)現(xiàn)UserService接口,用于調(diào)用用戶微服務(wù)的容錯類,如下所示。
/**
* @author binghe (公眾號:冰河技術(shù))
* @version 1.0.0
* @description 用戶服務(wù)容錯類
*/
@Component
public class UserServiceFallBack implements UserService {
@Override
public User getUser(Long uid) {
User user = new User();
user.setId(-1L);
return user;
}
}
注意:容錯類需要實(shí)現(xiàn)一個被容錯的接口,并實(shí)現(xiàn)這個接口的方法。
接下來,在訂單微服務(wù)的io.binghe.shop.order.Feign.UserService接口上的@FeignClient注解上指定容錯類,如下所示。
/**
* @author binghe (公眾號:冰河技術(shù))
* @version 1.0.0
* @description 調(diào)用用戶微服務(wù)的接口
*/
@FeignClient(value = "server-user", fallback = UserServiceFallBack.class)
public interface UserService {
@GetMapping(value = "/user/get/{uid}")
User getUser(@PathVariable("uid") Long uid);
}
(2)在訂單微服務(wù)中的 io.binghe.shop.order.Feign.fallback包下創(chuàng)建ProductServiceFallBack類實(shí)現(xiàn)ProductService接口,用于調(diào)用商品微服務(wù)的容錯類,如下所示。
/**
* @author binghe (公眾號:冰河技術(shù))
* @version 1.0.0
* @description 商品微服務(wù)的容錯類
*/
@Component
public class ProductServiceFallBack implements ProductService {
@Override
public Product getProduct(Long pid) {
Product product = new Product();
product.setId(-1L);
return product;
}
@Override
public ResultupdateCount(Long pid, Integer count) {
Resultresult = new Result<>();
result.setCode(1001);
result.setCodeMsg("觸發(fā)了容錯邏輯");
return result;
}
}
接下來,在訂單微服務(wù)的io.binghe.shop.order.fegin.ProductService接口的@FeignClient注解上指定容錯類,如下所示。
/**
* @author binghe (公眾號:冰河技術(shù))
* @version 1.0.0
* @description 調(diào)用商品微服務(wù)的接口
*/
@FeignClient(value = "server-product", fallback = ProductServiceFallBack.class)
public interface ProductService {
/**
* 獲取商品信息
*/
@GetMapping(value = "/product/get/{pid}")
Product getProduct(@PathVariable("pid") Long pid);
/**
* 更新庫存數(shù)量
*/
@GetMapping(value = "/product/update_count/{pid}/{count}")
ResultupdateCount(@PathVariable("pid") Long pid, @PathVariable("count") Integer count);
}
(3)修改訂單微服務(wù)的業(yè)務(wù)實(shí)現(xiàn)類中提交訂單的業(yè)務(wù)方法,這里修改的方法位于io.binghe.shop.order.service.impl.OrderServiceV6Impl類中,同時需要將類上的@Service注解中指定bean的名稱為orderServiceV6。
@Slf4j
@Service("orderServiceV6")
public class OrderServiceV6Impl implements OrderService {
//省略所有代碼
}
在提交訂單的業(yè)務(wù)方法中,修改前的代碼片段如下所示。
User user = userService.getUser(orderParams.getUserId());
if (user == null){
throw new RuntimeException("未獲取到用戶信息: " + JSONObject.toJSONString(orderParams));
}
Product product = productService.getProduct(orderParams.getProductId());
if (product == null){
throw new RuntimeException("未獲取到商品信息: " + JSONObject.toJSONString(orderParams));
}
//#####################省略N行代碼##########################
Resultresult = productService.updateCount(orderParams.getProductId(), orderParams.getCount());
if (result.getCode() != HttpCode.SUCCESS){
throw new RuntimeException("庫存扣減失敗");
}
修改后的代碼片段如下所示。
User user = userService.getUser(orderParams.getUserId());
if (user == null){
throw new RuntimeException("未獲取到用戶信息: " + JSONObject.toJSONString(orderParams));
}
if (user.getId() == -1){
throw new RuntimeException("觸發(fā)了用戶微服務(wù)的容錯邏輯: " + JSONObject.toJSONString(orderParams));
}
Product product = productService.getProduct(orderParams.getProductId());
if (product == null){
throw new RuntimeException("未獲取到商品信息: " + JSONObject.toJSONString(orderParams));
}
if (product.getId() == -1){
throw new RuntimeException("觸發(fā)了商品微服務(wù)的容錯邏輯: " + JSONObject.toJSONString(orderParams));
}
//#####################省略N行代碼##########################
Resultresult = productService.updateCount(orderParams.getProductId(), orderParams.getCount());
if (result.getCode() == 1001){
throw new RuntimeException("觸發(fā)了商品微服務(wù)的容錯邏輯: " + JSONObject.toJSONString(orderParams));
}
if (result.getCode() != HttpCode.SUCCESS){
throw new RuntimeException("庫存扣減失敗");
}
可以看到,修改后的提交訂單的業(yè)務(wù)方法主要增加了服務(wù)容錯的判斷邏輯。
(4)在io.binghe.shop.order.controller.OrderController中注入bean名稱為orderServiceV6的OrderService對象,如下所示。
@Autowired
@Qualifier(value = "orderServiceV6")
private OrderService orderService;
至此,我們在項(xiàng)目中使用Sentinel實(shí)現(xiàn)了服務(wù)容錯的功能。
測試服務(wù)容錯
(1)停掉所有的商品微服務(wù)(也就是只啟動用戶微服務(wù)和訂單微服務(wù)),在瀏覽器中訪問http://localhost:8080/order/submit_order?userId=1001&productId=1001&count=1,結(jié)果如下所示。
返回的原始數(shù)據(jù)如下所示。
{"code":500,"codeMsg":"執(zhí)行失敗","data":"觸發(fā)了商品微服務(wù)的容錯邏輯: {\"count\":1,\"empty\":false,\"productId\":1001,\"userId\":1001}"}說明停掉所有的商品微服務(wù)后,觸發(fā)了商品微服務(wù)的容錯邏輯。
(2)停掉所有的用戶微服務(wù)(也就是只啟動商品微服務(wù)和訂單微服務(wù))在瀏覽器中訪問http://localhost:8080/order/submit_order?userId=1001&productId=1001&count=1,結(jié)果如下所示。
返回的原始數(shù)據(jù)如下所示。
{"code":500,"codeMsg":"執(zhí)行失敗","data":"觸發(fā)了用戶微服務(wù)的容錯邏輯: {\"count\":1,\"empty\":false,\"productId\":1001,\"userId\":1001}"}(3)停掉所有的用戶微服務(wù)和商品微服務(wù)(也就是只啟動訂單微服務(wù)),在瀏覽器中訪問http://localhost:8080/order/submit_order?userId=1001&productId=1001&count=1,結(jié)果如下所示。
返回的原始數(shù)據(jù)如下所示。
{"code":500,"codeMsg":"執(zhí)行失敗","data":"觸發(fā)了用戶微服務(wù)的容錯邏輯: {\"count\":1,\"empty\":false,\"productId\":1001,\"userId\":1001}"}說明項(xiàng)目集成Sentinel成功實(shí)現(xiàn)了服務(wù)的容錯功能。
容錯擴(kuò)展
如果想要在訂單微服務(wù)中獲取到容錯時的具體信息時,可以按照如下方式實(shí)現(xiàn)容錯方案。
實(shí)現(xiàn)容錯時獲取異常
(1)在訂單微服務(wù)shop-order中新建io.binghe.shop.order.fegin.fallback.factory包,在io.binghe.shop.order.fegin.fallback.factory包中新建UserServiceFallBackFactory類,并實(shí)現(xiàn)FallbackFactory接口,F(xiàn)allbackFactory接口的泛型指定為UserService,源碼如下所示。
/**
* @author binghe (公眾號:冰河技術(shù))
* @version 1.0.0
* @description 用戶微服務(wù)容錯Factory
*/
@Component
public class UserServiceFallBackFactory implements FallbackFactory{
@Override
public UserService create(Throwable cause) {
return new UserService() {
@Override
public User getUser(Long uid) {
User user = new User();
user.setId(-1L);
return user;
}
};
}
}
(2)在訂單微服務(wù)的 io.binghe.shop.order.fegin.UserService 接口上的@FeignClient注解上指定fallbackFactory屬性,如下所示。
/**
* @author binghe (公眾號:冰河技術(shù))
* @version 1.0.0
* @description 調(diào)用用戶微服務(wù)的接口
*/
//@FeignClient(value = "server-user", fallback = UserServiceFallBack.class)
@FeignClient(value = "server-user", fallbackFactory = UserServiceFallBackFactory.class)
public interface UserService {
@GetMapping(value = "/user/get/{uid}")
User getUser(@PathVariable("uid") Long uid);
}
(3)在io.binghe.shop.order.fegin.fallback.factory包中新建ProductServiceFallBackFactory類,并實(shí)現(xiàn)FallbackFactory接口,F(xiàn)allbackFactory接口的泛型指定為ProductService,源碼如下所示。
/**
* @author binghe (公眾號:冰河技術(shù))
* @version 1.0.0
* @description 商品微服務(wù)容錯Factory
*/
@Component
public class ProductServiceFallBackFactory implements FallbackFactory{
@Override
public ProductService create(Throwable cause) {
return new ProductService() {
@Override
public Product getProduct(Long pid) {
Product product = new Product();
product.setId(-1L);
return product;
}
@Override
public ResultupdateCount(Long pid, Integer count) {
Resultresult = new Result<>();
result.setCode(1001);
result.setCodeMsg("觸發(fā)了容錯邏輯");
return result;
}
};
}
}
(4)在訂單微服務(wù)的 io.binghe.shop.order.fegin.ProductService 接口上的@FeignClient注解上指定fallbackFactory屬性,如下所示。
/**
* @author binghe (公眾號:冰河技術(shù))
* @version 1.0.0
* @description 調(diào)用商品微服務(wù)的接口
*/
//@FeignClient(value = "server-product", fallback = ProductServiceFallBack.class)
@FeignClient(value = "server-product", fallbackFactory = ProductServiceFallBackFactory.class)
public interface ProductService {
/**
* 獲取商品信息
*/
@GetMapping(value = "/product/get/{pid}")
Product getProduct(@PathVariable("pid") Long pid);
/**
* 更新庫存數(shù)量
*/
@GetMapping(value = "/product/update_count/{pid}/{count}")
ResultupdateCount(@PathVariable("pid") Long pid, @PathVariable("count") Integer count);
}
測試服務(wù)容錯
與“Feign整合Sentinel實(shí)現(xiàn)容錯-測試服務(wù)容錯”中的測試方法相同,這里不再贅述。
至此,使用Sentinel實(shí)現(xiàn)限流和容錯的功能就完成了。
最后,需要注意的是:使用Sentinel實(shí)現(xiàn)服務(wù)容錯時,fallback和fallbackFactory只能使用其中一種方式實(shí)現(xiàn)服務(wù)容錯,二者不能同時使用。
新聞標(biāo)題:服務(wù)容錯:項(xiàng)目整合Sentinel實(shí)現(xiàn)限流與容錯
標(biāo)題URL:http://m.5511xx.com/article/dpggjip.html


咨詢
建站咨詢
