Skip to content

主流框架的适配

Eric Zhao edited this page Mar 4, 2019 · 65 revisions

目录

注:适配模块仅提供相应适配功能,若希望接入 Sentinel 控制台,请务必参考 Sentinel 控制台文档

Web Servlet

Sentinel 提供与 Servlet 的整合,可以对 Web 请求进行流量控制。使用时需引入以下模块(以 Maven 为例):

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-web-servlet</artifactId>
    <version>x.y.z</version>
</dependency>

您只需要在 Web 容器中的 web.xml 配置文件中进行如下配置即可开启 Sentinel 支持:

<filter>
	<filter-name>SentinelCommonFilter</filter-name>
	<filter-class>com.alibaba.csp.sentinel.adapter.servlet.CommonFilter</filter-class>
</filter>

<filter-mapping>
	<filter-name>SentinelCommonFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

若是 Spring 应用可以通过 Spring 进行配置,例如:

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean sentinelFilterRegistration() {
        FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new CommonFilter());
        registration.addUrlPatterns("/*");
        registration.setName("sentinelFilter");
        registration.setOrder(1);

        return registration;
    }
}

默认情况下,当请求被限流时会返回默认的提示页面。您也可以通过 WebServletConfig.setBlockPage(blockPage) 方法设定自定义的跳转 URL,当请求被限流时会自动跳转至设定好的 URL。同样也可以实现 UrlBlockHandler 接口并编写定制化的限流处理逻辑,然后将其注册至 WebCallbackManager 中。

注意:Sentinel Web Filter 会将每个到来的不同的 URL 都作为不同的资源处理,因此对于 REST 风格的 API,需要自行实现 UrlCleaner 接口清洗一下资源(比如将满足 /foo/:id 的 URL 都归到 /foo/* 资源下),然后将其注册至 WebCallbackManager 中。否则会导致资源数量过多,超出资源数量阈值(目前是 6000)时多出的资源的规则将 不会生效

若希望对 HTTP 请求按照来源限流,则可以自己实现 RequestOriginParser 接口从 HTTP 请求中解析 origin 并注册至 WebCallbackManager 中。

如果您正在使用 Spring Boot / Spring Cloud,那么可以通过引入 Spring Cloud Sentinel Starter 来更方便地整合 Sentinel,详情请见 Spring Cloud Alibaba

Dubbo

Sentinel 提供 Dubbo 的相关适配 Sentinel Dubbo Adapter,主要包括针对 Service Provider 和 Service Consumer 实现的 Filter。使用时需引入以下模块(以 Maven 为例):

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-dubbo-adapter</artifactId>
    <version>x.y.z</version>
</dependency>

引入此依赖后,Dubbo 的服务接口和方法(包括调用端和服务端)就会成为 Sentinel 中的资源,在配置了规则后就可以自动享受到 Sentinel 的防护能力。

注:若希望接入 Dashboard,请参考 接入控制台的步骤。只引入 Sentinel Dubbo Adapter 无法接入控制台!

若不希望开启 Sentinel Dubbo Adapter 中的某个 Filter,可以手动关闭对应的 Filter,比如:

<!-- 关闭 Sentinel 对应的 Service Consumer Filter -->
<dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/>

限流粒度可以是服务接口和服务方法两种粒度:

  • 服务接口:resourceName 为 接口全限定名,如 com.alibaba.csp.sentinel.demo.dubbo.FooService
  • 服务方法:resourceName 为 接口全限定名:方法签名,如 com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)

Sentinel Dubbo Adapter 还支持配置全局的 fallback 函数,可以在 Dubbo 服务被限流/降级/负载保护的时候进行相应的 fallback 处理。用户只需要实现自定义的 DubboFallback 接口,并通过 DubboFallbackRegistry 注册即可。默认情况会直接将 BlockException 包装后抛出。同时,我们还可以配合 Dubbo 的 fallback 机制 来为降级的服务提供替代的实现。

注:一般情况下熔断降级 / fallback 用于调用端(客户端)。

我们提供了 Dubbo 的相关示例,请见 sentinel-demo-dubbo

有关 Sentinel 在 Dubbo 中的最佳实践,请参考 Sentinel: Dubbo 服务的流量哨兵

关于 Dubbo Filter 的更多信息,请参考 Dubbo Filter 文档

Spring Cloud

Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。Sentinel 与 Spring Cloud 的整合见 Sentinel Spring Cloud Starter

Spring Cloud Alibaba 默认为 Sentinel 整合了 Servlet 、RestTemplate 和 FeignClient。Sentinel 在 Spring Cloud 生态中,不仅补全了 Hystrix 在 Servlet 和 RestTemplate 这一块的空白,而且还完全兼容了 Hystrix 在 FeignClient 中限流降级的用法,最后还支持运行时灵活地配置和调整限流降级规则。

gRPC

Sentinel 提供与 gRPC Java 的整合,以 gRPC ServerInterceptorClientInterceptor 的形式保护 gRPC 服务资源。使用时需引入以下模块(以 Maven 为例):

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-grpc-adapter</artifactId>
    <version>x.y.z</version>
</dependency>

在使用 Sentinel gRPC Adapter 时,只需要将对应的 Interceptor 注册至对应的客户端或服务端中。其中客户端的示例如下:

public class ServiceClient {

    private final ManagedChannel channel;

    ServiceClient(String host, int port) {
        this.channel = ManagedChannelBuilder.forAddress(host, port)
            .intercept(new SentinelGrpcClientInterceptor()) // 在此处注册拦截器
            .build();
        // 在此处初始化客户端 stub 类
    }
}

服务端的示例如下:

import io.grpc.Server;

Server server = ServerBuilder.forPort(port)
     .addService(new MyServiceImpl()) // 添加自己的服务实现
     .intercept(new SentinelGrpcServerInterceptor()) // 在此处注册拦截器
     .build();

注意:由于 gRPC 拦截器中 ClientCall/ServerCall 以回调的形式进行请求响应信息的获取,每次 gRPC 服务调用计算出的 RT 可能会不准确。Sentinel gRPC Adapter 目前只支持 unary call。

Apache RocketMQ

在 Apache RocketMQ 中,当消费者去消费消息的时候,无论是通过 pull 的方式还是 push 的方式,都可能会出现大批量的消息突刺。如果此时要处理所有消息,很可能会导致系统负载过高,影响稳定性。但其实可能后面几秒之内都没有消息投递,若直接把多余的消息丢掉则没有充分利用系统处理消息的能力。我们希望可以把消息突刺均摊到一段时间内,让系统负载保持在消息处理水位之下的同时尽可能地处理更多消息,从而起到“削峰填谷”的效果:

削峰填谷

上图中红色的部分代表超出消息处理能力的部分。我们可以看到消息突刺往往都是瞬时的、不规律的,其后一段时间系统往往都会有空闲资源。我们希望把红色的那部分消息平摊到后面空闲时去处理,这样既可以保证系统负载处在一个稳定的水位,又可以尽可能地处理更多消息。Sentinel 专门为这种场景提供了匀速器的特性,可以把突然到来的大量请求以匀速的形式均摊,以固定的间隔时间让请求通过,以稳定的速度逐步处理这些请求,起到“削峰填谷”的效果,从而避免流量突刺造成系统负载过高。同时堆积的请求将会排队,逐步进行处理;当请求排队预计超过最大超时时长的时候则直接拒绝,而不是拒绝全部请求。

比如在 RocketMQ 的场景下配置了匀速模式下请求 QPS 为 5,则会每 200 ms 处理一条消息,多余的处理任务将排队;同时设置了超时时间为 5 s,预计排队时长超过 5 s 的处理任务将会直接被拒绝。示意图如下图所示:

Uniform rate

RocketMQ 用户可以根据不同的 group 和不同的 topic 分别设置限流规则,限流控制模式设置为匀速器模式(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER),比如:

private void initFlowControlRule() {
    FlowRule rule = new FlowRule();
    rule.setResource(KEY); // 对应的 key 为 `groupName:topicName`
    rule.setCount(5);
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    rule.setLimitApp("default");

    // 匀速器模式下,设置了 QPS 为 5,则请求每 200 ms 允许通过 1 个
    rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
    // 如果更多的请求到达,这些请求会被置于虚拟的等待队列中。等待队列有一个 max timeout,如果请求预计的等待时间超过这个时间会直接被 block
    // 在这里,timeout 为 5s
    rule.setMaxQueueingTimeMs(5 * 1000);
    FlowRuleManager.loadRules(Collections.singletonList(rule));
}

结合 RocketMQ Client 使用 Sentinel 时,用户需要在处理消息时手动埋点。详情请见 Sentinel RocketMQ Demo。相关 Blog 见 Sentinel 为 RocketMQ 保驾护航

Zuul 1.x

Sentinel 提供与 Netflix Zuul 1.x 的整合,可以对网关的转发请求进行流量控制,默认提供 Service 和 URL 两个维度上的限流。

使用时需引入以下模块(以 Maven 为例):

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-zuul-adapter</artifactId>
    <version>x.y.z</version>
</dependency>

下面数据是 Sentinel 生成的链路数据,其中 cokebook 是对应的微服务(服务 ID 存储在 Zuul 的 Context 中,对应的 key 为:ctx.get("serviceId"));--/coke/app 是服务对应的请求路径:

EntranceNode: machine-root
-EntranceNode: coke
--coke
---/coke/app
-EntranceNode: book
--book
---/book/app

可以针对服务 coke 调用进行限流,也可以直接对 URL 限流,或者根据需要同时限流。

Zuul Adapter 通过实现 ZuulFilter 来完成来完成 Sentinel 的整合,所以我们需要注册 ZuulFilter

// 获取 FilterRegistry
final FilterRegistry r = FilterRegistry.instance();
// 开关,顺序等ZuulFilter的配置信息。
SentinelZuulProperties properties = new SentinelZuulProperties();
// 开启Sentinel。
properties.setEnabled(true);
// 配置URL解析器(限流对应的资源),可以自定义,这里使用默认的解析器。
UrlCleaner defaultUrlCleaner = new DefaultUrlCleaner();
// 配置默认的orgin解析器(ContextUtil.enter(serviceTarget, origin) )
RequestOriginParser defaultRequestOriginParser = new DefaultRequestOriginParser();

// 注册ZuulFilter, 三个ZuulFilter 必须全部注册,才能完整的统计链路信息。
SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, defaultUrlCleaner, defaultRequestOriginParser);
r.put("sentinelPreFilter", sentinelPreFilter);
SentinelPostFilter postFilter = new SentinelPostFilter(properties);
r.put("sentinelPostFilter", postFilter);
SentinelErrorFilter errorFilter = new SentinelErrorFilter(properties);
r.put("sentinelErrorFilter", errorFilter);

发生限流之后的处理流程 :

  • 发生限流之后可自定义返回参数,通过实现 SentinelFallbackProvider 接口,默认的实现是 DefaultBlockFallbackProvider
  • 可以针对不同的路径有不同的返回,默认的 fallback route 的规则是 ServiveId + URI PATH,例如 /book/app,其中 book 是serviceId,/app 是 URI PATH 比如:
// 自定义 FallbackProvider 
public class MyBlockFallbackProvider implements ZuulBlockFallbackProvider {

    private Logger logger = LoggerFactory.getLogger(DefaultBlockFallbackProvider.class);
    
    // you can define route as service level 
    @Override
    public String getRoute() {
        return "/book/app";
    }

    @Override
        public BlockResponse fallbackResponse(String route, Throwable cause) {
            RecordLog.info(String.format("[Sentinel DefaultBlockFallbackProvider] Run fallback route: %s", route));
            if (cause instanceof BlockException) {
                return new BlockResponse(429, "Sentinel block exception", route);
            } else {
                return new BlockResponse(500, "System Error", route);
            }
        }
 }
 
 // 注册 FallbackProvider
 ZuulBlockFallbackManager.registerProvider(new MyBlockFallbackProvider());

限流发生之后的默认返回:

{
    "code":429,
    "message":"Sentinel block exception",
    "route":"/"
}

注意:Sentinel Zuul Filter 会将每个到来的不同的 URL 都作为不同的资源处理,因此对于 REST 风格的 API,需要自行实现 UrlCleaner 接口清洗一下资源(比如将满足 /foo/:id 的 URL 都归到 /foo/* 资源下), 然后将其传入至 SentinelPreFilter 的构造参数中。否则会导致资源数量过多,超出资源数量阈值(目前是 6000)时多出的资源的规则将 不会生效

若希望对 HTTP 请求按照来源限流,则可以自己实现 RequestOriginParser 接口从 HTTP 请求中解析 origin 然后将其传入至SentinelPreFilter的构造参数中。

如果您正在使用 Spring Cloud Zuul Starter,那么可以通过引入 Spring Cloud Alibaba Sentinel Zuul Starter 来更方便地整合 Sentinel。Starter 会依赖这个 adapter,进度请查看这里

Clone this wiki locally