美文网首页
28. Spring Cloud Alibaba之服务容错组件

28. Spring Cloud Alibaba之服务容错组件

作者: Zal哥哥 | 来源:发表于2020-10-15 10:06 被阅读0次

    扩展 - 错误信息优化

    Sentinel默认在当前服务触发限流或降级时仅返回简单的异常信息,如下:

    image

    并且限流和降级返回的异常信息是一样的,导致无法根据异常信息区分是触发了限流还是降级。

    所以我们需要对错误信息进行相应优化,以便可以细致区分触发的是什么规则。Sentinel提供了一个UrlBlockHandler接口,实现该接口即可自定义异常处理逻辑。具体如下示例:

    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;
    
    /**
     * 自定义流控异常处理
     **/
    @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("服务降级了")
                        .build();
            } else if (e instanceof ParamFlowException) {
                errorResponse = MyResponse.builder()
                        .status(102).msg("热点参数限流了")
                        .build();
            } else if (e instanceof SystemBlockException) {
                errorResponse = MyResponse.builder()
                        .status(103).msg("触发系统保护规则")
                        .build();
            } else if (e instanceof AuthorityException) {
                errorResponse = MyResponse.builder()
                        .status(104).msg("授权规则不通过")
                        .build();
            }
    
            response.setStatus(500);
            response.setCharacterEncoding("utf-8");
            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            new ObjectMapper().writeValue(response.getWriter(), errorResponse);
        }
    }
    
    /**
     * 简单的响应结构体
     */
    @Data
    @Builder
    class MyResponse {
        private Integer status;
        private String msg;
    }
    
    

    此时再触发流控规则就可以响应代码中自定义的提示信息了:

    image

    扩展 - 实现区分来源

    当配置流控规则或授权规则时,若需要针对调用来源进行限流,得先实现来源的区分,Sentinel提供了RequestOriginParser接口来处理来源。只要Sentinel保护的接口资源被访问,Sentinel就会调用RequestOriginParser的实现类去解析访问来源。

    写代码:首先,服务消费者需要具备有一个来源标识,这里假定为服务消费者在调用接口的时候都会传递一个origin的header参数标识来源。具体如下示例:

    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;
    
    /**
     * 实现区分来源
     **/
    @Slf4j
    @Component
    public class MyRequestOriginParser implements RequestOriginParser {
    
        @Override
        public String parseOrigin(HttpServletRequest request) {
            // 从header中获取名为 origin 的参数并返回
            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参数就会报错了:

    image

    扩展 - RESTful URL支持

    了解过RESTful URL的都知道这类URL路径可以动态变化,而Sentinel默认是无法识别这种变化的,所以每个路径都会被当成一个资源,如下图:

    image

    这显然是有问题的,好在Sentinel提供了UrlCleaner接口解决这个问题。实现该接口可以让我们对来源url进行编辑并返回,这样就可以将RESTful URL里动态的路径转换为占位符之类的字符串。具体实现代码如下:

    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支持
     **/
    @Slf4j
    @Component
    public class MyUrlCleaner implements UrlCleaner {
    
        @Override
        public String clean(String originUrl) {
            String[] split = originUrl.split("/");
    
            // 将数字转换为特定的占位标识符
            return Arrays.stream(split)
                    .map(s -> NumberUtils.isNumber(s) ? "{number}" : s)
                    .reduce((a, b) -> a + "/" + b)
                    .orElse("");
        }
    }
    
    

    此时该RESTful接口就不会像之前那样一个数字就注册一个资源了:


    image.png

    链接:https://www.jianshu.com/p/0fe4d78e2986

    相关文章

      网友评论

          本文标题:28. Spring Cloud Alibaba之服务容错组件

          本文链接:https://www.haomeiwen.com/subject/uyxsektx.html