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

Sentinel與控制臺通信原理

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

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

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

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

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

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

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

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

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

相關(guān)源碼:

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

Sentinel API的使用

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

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

示例代碼如下:

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

        ContextUtil.exit();
    }
}

對幾個可能有疑惑的點說明一下:

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

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

  • 如何使用#其它-api

@SentinelResource注解

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

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

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

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

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

fallback函數(shù)要求:

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

defaultFallback函數(shù)要求:

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

現(xiàn)在我們已經(jīng)對@SentinelResource注解有了一個比較全面的了解,接下來使用@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í)行被保護的業(yè)務(wù)邏輯耗時
    Thread.sleep(100);

    return a;
}

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

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

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

Tips:

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

blockHandlerfallback 都進行了配置,則被限流降級而拋出 BlockException 時只會進入
blockHandler 處理邏輯。若未配置 blockHandler、fallbackdefaultFallback,則被限流降級時會將 BlockException 直接拋出

從 1.3.1 版本開始,注解方式定義資源支持自動統(tǒng)計業(yè)務(wù)異常,無需手動調(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)該就會知道Hystrix除了可以對當前服務(wù)的接口進行容錯,還可以對服務(wù)提供者(被調(diào)用方)的接口進行容錯。到目前為止,我們只介紹了在Sentinel控制臺對當前服務(wù)的接口添加相關(guān)規(guī)則進行容錯,但還沒有介紹如何對服務(wù)提供者的接口進行容錯。

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

很簡單,只需要用到一個注解,在配置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ā)限流、降級時定制自己的異常處理邏輯

然后我們再來寫段測試代碼,用于調(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ù)的接口(此時user-center即為服務(wù)提供者)
        return restTemplate.getForObject(
                "http://user-center/users/{userId}", UserDTO.class, userId);
    }
}

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

若我們在開發(fā)期間,不希望Sentinel對服務(wù)提供者的接口進行容錯,可以通過以下配置進行開關(guān):

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

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

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

Feign整合Sentinel

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

feign:
  sentinel:
    # 開啟Sentinel對Feign的支持
    enabled: true

定義一個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);
}

同樣的來寫段測試代碼,用于調(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ù)的接口(此時user-center即為服務(wù)提供者)
        return feignClient.findById(id);
    }
}

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


默認當限流、降級發(fā)生時,Sentinel的處理是直接拋出異常。如果需要自定義限流、降級發(fā)生時的異常處理邏輯,而不是直接拋出異常該如何做?@FeignClient注解中有一個fallback屬性,用于指定當遠程調(diào)用失敗時使用哪個類去處理。所以在這個例子中,我們首先需要定義一個類,并實現(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) {
        // 自定義限流、降級發(fā)生時的處理邏輯
        log.warn("遠程調(diào)用被限流/降級了");
        return UserDTO.builder().
                wxNickname("Default").
                build();
    }
}

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

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

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

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

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


但是又有另外一個問題,這種方式無法獲取到異常對象,并且控制臺不會輸出任何相關(guān)的異常信息,若業(yè)務(wù)需要打印異常日志或針對異常進行相關(guān)處理的話該怎么辦呢?此時就得用到@FeignClient注解中的另一個屬性:fallbackFactory,同樣需要定義一個類,只不過實現(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) {
                // 自定義限流、降級發(fā)生時的處理邏輯
                log.warn("遠程調(diào)用被限流/降級了", cause);
                return UserDTO.builder().
                        wxNickname("Default").
                        build();
            }
        };
    }
}

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

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

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

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

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

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

Sentinel使用姿勢總結(jié)

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


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

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

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

所以我們需要對錯誤信息進行相應(yīng)優(yōu)化,以便可以細致區(qū)分觸發(fā)的是什么規(guī)則。Sentinel提供了一個UrlBlockHandler接口,實現(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ù)降級了")
                    .build();
        } else if (e instanceof ParamFlowException) {
            errorResponse = MyResponse.builder()
                    .status(102).msg("熱點參數(shù)限流了")
                    .build();
        } else if (e instanceof SystemBlockException) {
            errorResponse = MyResponse.builder()
                    .status(103).msg("觸發(fā)系統(tǒng)保護規(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);
    }
}

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

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


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

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

寫代碼:首先,服務(wù)消費者需要具備有一個來源標識,這里假定為服務(wù)消費者在調(diào)用接口的時候都會傳遞一個origin的header參數(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;

/**
 * 實現(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;
    }
}

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


擴展 - RESTful URL支持

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

這顯然是有問題的,好在Sentinel提供了UrlCleaner接口解決這個問題。實現(xiàn)該接口可以讓我們對來源url進行編輯并返回,這樣就可以將RESTful URL里動態(tài)的路徑轉(zhuǎn)換為占位符之類的字符串。具體實現(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)換為特定的占位標識符
        return Arrays.stream(split)
                .map(s -> NumberUtils.isNumber(s) ? "{number}" : s)
                .reduce((a, b) -> a + "/" + b)
                .orElse("");
    }
}

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

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

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

廣告

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

成都定制網(wǎng)站網(wǎng)頁設(shè)計