Spring Cloud Alibaba系列之-Sentinel服務(wù)降級(jí)與熔斷(八)

一询刹、熔斷降級(jí)

除了流量控制以外烘跺,對(duì)調(diào)用鏈路中不穩(wěn)定的資源進(jìn)行熔斷降級(jí)也是保障高可用的重要措施之一。一個(gè)服務(wù)常常會(huì)調(diào)用別的模塊返弹,可能是另外的一個(gè)遠(yuǎn)程服務(wù)分瘦、數(shù)據(jù)庫(kù),或者第三方 API 等琉苇。例如嘲玫,支付的時(shí)候,可能需要遠(yuǎn)程調(diào)用銀聯(lián)提供的 API并扇;查詢某個(gè)商品的價(jià)格去团,可能需要進(jìn)行數(shù)據(jù)庫(kù)查詢。然而穷蛹,這個(gè)被依賴服務(wù)的穩(wěn)定性是不能保證的土陪。如果依賴的服務(wù)出現(xiàn)了不穩(wěn)定的情況,請(qǐng)求的響應(yīng)時(shí)間變長(zhǎng)肴熏,那么調(diào)用服務(wù)的方法的響應(yīng)時(shí)間也會(huì)變長(zhǎng)鬼雀,線程會(huì)產(chǎn)生堆積,最終可能耗盡業(yè)務(wù)自身的線程池蛙吏,服務(wù)本身也變得不可用源哩。

服務(wù)調(diào)用

現(xiàn)代微服務(wù)架構(gòu)都是分布式的,由非常多的服務(wù)組成鸦做。不同服務(wù)之間相互調(diào)用励烦,組成復(fù)雜的調(diào)用鏈路。以上的問(wèn)題在鏈路調(diào)用中會(huì)產(chǎn)生放大的效果泼诱。復(fù)雜鏈路上的某一環(huán)不穩(wěn)定坛掠,就可能會(huì)層層級(jí)聯(lián),最終導(dǎo)致整個(gè)鏈路都不可用。因此我們需要對(duì)不穩(wěn)定的弱依賴服務(wù)調(diào)用進(jìn)行熔斷降級(jí)屉栓,暫時(shí)切斷不穩(wěn)定調(diào)用舷蒲,避免局部不穩(wěn)定因素導(dǎo)致整體的雪崩。熔斷降級(jí)作為保護(hù)自身的手段友多,通常在客戶端(調(diào)用端)進(jìn)行配置牲平。

二、熔斷&降級(jí)區(qū)分

2.1 服務(wù)降級(jí)舉例

下單接口調(diào)用遠(yuǎn)程更新產(chǎn)品銷量接口夷陋,當(dāng)某一天更新產(chǎn)品銷量接口掛了欠拾,下單服務(wù)會(huì)在本地提供一個(gè)降級(jí)接口來(lái)記錄xx訂單xx產(chǎn)品需要更新銷量,這條記錄可以寫入對(duì)應(yīng)的記錄數(shù)據(jù)表或者消息中間件中骗绕,然后定時(shí)掃描來(lái)完成產(chǎn)品銷量更新的任務(wù)藐窄,這就是服務(wù)降級(jí)。

2.2 服務(wù)熔斷舉例

遠(yuǎn)程更新產(chǎn)品銷量接口暫時(shí)已經(jīng)掛了酬土,如果下單接口每次下單時(shí)還是去請(qǐng)求網(wǎng)絡(luò)調(diào)用遠(yuǎn)程更新產(chǎn)品銷量接口荆忍,然后再走本地降級(jí)接口,這樣會(huì)浪費(fèi)系統(tǒng)資源撤缴,如果用熔斷功能刹枉,可以設(shè)定30秒內(nèi)加積分接口失敗5次就熔斷的規(guī)則,后面執(zhí)行下單接口就不會(huì)再網(wǎng)絡(luò)請(qǐng)求遠(yuǎn)程更新產(chǎn)品銷量接口了屈呕,而是直接調(diào)用本地的降級(jí)接口微宝,記錄補(bǔ)償更新產(chǎn)品銷量日志。過(guò)了30秒后再嘗試網(wǎng)絡(luò)請(qǐng)求遠(yuǎn)程更新產(chǎn)品銷量接口(更新產(chǎn)品銷量接口可能會(huì)在未來(lái)某個(gè)時(shí)間修復(fù)啟動(dòng))

3.3 關(guān)系

熔斷和降級(jí)一般是聯(lián)系在一起的虎眨,熔斷時(shí)需要調(diào)用降級(jí)接口蟋软。Spring Cloud Alibaba用Sentinel來(lái)做服務(wù)限流、熔斷嗽桩、降級(jí)

三岳守、實(shí)現(xiàn)[order-service]調(diào)用[product-service]更新產(chǎn)品銷量功能

3.1 module [product-service]
3.1.1 配置maven

[product-service] pom.xml完整配置如下

<?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>ac-mall-cloud</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.ac</groupId>
    <artifactId>product-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</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>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

    </dependencies>

</project>
3.1.2 編寫更新產(chǎn)品銷量代碼

新建com.ac.product 包,創(chuàng)建ProductApplication碌冶、ModulePrePath湿痢、ProductApi 類

@SpringBootApplication
public class ProductApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class);
    }
}
public class ModulePrePath {

    public static final String API = "api";
}
@RestController
@RequestMapping(ModulePrePath.API+"/products")
public class ProductApi {

    @PutMapping("/{productId}")
    public void updateSales(@PathVariable String productId){
        System.out.println("商品:"+productId+"更新銷量數(shù)");
    }
}
3.1.3 配置文件

新建application.yml配置文件,配置端口為8030扑庞,完整配置如下

server:
  port: 8030

spring:
  application:
    name: product-service

  cloud:
    nacos:
      discovery:
        server-addr: 47.105.146.74:8848

    sentinel:
      transport:
        dashboard: 127.0.0.1:8888

[product-service]項(xiàng)目完整結(jié)構(gòu)如下

[product-service]項(xiàng)目完整結(jié)構(gòu)
3.2 module [gateway-service] 添加 [product-service] 路由

打開[gateway-service] application.yml配置文件譬重,添加[product-service] 路由

spring:
    gateway:
      routes: 
        - id: product-service-api
          uri: lb://product-service
          predicates:
            - Path=/products/**

[gateway-service] application.yml完整配置如下

server:
  port: 7010

spring:
  application:
    name: gateway-service

  redis:
    database: 0
    host: 39.108.250.186
    port: 6379
    password: xxxx
    jedis:
      pool:
        max-active: 500  #連接池的最大數(shù)據(jù)庫(kù)連接數(shù)。設(shè)為0表示無(wú)限制
        max-idle: 20   #最大空閑數(shù)
        max-wait: -1
        min-idle: 5
    timeout: 1000

  cloud:
    nacos:
      discovery:
        server-addr: 47.105.146.74:8848

    gateway:
      routes:                       # 路由數(shù)組[路由 就是指定當(dāng)請(qǐng)求滿足什么條件的時(shí)候轉(zhuǎn)到哪個(gè)微服務(wù)]
        - id: user-service-api          # 當(dāng)前路由的標(biāo)識(shí), 要求唯一
          uri: lb://user-service  # lb指的是從nacos中按照名稱獲取微服務(wù),并遵循負(fù)載均衡策略
          predicates:                # 斷言(就是路由轉(zhuǎn)發(fā)要滿足的條件)
            - Path=/users/**       # 當(dāng)請(qǐng)求路徑滿足Path指定的規(guī)則時(shí),才進(jìn)行路由轉(zhuǎn)發(fā)
            #filters:                   # 過(guò)濾器,請(qǐng)求在傳遞過(guò)程中可以通過(guò)過(guò)濾器對(duì)其進(jìn)行一定的修改
            #- StripPrefix=1           # 轉(zhuǎn)發(fā)之前去掉1層路徑

        - id: order-service-api
          uri: lb://order-service
          predicates:
            - Path=/orders/**

        - id: product-service-api
          uri: lb://product-service
          predicates:
            - Path=/products/**

        - id: auth-service-api
          uri: lb://auth-service
          predicates:
            - Path=/auth/**

        - id: jianshu_route
          uri: http://www.reibang.com/
          predicates:
            - Query=url,jianshu
3.3 [order-service]下單接口添加更新產(chǎn)品銷量邏輯
3.3.1 創(chuàng)建feign接口ProductServiceClient

在feign包下創(chuàng)建ProductServiceClient類

@FeignClient("product-service")
public interface ProductServiceClient {

    /**
     * 更新產(chǎn)品銷量
     * @param productId
     */
    @PutMapping(ModulePrePath.API+"/products/{productId}")
    void updateSales(@PathVariable("productId") String productId);
}
ProductServiceClient接口
3.3.2 更新下單接口

編輯OrderServiceImpl類嫩挤,在下單方法里加入更新產(chǎn)品銷量邏輯

package com.ac.order.service.impl;

import com.ac.order.dao.OrderDao;
import com.ac.order.dto.UserDto;
import com.ac.order.entity.Order;
import com.ac.order.feign.ProductServiceClient;
import com.ac.order.feign.UserServiceClient;
import com.ac.order.service.IOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.Date;
import java.util.UUID;

/**
 * @author Alan Chen
 * @description
 * @date 2020/10/15
 */
@Service
public class OrderServiceImpl implements IOrderService {

    @Autowired
    OrderDao orderDao;

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    UserServiceClient userServiceClient;

    @Autowired
    ProductServiceClient productServiceClient;

    //final static String USER_SERVICE_URL="http://127.0.0.1:8010/users/{userId}";

    final static String USER_SERVICE_URL="http://user-service/users/{userId}"; //用服務(wù)名來(lái)替換IP

    public Order makeOrder(String productId, String userId) {

        /**
         * RestTemplate是java創(chuàng)造出來(lái)的害幅,在java能夠訪問(wèn)到網(wǎng)絡(luò)資源的包是java.net.URLConnenction/Socket
         * RestTemplate是對(duì)URLConnenction的封裝
         * apache--HttpClient 也是對(duì)URLConnenction/HttpURLConnenction的封裝
         * oKHttp 也封裝了URLConnenction
         * netty/rpc/grpc/thirt/tomcat
         */

        // 1、根據(jù)用戶ID調(diào)用用戶服務(wù)接口數(shù)據(jù)岂昭,查詢用戶的名字
        //UserDto userDto = restTemplate.getForObject(USER_SERVICE_URL,UserDto.class,userId);

        //換成OpenFeign
        UserDto userDto = userServiceClient.getUser(userId);

        String userName=userDto.getUserName();

        // 2、生成訂單
        Order order = new Order();
        order.setId(UUID.randomUUID().toString());
        order.setCreateTime(new Date());
        order.setPriceFen(1600L);
        order.setUserId(userId);
        order.setUserName(userName);
        order.setProductId(productId);
        order.setOrderNo(UUID.randomUUID().toString());

        // 3、保存數(shù)據(jù)庫(kù)
        orderDao.insert(order);

        // 4约啊、更新產(chǎn)品銷量
        productServiceClient.updateSales(productId);

        return order;
    }
}
更新產(chǎn)品銷量
3.4 啟動(dòng)微服務(wù)訪問(wèn)下單接口

依次啟動(dòng)[user-service]邑遏、[order-service]、[auth-service]恰矩、[gateway-service]记盒、[product-service]服務(wù),通過(guò)[gateway-service]訪問(wèn)[order-service]的下單接口

訪問(wèn)下單接口
產(chǎn)品服務(wù)控制臺(tái)

接口訪問(wèn)成功外傅,同時(shí)產(chǎn)品服務(wù)控制臺(tái)也打印了對(duì)應(yīng)的成功信息

3.5 模擬[product-service]掉線

手動(dòng)關(guān)閉[product-service]纪吮,模擬[product-service]掉線,再次訪問(wèn)下單接口

下單接口失敗

我們看到下單接口訪問(wèn)失敗了萎胰,提示的信息就是無(wú)法連接到[product-service]的更新產(chǎn)品銷量接口

四碾盟、 [order-service]提供降級(jí)接口,記錄更新產(chǎn)品銷量日志

4.1 Feign整合Sentinel實(shí)現(xiàn)降級(jí)處理
4.1.1 添加Feign整合Sentinel配置

編輯[order-service] application.yml配置文件技竟,添加Feign整合Sentinel配置

feign:
  sentinel:
    #為feign整合Sentinel
    enabled: true

[order-service] application.yml 完整配置如下

server:
  port: 8020

spring:
  application:
    name: order-service

  cloud:
    nacos:
      discovery:
        server-addr: 47.105.146.74:8848

    sentinel:
      transport:
        dashboard: 127.0.0.1:8888

feign:
  sentinel:
    #為feign整合Sentinel
    enabled: true
4.1.2 創(chuàng)建降級(jí)接口ProductFeignClientFallbackFactory

新建包com.ac.order.fallback 并創(chuàng)建降級(jí)接口ProductFeignClientFallbackFactory

package com.ac.order.fallback;

import com.ac.order.feign.ProductServiceClient;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

/**
 * @author Alan Chen
 * @description
 * @date 2021/4/10
 */
@Component
public class ProductFeignClientFallbackFactory implements FallbackFactory<ProductServiceClient> {

    public ProductServiceClient create(Throwable throwable) {

        return new ProductServiceClient() {
            public void updateSales(String productId) {
                System.out.println("調(diào)用產(chǎn)品更新銷量接口失敗冰肴,記錄日志(記錄到數(shù)據(jù)庫(kù)或消息中間件),產(chǎn)品:"+productId+"需要更新銷量");
            }
        };
    }
}
4.1.3 配置降級(jí)接口

編輯ProductServiceClient類榔组,在@FeignClient注解中添加降級(jí)接口配置

@FeignClient(name="product-service",fallbackFactory = ProductFeignClientFallbackFactory.class)

package com.ac.order.feign;

import com.ac.order.constant.ModulePrePath;
import com.ac.order.fallback.ProductFeignClientFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;

/**
 * @author Alan Chen
 * @description 產(chǎn)品接口
 * @date 2021/4/10
 */
@FeignClient(name="product-service",fallbackFactory = ProductFeignClientFallbackFactory.class)
public interface ProductServiceClient {

    /**
     * 更新產(chǎn)品銷量
     * @param productId
     */
    @PutMapping(ModulePrePath.API+"/products/{productId}")
    void updateSales(@PathVariable("productId") String productId);
}
4.1.4 測(cè)試降級(jí)接口

重啟[order-service]服務(wù)熙尉,注意[product-service]不要啟動(dòng),再次調(diào)用下單接口

下單接口訪問(wèn)成功
降級(jí)接口

我們看到的結(jié)果是搓扯,下單接口訪問(wèn)成功了检痰,同時(shí)[order-service]控制臺(tái)打印了降級(jí)接口的內(nèi)容

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锨推,隨后出現(xiàn)的幾起案子铅歼,更是在濱河造成了極大的恐慌,老刑警劉巖爱态,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谭贪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡锦担,警方通過(guò)查閱死者的電腦和手機(jī)俭识,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)洞渔,“玉大人套媚,你說(shuō)我怎么就攤上這事〈沤罚” “怎么了堤瘤?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)浆熔。 經(jīng)常有香客問(wèn)我本辐,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任慎皱,我火速辦了婚禮老虫,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘茫多。我一直安慰自己祈匙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布天揖。 她就那樣靜靜地躺著夺欲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪今膊。 梳的紋絲不亂的頭發(fā)上些阅,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音万细,去河邊找鬼扑眉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛赖钞,可吹牛的內(nèi)容都是我干的腰素。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼雪营,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼弓千!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起献起,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤洋访,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后谴餐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姻政,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年岂嗓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了汁展。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡厌殉,死狀恐怖食绿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情公罕,我是刑警寧澤器紧,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站楼眷,受9級(jí)特大地震影響铲汪,放射性物質(zhì)發(fā)生泄漏熊尉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一桥状、第九天 我趴在偏房一處隱蔽的房頂上張望帽揪。 院中可真熱鬧硝清,春花似錦辅斟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至蔗崎,卻和暖如春酵幕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缓苛。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工芳撒, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人未桥。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓笔刹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親冬耿。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舌菜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容