成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

SpringCloudAlibaba之服務(wù)容錯(cuò)組件-Sentinel[代碼篇]

Sentinel與控制臺(tái)通信原理

在基礎(chǔ)篇中我們學(xué)習(xí)了如何為項(xiàng)目整合Sentinel,并搭建了Sentinel的可視化控制臺(tái),介紹及演示了各種Sentinel所支持的規(guī)則配置方式。本文則對(duì)Sentinel進(jìn)行更進(jìn)一步的介紹。

為洞頭等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計(jì)制作服務(wù),及洞頭網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作、洞頭網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!

首先我們來了解控制臺(tái)是如何獲取到微服務(wù)的監(jiān)控信息的:

微服務(wù)集成Sentinel需要添加spring-cloud-starter-alibaba-sentinel依賴,該依賴中包含了sentinel-transport-simple-http模塊。集成了該模塊后,微服務(wù)就會(huì)通過配置文件中所配置的連接地址,將自身注冊(cè)到Sentinel控制臺(tái)上,并通過心跳機(jī)制告知存活狀態(tài),由此可知Sentinel是實(shí)現(xiàn)了一套服務(wù)發(fā)現(xiàn)機(jī)制的。

如下圖:
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]

通過該機(jī)制,從Sentinel控制臺(tái)的機(jī)器列表中就可以查看到Sentinel客戶端(即微服務(wù))的通信地址及端口號(hào):
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]

如此一來,Sentinel控制臺(tái)就可以實(shí)現(xiàn)與微服務(wù)通信了,當(dāng)需要獲取微服務(wù)的監(jiān)控信息時(shí),Sentinel控制臺(tái)會(huì)定時(shí)調(diào)用微服務(wù)所暴露出來的監(jiān)控API,這樣就可以實(shí)現(xiàn)實(shí)時(shí)獲取微服務(wù)的監(jiān)控信息。

另外一個(gè)問題就是使用控制臺(tái)配置規(guī)則時(shí),控制臺(tái)是如何將規(guī)則發(fā)送到各個(gè)微服務(wù)的呢?同理,想要將配置的規(guī)則推送給微服務(wù),只需要調(diào)用微服務(wù)上接收推送規(guī)則的API即可。

我們可以通過訪問http://{微服務(wù)注冊(cè)的ip地址}:8720/api接口查看微服務(wù)暴露給Sentinel控制臺(tái)調(diào)用的API,如下:
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]

相關(guān)源碼:

  • 注冊(cè)/心跳機(jī)制:com.alibaba.csp.sentinel.transport.heartbeat.SimpleHttpHeartbeatSender
  • 通信API:com.alibaba.csp.sentinel.command.CommandHandler的實(shí)現(xiàn)類

Sentinel API的使用

本小節(jié)簡(jiǎn)單介紹一下在代碼中如何使用Sentinel API,Sentinel主要有以下三個(gè)API:

  • SphU:添加需要讓sentinel監(jiān)控、保護(hù)的資源
  • Tracer:對(duì)業(yè)務(wù)異常進(jìn)行統(tǒng)計(jì)(非 BlockException 異常)
  • ContextUtil:上下文工具類,通常用于標(biāo)識(shí)調(diào)用來源

示例代碼如下:

@GetMapping("/test-sentinel-api")
public String testSentinelAPI(@RequestParam(required = false) String a) {
    String resourceName = "test-sentinel-api";
    // 這里不使用try-with-resources是因?yàn)門racer.trace會(huì)統(tǒng)計(jì)不上異常
    Entry entry = null;
    try {
        // 定義一個(gè)sentinel保護(hù)的資源,名稱為test-sentinel-api
        entry = SphU.entry(resourceName);
        // 標(biāo)識(shí)對(duì)test-sentinel-api調(diào)用來源為test-origin(用于流控規(guī)則中“針對(duì)來源”的配置)
        ContextUtil.enter(resourceName, "test-origin");
        // 模擬執(zhí)行被保護(hù)的業(yè)務(wù)邏輯耗時(shí)
        Thread.sleep(100);
        return a;
    } catch (BlockException e) {
        // 如果被保護(hù)的資源被限流或者降級(jí)了,就會(huì)拋出BlockException
        log.warn("資源被限流或降級(jí)了", e);
        return "資源被限流或降級(jí)了";
    } catch (InterruptedException e) {
        // 對(duì)業(yè)務(wù)異常進(jìn)行統(tǒng)計(jì)
        Tracer.trace(e);
        return "發(fā)生InterruptedException";
    } finally {
        if (entry != null) {
            entry.exit();
        }

        ContextUtil.exit();
    }
}

對(duì)幾個(gè)可能有疑惑的點(diǎn)說明一下:

  • 資源名:可任意填寫,只要是唯一的即可,通常使用接口名
  • ContextUtil.enter:在該例子中,用于標(biāo)識(shí)對(duì)test-sentinel-api的調(diào)用來源均為test-origin。例如使用postman或其他請(qǐng)求方式調(diào)用了該資源,其來源都會(huì)被標(biāo)識(shí)為test-origin
  • Tracer.trace:降級(jí)規(guī)則中可以針對(duì)異常比例或異常數(shù)的閾值進(jìn)行降級(jí),而Sentinel只會(huì)對(duì)BlockException及其子類進(jìn)行統(tǒng)計(jì),其他異常不在統(tǒng)計(jì)范圍,所以需要使用Tracer.trace手動(dòng)統(tǒng)計(jì)。1.3.1 版本開始支持自動(dòng)統(tǒng)計(jì),將在下一小節(jié)進(jìn)行介紹

相關(guān)官方文檔:

  • 如何使用#其它-api

@SentinelResource注解

經(jīng)過上一小節(jié)的代碼示例,可以看到這些Sentinel API的使用方式并不是很優(yōu)雅,有點(diǎn)類似于使用I/O流API的感覺,顯得代碼比較臃腫。好在Sentinel在1.3.1 版本開始支持@SentinelResource注解,該注解可以讓我們避免去寫這種臃腫不美觀的代碼。但即便如此,也還是有必要去學(xué)習(xí)Sentinel API的使用方式,因?yàn)槠涞讓舆€是得通過這些API來實(shí)現(xiàn)。

學(xué)習(xí)一個(gè)注解除了需要知道它能干什么之外,還得了解其支持的屬性作用,下表總結(jié)了@SentinelResource注解的屬性:

屬性作用是否必須
value 資源名稱
entryType entry類型,標(biāo)記流量的方向,取值IN/OUT,默認(rèn)是OUT
blockHandler 處理BlockException的函數(shù)名稱
blockHandlerClass 存放blockHandler的類。對(duì)應(yīng)的處理函數(shù)必須static修飾,否則無法解析,其他要求:同blockHandler
fallback 用于在拋出異常的時(shí)候提供fallback處理邏輯。fallback函數(shù)可以針對(duì)所有類型的異常(除了exceptionsToIgnore 里面排除掉的異常類型)進(jìn)行處理
fallbackClass【1.6支持】 存放fallback的類。對(duì)應(yīng)的處理函數(shù)必須static修飾,否則無法解析,其他要求:同fallback
defaultFallback【1.6支持】 用于通用的 fallback 邏輯。默認(rèn)fallback函數(shù)可以針對(duì)所有類型的異常(除了exceptionsToIgnore 里面排除掉的異常類型)進(jìn)行處理。若同時(shí)配置了 fallback 和 defaultFallback,以fallback為準(zhǔn)
exceptionsToIgnore【1.6支持】 指定排除掉哪些異常。排除的異常不會(huì)計(jì)入異常統(tǒng)計(jì),也不會(huì)進(jìn)入fallback邏輯,而是原樣拋出
exceptionsToTrace 需要trace的異常 Throwable

blockHandler,處理BlockException函數(shù)的要求:

  1. 必須是public
  2. 返回類型與原方法一致
  3. 參數(shù)類型需要和原方法相匹配,并在最后加BlockException類型的參數(shù)
  4. 默認(rèn)需和原方法在同一個(gè)類中。若希望使用其他類的函數(shù),可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法

fallback函數(shù)要求:

  1. 返回類型與原方法一致
  2. 參數(shù)類型需要和原方法相匹配,Sentinel 1.6開始,也可在方法最后加Throwable類型的參數(shù)
  3. 默認(rèn)需和原方法在同一個(gè)類中。若希望使用其他類的函數(shù),可配置 fallbackClass ,并指定fallbackClass里面的方法

defaultFallback函數(shù)要求:

  1. 返回類型與原方法一致
  2. 方法參數(shù)列表為空,或者有一個(gè)Throwable類型的參數(shù)
  3. 默認(rèn)需要和原方法在同一個(gè)類中。若希望使用其他類的函數(shù),可配置 fallbackClass ,并指定 fallbackClass 里面的方法

現(xiàn)在我們已經(jīng)對(duì)@SentinelResource注解有了一個(gè)比較全面的了解,接下來使用@SentinelResource注解重構(gòu)之前的代碼,直觀地了解下該注解帶來了哪些便利,重構(gòu)后的代碼如下:

@GetMapping("/test-sentinel-resource")
@SentinelResource(
        value = "test-sentinel-resource",
        blockHandler = "blockHandlerFunc",
        fallback = "fallbackFunc"
)
public String testSentinelResource(@RequestParam(required = false) String a)
        throws InterruptedException {
    // 模擬執(zhí)行被保護(hù)的業(yè)務(wù)邏輯耗時(shí)
    Thread.sleep(100);

    return a;
}

/**
 * 處理BlockException的函數(shù)(處理限流)
 */
public String blockHandlerFunc(String a, BlockException e) {
    // 如果被保護(hù)的資源被限流或者降級(jí)了,就會(huì)拋出BlockException
    log.warn("資源被限流或降級(jí)了.", e);
    return "資源被限流或降級(jí)了";
}

/**
 * 1.6 之前處理降級(jí)
 * 1.6 開始可以針對(duì)所有類型的異常(除了 exceptionsToIgnore 里面排除掉的異常類型)進(jìn)行處理
 */
public String fallbackFunc(String a) {
    return "發(fā)生異常了";
}

注:@SentinelResource注解目前不支持標(biāo)識(shí)調(diào)用來源

Tips:

1.6.0 之前的版本 fallback 函數(shù)只針對(duì)降級(jí)異常(DegradeException)進(jìn)行處理,不能針對(duì)業(yè)務(wù)異常進(jìn)行處理

blockHandlerfallback 都進(jìn)行了配置,則被限流降級(jí)而拋出 BlockException 時(shí)只會(huì)進(jìn)入
blockHandler 處理邏輯。若未配置 blockHandlerfallbackdefaultFallback,則被限流降級(jí)時(shí)會(huì)將 BlockException 直接拋出

從 1.3.1 版本開始,注解方式定義資源支持自動(dòng)統(tǒng)計(jì)業(yè)務(wù)異常,無需手動(dòng)調(diào)用 Tracer.trace(ex) 來記錄業(yè)務(wù)異常。Sentinel 1.3.1 以前的版本需要自行調(diào)用 Tracer.trace(ex) 來記錄業(yè)務(wù)異常

@SentinelResource注解相關(guān)源碼:

  • com.alibaba.csp.sentinel.annotation.aspectj.AbstractSentinelAspectSupport
  • com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect

相關(guān)官方文檔:

  • 注解支持
  • 官方代碼示例

RestTemplate整合Sentinel

如果有了解過Hystrix的話,應(yīng)該就會(huì)知道Hystrix除了可以對(duì)當(dāng)前服務(wù)的接口進(jìn)行容錯(cuò),還可以對(duì)服務(wù)提供者(被調(diào)用方)的接口進(jìn)行容錯(cuò)。到目前為止,我們只介紹了在Sentinel控制臺(tái)對(duì)當(dāng)前服務(wù)的接口添加相關(guān)規(guī)則進(jìn)行容錯(cuò),但還沒有介紹如何對(duì)服務(wù)提供者的接口進(jìn)行容錯(cuò)。

實(shí)際上有了前面的鋪墊,現(xiàn)在想要實(shí)現(xiàn)對(duì)服務(wù)提供者的接口進(jìn)行容錯(cuò)就很簡(jiǎn)單了,我們都知道在Spring Cloud體系中可以通過RestTemplate或Feign實(shí)現(xiàn)微服務(wù)之間的通信。所以只需要在RestTemplate或Feign上做文章就可以了,本小節(jié)先以RestTemplate為例,介紹如何整合Sentinel實(shí)現(xiàn)對(duì)服務(wù)提供者的接口進(jìn)行容錯(cuò)。

很簡(jiǎn)單,只需要用到一個(gè)注解,在配置RestTemplate的方法上添加@SentinelRestTemplate注解即可,代碼如下:

package com.zj.node.contentcenter.configuration;

import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class BeanConfig {

    @Bean
    @LoadBalanced
    @SentinelRestTemplate
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

注:@SentinelRestTemplate注解包含blockHandler、blockHandlerClass、fallback、fallbackClass屬性,這些屬性的使用方式與@SentinelResource注解一致,所以我們可以利用這些屬性,在觸發(fā)限流、降級(jí)時(shí)定制自己的異常處理邏輯

然后我們?cè)賮韺懚螠y(cè)試代碼,用于調(diào)用服務(wù)提供者的接口,代碼如下:

package com.zj.node.contentcenter.controller.content;

import com.zj.node.contentcenter.domain.dto.user.UserDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@Slf4j
@RestController
@RequiredArgsConstructor
public class TestController {

    private final RestTemplate restTemplate;

    @GetMapping("/test-rest-template-sentinel/{userId}")
    public UserDTO test(@PathVariable("userId") Integer userId) {
        // 調(diào)用user-center服務(wù)的接口(此時(shí)user-center即為服務(wù)提供者)
        return restTemplate.getForObject(
                "http://user-center/users/{userId}", UserDTO.class, userId);
    }
}

編寫完以上代碼重啟項(xiàng)目并可以正常訪問該測(cè)試接口后,此時(shí)在Sentinel控制臺(tái)的簇點(diǎn)鏈路中,就可以看到服務(wù)提供者(user-center)的接口已經(jīng)注冊(cè)到這里來了,現(xiàn)在只需要對(duì)其添加相關(guān)規(guī)則就可以實(shí)現(xiàn)容錯(cuò):
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]

若我們?cè)陂_發(fā)期間,不希望Sentinel對(duì)服務(wù)提供者的接口進(jìn)行容錯(cuò),可以通過以下配置進(jìn)行開關(guān):

# 用于開啟或關(guān)閉@SentinelRestTemplate注解
resttemplate:
  sentinel:
    enabled: true

Sentinel實(shí)現(xiàn)與RestTemplate整合的相關(guān)源碼:

  • org.springframework.cloud.alibaba.sentinel.custom.SentinelBeanPostProcessor

Feign整合Sentinel

上一小節(jié)介紹RestTemplate整合Sentinel時(shí)已經(jīng)做了相關(guān)鋪墊,這里就不廢話了直接上例子。首先在配置文件中添加如下配置:

feign:
  sentinel:
    # 開啟Sentinel對(duì)Feign的支持
    enabled: true

定義一個(gè)FeignClient接口:

package com.zj.node.contentcenter.feignclient;

import com.zj.node.contentcenter.domain.dto.user.UserDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient(name = "user-center")
public interface UserCenterFeignClient {

    @GetMapping("/users/{id}")
    UserDTO findById(@PathVariable Integer id);
}

同樣的來寫段測(cè)試代碼,用于調(diào)用服務(wù)提供者的接口,代碼如下:

package com.zj.node.contentcenter.controller.content;

import com.zj.node.contentcenter.domain.dto.user.UserDTO;
import com.zj.node.contentcenter.feignclient.UserCenterFeignClient;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class TestFeignController {

    private final UserCenterFeignClient feignClient;

    @GetMapping("/test-feign/{id}")
    public UserDTO test(@PathVariable Integer id) {
        // 調(diào)用user-center服務(wù)的接口(此時(shí)user-center即為服務(wù)提供者)
        return feignClient.findById(id);
    }
}

編寫完以上代碼重啟項(xiàng)目并可以正常訪問該測(cè)試接口后,此時(shí)在Sentinel控制臺(tái)的簇點(diǎn)鏈路中,就可以看到服務(wù)提供者(user-center)的接口已經(jīng)注冊(cè)到這里來了,行為與RestTemplate整合Sentinel是一樣的:
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]


默認(rèn)當(dāng)限流、降級(jí)發(fā)生時(shí),Sentinel的處理是直接拋出異常。如果需要自定義限流、降級(jí)發(fā)生時(shí)的異常處理邏輯,而不是直接拋出異常該如何做?@FeignClient注解中有一個(gè)fallback屬性,用于指定當(dāng)遠(yuǎn)程調(diào)用失敗時(shí)使用哪個(gè)類去處理。所以在這個(gè)例子中,我們首先需要定義一個(gè)類,并實(shí)現(xiàn)UserCenterFeignClient接口,代碼如下:

package com.zj.node.contentcenter.feignclient.fallback;

import com.zj.node.contentcenter.domain.dto.user.UserDTO;
import com.zj.node.contentcenter.feignclient.UserCenterFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class UserCenterFeignClientFallback implements UserCenterFeignClient {

    @Override
    public UserDTO findById(Integer id) {
        // 自定義限流、降級(jí)發(fā)生時(shí)的處理邏輯
        log.warn("遠(yuǎn)程調(diào)用被限流/降級(jí)了");
        return UserDTO.builder().
                wxNickname("Default").
                build();
    }
}

然后在UserCenterFeignClient接口的@FeignClient注解上指定fallback屬性,如下:

@FeignClient(name = "user-center", fallback = UserCenterFeignClientFallback.class)
public interface UserCenterFeignClient {
    ...

接下來做一個(gè)簡(jiǎn)單的測(cè)試,看看當(dāng)遠(yuǎn)程調(diào)用失敗時(shí)是否調(diào)用了fallback屬性所指定實(shí)現(xiàn)類里的方法。為服務(wù)提供者的接口添加一條流控規(guī)則,如下圖:
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]

使用postman頻繁發(fā)生請(qǐng)求,當(dāng)QPS超過1時(shí),返回結(jié)果如下:
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]

可以看到,返回了代碼中定義的默認(rèn)值。由此可證當(dāng)限流、降級(jí)或其他原因?qū)е逻h(yuǎn)程調(diào)用失敗時(shí),就會(huì)調(diào)用UserCenterFeignClientFallback類里所實(shí)現(xiàn)的方法。


但是又有另外一個(gè)問題,這種方式無法獲取到異常對(duì)象,并且控制臺(tái)不會(huì)輸出任何相關(guān)的異常信息,若業(yè)務(wù)需要打印異常日志或針對(duì)異常進(jìn)行相關(guān)處理的話該怎么辦呢?此時(shí)就得用到@FeignClient注解中的另一個(gè)屬性:fallbackFactory,同樣需要定義一個(gè)類,只不過實(shí)現(xiàn)的接口不一樣。代碼如下:

package com.zj.node.contentcenter.feignclient.fallbackfactory;

import com.zj.node.contentcenter.domain.dto.user.UserDTO;
import com.zj.node.contentcenter.feignclient.UserCenterFeignClient;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class UserCenterFeignClientFallbackFactory implements FallbackFactory<UserCenterFeignClient> {

    @Override
    public UserCenterFeignClient create(Throwable cause) {

        return new UserCenterFeignClient() {
            @Override
            public UserDTO findById(Integer id) {
                // 自定義限流、降級(jí)發(fā)生時(shí)的處理邏輯
                log.warn("遠(yuǎn)程調(diào)用被限流/降級(jí)了", cause);
                return UserDTO.builder().
                        wxNickname("Default").
                        build();
            }
        };
    }
}

在UserCenterFeignClient接口的@FeignClient注解上指定fallbackFactory屬性,如下:

@FeignClient(name = "user-center", fallbackFactory = UserCenterFeignClientFallbackFactory.class)
public interface UserCenterFeignClient {
    ...

需要注意的是,fallback與fallbackFactory只能二選一,不能同時(shí)使用。

重復(fù)之前的測(cè)試,此時(shí)控制臺(tái)就可以輸出相關(guān)異常信息了:
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]

Sentinel實(shí)現(xiàn)與Feign整合的相關(guān)源碼:

  • org.springframework.cloud.alibaba.sentinel.feign.SentinelFeign

Sentinel使用姿勢(shì)總結(jié)

Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]


擴(kuò)展 - 錯(cuò)誤信息優(yōu)化

Sentinel默認(rèn)在當(dāng)前服務(wù)觸發(fā)限流或降級(jí)時(shí)僅返回簡(jiǎn)單的異常信息,如下:
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]

并且限流和降級(jí)返回的異常信息是一樣的,導(dǎo)致無法根據(jù)異常信息區(qū)分是觸發(fā)了限流還是降級(jí)。

所以我們需要對(duì)錯(cuò)誤信息進(jìn)行相應(yīng)優(yōu)化,以便可以細(xì)致區(qū)分觸發(fā)的是什么規(guī)則。Sentinel提供了一個(gè)UrlBlockHandler接口,實(shí)現(xiàn)該接口即可自定義異常處理邏輯。具體如下示例:

package com.zj.node.contentcenter.sentinel;

import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 自定義流控異常處理
 *
 * @author 01
 * @date 2019-08-02
 **/
@Slf4j
@Component
public class MyUrlBlockHandler implements UrlBlockHandler {

    @Override
    public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
        MyResponse errorResponse = null;
        // 不同的異常返回不同的提示語
        if (e instanceof FlowException) {
            errorResponse = MyResponse.builder()
                    .status(100).msg("接口限流了")
                    .build();
        } else if (e instanceof DegradeException) {
            errorResponse = MyResponse.builder()
                    .status(101).msg("服務(wù)降級(jí)了")
                    .build();
        } else if (e instanceof ParamFlowException) {
            errorResponse = MyResponse.builder()
                    .status(102).msg("熱點(diǎn)參數(shù)限流了")
                    .build();
        } else if (e instanceof SystemBlockException) {
            errorResponse = MyResponse.builder()
                    .status(103).msg("觸發(fā)系統(tǒng)保護(hù)規(guī)則")
                    .build();
        } else if (e instanceof AuthorityException) {
            errorResponse = MyResponse.builder()
                    .status(104).msg("授權(quán)規(guī)則不通過")
                    .build();
        }

        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        new ObjectMapper().writeValue(response.getWriter(), errorResponse);
    }
}

/**
 * 簡(jiǎn)單的響應(yīng)結(jié)構(gòu)體
 */
@Data
@Builder
class MyResponse {
    private Integer status;
    private String msg;
}

此時(shí)再觸發(fā)流控規(guī)則就可以響應(yīng)代碼中自定義的提示信息了:
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]


擴(kuò)展 - 實(shí)現(xiàn)區(qū)分來源

當(dāng)配置流控規(guī)則或授權(quán)規(guī)則時(shí),若需要針對(duì)調(diào)用來源進(jìn)行限流,得先實(shí)現(xiàn)來源的區(qū)分,Sentinel提供了RequestOriginParser接口來處理來源。只要Sentinel保護(hù)的接口資源被訪問,Sentinel就會(huì)調(diào)用RequestOriginParser的實(shí)現(xiàn)類去解析訪問來源。

寫代碼:首先,服務(wù)消費(fèi)者需要具備有一個(gè)來源標(biāo)識(shí),這里假定為服務(wù)消費(fèi)者在調(diào)用接口的時(shí)候都會(huì)傳遞一個(gè)origin的header參數(shù)標(biāo)識(shí)來源。具體如下示例:

package com.zj.node.contentcenter.sentinel;

import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import com.alibaba.nacos.client.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * 實(shí)現(xiàn)區(qū)分來源
 *
 * @author 01
 * @date 2019-08-02
 **/
@Slf4j
@Component
public class MyRequestOriginParser implements RequestOriginParser {

    @Override
    public String parseOrigin(HttpServletRequest request) {
        // 從header中獲取名為 origin 的參數(shù)并返回
        String origin = request.getHeader("origin");
        if (StringUtils.isBlank(origin)) {
            // 如果獲取不到,則拋異常
            String err = "origin param must not be blank!";
            log.error("parse origin failed: {}", err);
            throw new IllegalArgumentException(err);
        }

        return origin;
    }
}

編寫完以上代碼并重啟項(xiàng)目后,此時(shí)header中不包含origin參數(shù)就會(huì)報(bào)錯(cuò)了:
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]


擴(kuò)展 - RESTful URL支持

了解過RESTful URL的都知道這類URL路徑可以動(dòng)態(tài)變化,而Sentinel默認(rèn)是無法識(shí)別這種變化的,所以每個(gè)路徑都會(huì)被當(dāng)成一個(gè)資源,如下圖:
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]

這顯然是有問題的,好在Sentinel提供了UrlCleaner接口解決這個(gè)問題。實(shí)現(xiàn)該接口可以讓我們對(duì)來源url進(jìn)行編輯并返回,這樣就可以將RESTful URL里動(dòng)態(tài)的路徑轉(zhuǎn)換為占位符之類的字符串。具體實(shí)現(xiàn)代碼如下:

package com.zj.node.contentcenter.sentinel;

import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * RESTful URL支持
 *
 * @author 01
 * @date 2019-08-02
 **/
@Slf4j
@Component
public class MyUrlCleaner implements UrlCleaner {

    @Override
    public String clean(String originUrl) {
        String[] split = originUrl.split("/");

        // 將數(shù)字轉(zhuǎn)換為特定的占位標(biāo)識(shí)符
        return Arrays.stream(split)
                .map(s -> NumberUtils.isNumber(s) ? "{number}" : s)
                .reduce((a, b) -> a + "/" + b)
                .orElse("");
    }
}

此時(shí)該RESTful接口就不會(huì)像之前那樣一個(gè)數(shù)字就注冊(cè)一個(gè)資源了:
Spring Cloud Alibaba之服務(wù)容錯(cuò)組件 - Sentinel [代碼篇]

文章名稱:SpringCloudAlibaba之服務(wù)容錯(cuò)組件-Sentinel[代碼篇]
分享鏈接:http://jinyejixie.com/article18/ijchdp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供軟件開發(fā)、Google、域名注冊(cè)、品牌網(wǎng)站設(shè)計(jì)、網(wǎng)頁設(shè)計(jì)公司網(wǎng)站排名

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計(jì)
和硕县| 荃湾区| 青州市| 咸宁市| 重庆市| 南宫市| 九龙县| 鄂尔多斯市| 黄骅市| 九台市| 若羌县| 古蔺县| 信阳市| 兴国县| 手机| 锦屏县| 阜南县| 通州区| 栾川县| 鹤岗市| 大姚县| 嘉定区| 会理县| 遵化市| 抚远县| 承德县| 汝阳县| 聂荣县| 皋兰县| 康乐县| 天津市| 西和县| 安国市| 偏关县| 台州市| 彭泽县| 南岸区| 浙江省| 杭锦旗| 札达县| 红安县|