Sentinel系列之工作原理(源码分析篇)

在SpringCloud中使用sentinel实现限流,我们并不需要在项目中过多的任何配置,Sentinel会自动保护所有的HTTP服务

我们看一下Spring-Cloud-Starter-Alibaba-Sentinel这个包下面,这个包带这Starter说明实现了自动装配,所以我们直接找这个包下面的spring.factories文件,看到的都是key=value的配置信息,这里就不贴出来了,主要讲解一下最主要的几个配置类

1、SentinelWebAutoConfiguration是对Web Servlet环境的支持

2、SentinelWebFluxAutoConfiguration是对Spring WebFlux的支持

3、SentinelEndpointAutoConfiguration暴露Endpoint信息

4、SentinelFeignAutoConfiguration用于适配Feign组件

5、SentinelAutoConfiguration支持对RestTemplate的服务调用使用Sentinel进行保护

这里我们重点讲一下:SentinelWebAutoConfiguration,这个Web Servlet的实现,上源码:

package com.alibaba.cloud.sentinel;

import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner;
import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import javax.annotation.PostConstruct;
import javax.servlet.Filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@ConditionalOnClass({CommonFilter.class})
@ConditionalOnProperty(
    name = {"spring.cloud.sentinel.enabled"},
    matchIfMissing = true
)
@EnableConfigurationProperties({SentinelProperties.class})
public class SentinelWebAutoConfiguration {
    private static final Logger log = LoggerFactory.getLogger(SentinelWebAutoConfiguration.class);
    @Autowired
    private SentinelProperties properties;
    @Autowired
    private Optional<UrlCleaner> urlCleanerOptional;
    @Autowired
    private Optional<UrlBlockHandler> urlBlockHandlerOptional;
    @Autowired
    private Optional<RequestOriginParser> requestOriginParserOptional;

    public SentinelWebAutoConfiguration() {
    }

    @PostConstruct
    public void init() {
        this.urlBlockHandlerOptional.ifPresent(WebCallbackManager::setUrlBlockHandler);
        this.urlCleanerOptional.ifPresent(WebCallbackManager::setUrlCleaner);
        this.requestOriginParserOptional.ifPresent(WebCallbackManager::setRequestOriginParser);
    }

    @Bean
    @ConditionalOnProperty(
        name = {"spring.cloud.sentinel.filter.enabled"},
        matchIfMissing = true
    )
    public FilterRegistrationBean sentinelFilter() {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean();
        com.alibaba.cloud.sentinel.SentinelProperties.Filter filterConfig = this.properties.getFilter();
        if (filterConfig.getUrlPatterns() == null || filterConfig.getUrlPatterns().isEmpty()) {
            List<String> defaultPatterns = new ArrayList();
            defaultPatterns.add("/*");
            filterConfig.setUrlPatterns(defaultPatterns);
        }

        registration.addUrlPatterns((String[])filterConfig.getUrlPatterns().toArray(new String[0]));
        Filter filter = new CommonFilter();
        registration.setFilter(filter);
        registration.setOrder(filterConfig.getOrder());
        registration.addInitParameter("HTTP_METHOD_SPECIFY", String.valueOf(this.properties.getHttpMethodSpecify()));
        log.info("[Sentinel Starter] register Sentinel CommonFilter with urlPatterns: {}.", filterConfig.getUrlPatterns());
        return registration;
    }
}

在这个类中,发现了一个自动装配了一个FilterRegistrationBean,主要作用就是注册一个CommonFilter,并且默认情况是/*,表示拦截所有请求,那我们查看一下CommonFilter的源码逻辑,其实很简单:

public class CommonFilter implements Filter {
    public static final String HTTP_METHOD_SPECIFY = "HTTP_METHOD_SPECIFY";
    public static final String WEB_CONTEXT_UNIFY = "WEB_CONTEXT_UNIFY";
    private static final String COLON = ":";
    private boolean httpMethodSpecify = false;
    private boolean webContextUnify = true;
    private static final String EMPTY_ORIGIN = "";

    public CommonFilter() {
    }

    public void init(FilterConfig filterConfig) {
        this.httpMethodSpecify = Boolean.parseBoolean(filterConfig.getInitParameter("HTTP_METHOD_SPECIFY"));
        if (filterConfig.getInitParameter("WEB_CONTEXT_UNIFY") != null) {
            this.webContextUnify = Boolean.parseBoolean(filterConfig.getInitParameter("WEB_CONTEXT_UNIFY"));
        }

    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest sRequest = (HttpServletRequest)request;
        Entry urlEntry = null;

        try {
            //解析请求的URL
            String target = FilterUtil.filterTarget(sRequest);
            //URL清洗
            UrlCleaner urlCleaner = WebCallbackManager.getUrlCleaner();
            if (urlCleaner != null) {
                target = urlCleaner.clean(target);
            }

            if (!StringUtil.isEmpty(target)) {
                String origin = this.parseOrigin(sRequest);
                String contextName = this.webContextUnify ? "sentinel_web_servlet_context" : target;
                ContextUtil.enter(contextName, origin);
                if (this.httpMethodSpecify) {
                    String pathWithHttpMethod = sRequest.getMethod().toUpperCase() + ":" + target;
                    //这个我们在热点数据限流有讲过
                    urlEntry = SphU.entry(pathWithHttpMethod, 1, EntryType.IN);
                } else {
                    urlEntry = SphU.entry(target, 1, EntryType.IN);
                }
            }

            chain.doFilter(request, response);
        } catch (BlockException var15) {
            HttpServletResponse sResponse = (HttpServletResponse)response;
            WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, var15);
        } catch (ServletException | RuntimeException | IOException var16) {
            Tracer.traceEntry(var16, urlEntry);
            throw var16;
        } finally {
            if (urlEntry != null) {
                urlEntry.exit();
            }

            ContextUtil.exit();
        }

    }

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

        return origin;
    }

    public void destroy() {
    }
}

逻辑是不是很简单,其实就做了三件事:

1、获取请求的URL

2、获取Urlcleaner,如果非空存在,就说明URL有配置过清洗策略,调用clean方法替换target

3、使用SphU.entry对当前URL添加限流埋点

因此,对于Web Servlet环境,只是通过Filter的方式将所有请求自动设置为Sentinel的资源,从而达到限流的目的 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值