008-Sentinel清洗RESTful的@PathVariable

栏目: 后端 · 发布时间: 6年前

内容简介:这是坚持技术写作计划(含翻译)的第8篇,定个小目标999,每周最少2篇。前段时间的文章多是运维方面的,最近放出一波后端相关的。最近开始使用Sentinel进行流量保护,但是默认的web servlet filter是拦截全部http请求。在传统的项目中问题不大。但是如果项目中用了Spring MVC,并且用了@PathVariable就尴尬了。

这是坚持技术写作计划(含翻译)的第8篇,定个小目标999,每周最少2篇。

前段时间的文章多是运维方面的,最近放出一波后端相关的。

背景

最近开始使用Sentinel进行流量保护,但是默认的web servlet filter是拦截全部http请求。在传统的项目中问题不大。但是如果项目中用了Spring MVC,并且用了@PathVariable就尴尬了。

比如 uri pattern是 /foo/{id} ,而从Sentinel监控看 /foo/1/foo/2 就是两个资源了,并且Sentinel最大支持6000个资源,再多就不生效了。

解决办法

官方给的方案是:UrlCleaner

WebCallbackManager.setUrlCleaner(new UrlCleaner() {
           @Override
           public String clean(String originUrl) {
               if (originUrl.startsWith(fooPrefix)) {
                   return "/foo/*";
               }
               return originUrl;
           }
       });

但是想想就吐, /v1/{foo}/{bar}/qux/{baz} 这种的来个20来个,截一个我看看。

AOP

换种思路,uri pattern难搞,用笨办法 aop总行吧?答案是可以的。

@Aspect
public class SentinelResourceAspect {
    @Pointcut("within(com.anjia.*.web.rest..*)")
    public void sentinelResourcePackagePointcut() {
        // Method is empty as this is just a Pointcut, the implementations are
        // in the advices.
    }
    @Around("sentinelResourcePackagePointcut()")
    public Object sentinelResourceAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Entry entry = null;
        // 务必保证finally会被执行
        try {
          // 资源名可使用任意有业务语义的字符串
          // 注意此处只是类名#方法名,方法重载是合并的,如果需要进行区分,
          // 可以获取参数类型加入到资源名称上
          entry = SphU.entry(joinPoint.getSignature().getDeclaringTypeName()+
                             "#"+joinPoint.getSignature().getName());
          // 被保护的业务逻辑
          // do something...
        } catch (BlockException ex) {
          // 资源访问阻止,被限流或被降级
          // 进行相应的处理操作
        } finally {
          if (entry != null) {
            entry.exit();
          }
        }
        return result;
    }
}

拦截器

温习一下 Spring mvc的执行流程 doFilter -> doService -> dispatcher -> preHandle -> controller -> postHandle -> afterCompletion -> filterAfter

核心的是 String pattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); 但是是在dispatcher阶段才赋值的,所以在CommFilter是取不到的,所以导致使用官方的Filter是不行的。只能用拦截器

import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SentinelHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String origin = parseOrigin(request);
        String pattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
        String uriTarget = StringUtils.defaultString(pattern,FilterUtil.filterTarget(request));
        try {
            // Clean and unify the URL.
            // For REST APIs, you have to clean the URL (e.g. `/foo/1` and `/foo/2` -> `/foo/:id`), or
            // the amount of context and resources will exceed the threshold.
            UrlCleaner urlCleaner = WebCallbackManager.getUrlCleaner();
            if (urlCleaner != null) {
                uriTarget = urlCleaner.clean(uriTarget);
            }
            RecordLog.info(String.format("[Sentinel Pre Filter] Origin: %s enter Uri Path: %s", origin, uriTarget));
            SphU.entry(uriTarget, EntryType.IN);
            return true;
        } catch (BlockException ex) {
            RecordLog.warn(String.format("[Sentinel Pre Filter] Block Exception when Origin: %s enter fall back uri: %s", origin, uriTarget), ex);
            return false;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        while (ContextUtil.getContext() != null && ContextUtil.getContext().getCurEntry() != null) {
            ContextUtil.getContext().getCurEntry().exit();
        }
        ContextUtil.exit();
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }

    private String parseOrigin(HttpServletRequest request) {
        RequestOriginParser originParser = WebCallbackManager.getRequestOriginParser();
        String origin = EMPTY_ORIGIN;
        if (originParser != null) {
            origin = originParser.parseOrigin(request);
            if (StringUtil.isEmpty(origin)) {
                return EMPTY_ORIGIN;
            }
        }
        return origin;
    }


    private static final String EMPTY_ORIGIN = "";
}
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SentinelHandlerInterceptor());
    }
}

参考资料

招聘小广告

山东济南的小伙伴欢迎投简历啊 加入我们 , 一起搞事情。

长期招聘,Java程序员,大数据工程师,运维工程师,前端工程师。

谢谢支持

008-Sentinel清洗RESTful的@PathVariable 支付宝

008-Sentinel清洗RESTful的@PathVariable 微信


以上所述就是小编给大家介绍的《008-Sentinel清洗RESTful的@PathVariable》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Data Structures and Algorithms

Data Structures and Algorithms

Alfred V. Aho、Jeffrey D. Ullman、John E. Hopcroft / Addison Wesley / 1983-1-11 / USD 74.20

The authors' treatment of data structures in Data Structures and Algorithms is unified by an informal notion of "abstract data types," allowing readers to compare different implementations of the same......一起来看看 《Data Structures and Algorithms》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器