跳到主要内容

22、SpringCloud Alibaba - Sentinel 与 OpenFeign服务熔断总结

一、fallback降级

1、 在@FeignClient加上注解;

@FeignClient(value = "server",fallback = ServerApiFallBack.class)
@Component
public interface ServerApi {
   
     

    @GetMapping("/server/test/{id}")
    Map test(@PathVariable String id);
}

2、 ServerApiFallBack熔断类;

@Component
public class ServerApiFallBack implements ServerApi{
   
     
    @Override
    public Map test(String id) {
   
     
        ImmutableMap map = ImmutableMap.of("code", 500, "reason", "服务不可用");
        return map;
    }
}

3、 Controller接口;

@RestController
@RequestMapping("/client")
public class ClientController {
   
     

  @GetMapping("/test/{id}")
    public Map test(@PathVariable String id){
   
     
        return serverApi.test(id);
    }
    
}

4、 此时调用接口,服务出现异常不步可用时,会触发fallback服务熔断,具体逻辑在SentinelInvocationHandler中实现;

				catch (Throwable ex) {
   
     
					...
					if (fallbackFactory != null) {
   
     
						try {
   
     
							//回调服务熔断方法
							Object fallbackResult = fallbackMethodMap.get(method)
									.invoke(fallbackFactory.create(ex), args);
							return fallbackResult;
						}
					...

二、SentinelResource流控降级

1、 在@FeignClient加上注解;

@FeignClient(value = "server",fallback = ServerApiFallBack.class)
@Component
public interface ServerApi {
   
     

    @GetMapping("/server/test/{id}")
    Map test(@PathVariable String id);
}

2、 Controller接口;

 @SentinelResource(value = "sentinel-test", blockHandler = "blockHandler")
    @GetMapping("/test/{id}")
    public Map test(@PathVariable String id){
   
     
        return serverApi.test(id);
    }

    public Map blockHandler(String id ,BlockException e) {
   
     
        ImmutableMap map = ImmutableMap.of(
                "code", 500,
                "reason", "sentinel服务流量控制",
                "error", e);
        return map;
    }

3、 配置熔断规则;

在sentine控制台设置熔断规则

 
4、 访问接口;

多次访问接口,会发生熔断,结果:

{
   
     
    "code": 500,
    "reason": "sentinel服务流量控制",
    "error": {
   
     
  	...
}

此时熔断逻辑是在SentinelResourceAspect中

		try {
   
     
            entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());
            Object result = pjp.proceed();
            return result;
        } catch (BlockException ex) {
   
     
        	//熔断
            return handleBlockException(pjp, annotation, ex);
        } 

根据@SentinelResource中配置的blockHandler、blockHandlerClass、fallback、defaultFallback、fallbackClass调用熔断的方法。

三、Sentinel全局熔断

1、 给接口设置熔断规则,例如给controller接口设置熔断规则,资源名是/client/test/{id},当触发熔断规则时,具体逻辑是在SentinelWebInterceptor中处理,它实现了springmvc的HandlerInterceptor拦截器接口在preHandle方法中触发接口的流控;

	catch (BlockException e) {
   
     
            try {
   
     
            	//处理熔断
                handleBlockException(request, response, e);
            } 

 protected void handleBlockException(HttpServletRequest request, HttpServletResponse response, BlockException e)
        throws Exception {
   
     
        //配置中存在BlockExceptionHandler时,调用其handle()方法
        if (baseWebMvcConfig.getBlockExceptionHandler() != null) {
   
     
            baseWebMvcConfig.getBlockExceptionHandler().handle(request, response, e);
        } else {
   
     
            // Throw BlockException directly. Users need to handle it in Spring global exception handler.
            throw e;
        }
    }

2、 baseWebMvcConfig中配置BlockExceptionHandler;

在SentinelWebAutoConfiguration配置类中,创建了SentinelWebMvcConfig

	@Autowired
	private Optional<BlockExceptionHandler> blockExceptionHandlerOptional;

	@Bean
	@ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled",
			matchIfMissing = true)
	public SentinelWebMvcConfig sentinelWebMvcConfig() {
   
     
		SentinelWebMvcConfig sentinelWebMvcConfig = new SentinelWebMvcConfig();
		sentinelWebMvcConfig.setHttpMethodSpecify(properties.getHttpMethodSpecify());
		sentinelWebMvcConfig.setWebContextUnify(properties.getWebContextUnify());
		//自定义 BlockExceptionHandler
		if (blockExceptionHandlerOptional.isPresent()) {
   
     
			blockExceptionHandlerOptional
					.ifPresent(sentinelWebMvcConfig::setBlockExceptionHandler);
		}
		else {
   
     
			//自定义熔断时跳转页面 blockPage
			if (StringUtils.hasText(properties.getBlockPage())) {
   
     
				sentinelWebMvcConfig.setBlockExceptionHandler(((request, response,
						e) -> response.sendRedirect(properties.getBlockPage())));
			}
			else {
   
     
				//默认的 DefaultBlockExceptionHandler
				sentinelWebMvcConfig
						.setBlockExceptionHandler(new DefaultBlockExceptionHandler());
			}
		}

		urlCleanerOptional.ifPresent(sentinelWebMvcConfig::setUrlCleaner);
		requestOriginParserOptional.ifPresent(sentinelWebMvcConfig::setOriginParser);
		return sentinelWebMvcConfig;
	}

3、 自定义BlockExceptionHandler;

@Component
public class GlobalBlockExceptionHandler implements BlockExceptionHandler {
   
     
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
   
     

        response.setStatus(500);

        ServletOutputStream out = response.getOutputStream();
        out.print(new String("触发 Sentinel 全局熔断处理".getBytes(StandardCharsets.UTF_8),"iso-8859-1"));
        out.flush();
        out.close();
    }
}