开源框架适配
Sentinel 提供了丰富的框架适配模块,可以轻松地将各种主流框架接入 Sentinel 进行流量控制和熔断降级。本章介绍 Sentinel 与常用框架的整合方式。
适配模块概览
Sentinel 提供的适配模块可以分为以下几类:
| 类别 | 支持的框架 |
|---|---|
| Web 框架 | Servlet、Spring WebFlux、JAX-RS |
| RPC 框架 | Dubbo、gRPC、SOFARPC、Feign |
| HTTP Client | Apache HttpClient、OkHttp |
| API Gateway | Spring Cloud Gateway、Zuul |
| 消息队列 | Apache RocketMQ |
| 响应式 | Reactor |
| 微服务 | Spring Cloud、Quarkus |
Web Servlet 适配
Sentinel 提供针对 Servlet 的原生整合,可以对 Web 请求进行流量控制。
引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
<version>1.8.8</version>
</dependency>
配置 Filter
在 web.xml 中配置:
<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 Boot 中配置:
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<Filter> sentinelFilterRegistration() {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/*");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration;
}
}
自定义限流处理
默认情况下,被限流的请求会返回 Blocked by Sentinel (flow limiting)。可以通过以下方式自定义处理逻辑:
方式一:设置跳转页面
// 通过代码设置
WebServletConfig.setBlockPage("/blocked");
// 或通过 JVM 参数
// -Dcsp.sentinel.web.servlet.block.page=/blocked
方式二:实现自定义处理器
WebCallbackManager.setUrlBlockHandler(new UrlBlockHandler() {
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
response.setStatus(429);
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("{\"code\":429,\"message\":\"系统繁忙\"}");
}
});
URL 资源清洗
对于 REST 风格的 API,需要清洗 URL 资源,避免资源数量过多:
WebCallbackManager.setUrlCleaner(new UrlCleaner() {
@Override
public String clean(String originUrl) {
if (originUrl == null || originUrl.isEmpty()) {
return originUrl;
}
// 将 /user/123 归一化为 /user/*
if (originUrl.matches("/user/\\d+")) {
return "/user/*";
}
// 将 /order/abc-123 归一化为 /order/*
if (originUrl.matches("/order/[a-z0-9-]+")) {
return "/order/*";
}
// 过滤静态资源
if (originUrl.endsWith(".ico") || originUrl.endsWith(".css") || originUrl.endsWith(".js")) {
return "";
}
return originUrl;
}
});
请求来源解析
按来源限流需要实现 RequestOriginParser:
WebCallbackManager.setRequestOriginParser(new RequestOriginParser() {
@Override
public String parseOrigin(HttpServletRequest request) {
// 从请求头获取来源
String origin = request.getHeader("X-Request-Source");
if (origin != null && !origin.isEmpty()) {
return origin;
}
// 从请求参数获取
origin = request.getParameter("source");
if (origin != null && !origin.isEmpty()) {
return origin;
}
// 默认使用 IP
return request.getRemoteAddr();
}
});
Dubbo 适配
Sentinel 提供 Dubbo 的适配模块,可以自动统计 Dubbo 服务接口和方法的调用。
引入依赖
根据 Dubbo 版本选择对应的适配模块:
<!-- Apache Dubbo 3.x -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-apache-dubbo3-adapter</artifactId>
<version>1.8.8</version>
</dependency>
<!-- Apache Dubbo 2.7.x -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
<version>1.8.8</version>
</dependency>
<!-- Dubbo 2.6.x -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-dubbo-adapter</artifactId>
<version>1.8.8</version>
</dependency>
资源名规则
引入依赖后,Dubbo 的服务接口和方法会自动成为 Sentinel 资源:
- 接口级别:接口全限定名,如
com.example.UserService - 方法级别:
接口全限定名:方法名(参数类型),如com.example.UserService:getUser(java.lang.String)
配置限流规则
// 对接口限流
FlowRule interfaceRule = new FlowRule();
interfaceRule.setResource("com.example.UserService");
interfaceRule.setCount(100);
// 对方法限流
FlowRule methodRule = new FlowRule();
methodRule.setResource("com.example.UserService:getUser(java.lang.String)");
methodRule.setCount(50);
FlowRuleManager.loadRules(Arrays.asList(interfaceRule, methodRule));
自定义降级处理
// 实现自定义 fallback
DubboFallback fallback = new DubboFallback() {
@Override
public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) {
// 返回降级结果
return new AppResponse("服务繁忙,请稍后重试");
}
};
// 注册 fallback
DubboFallbackRegistry.setConsumerFallback(fallback);
关闭适配
如果不需要某个 Filter,可以手动关闭:
<!-- 关闭 Consumer 端 Filter -->
<dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/>
<!-- 关闭 Provider 端 Filter -->
<dubbo:provider filter="-sentinel.dubbo.provider.filter"/>
gRPC 适配
Sentinel 提供 gRPC 的适配模块,通过 ServerInterceptor 和 ClientInterceptor 保护 gRPC 服务。
引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-grpc-adapter</artifactId>
<version>1.8.8</version>
</dependency>
服务端配置
import com.alibaba.csp.sentinel.adapter.grpc.SentinelGrpcServerInterceptor;
import io.grpc.Server;
import io.grpc.ServerBuilder;
public class GrpcServer {
public static void main(String[] args) throws Exception {
Server server = ServerBuilder.forPort(50051)
.addService(new MyServiceImpl())
.intercept(new SentinelGrpcServerInterceptor()) // 注册拦截器
.build();
server.start();
server.awaitTermination();
}
}
客户端配置
import com.alibaba.csp.sentinel.adapter.grpc.SentinelGrpcClientInterceptor;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
public class GrpcClient {
private final ManagedChannel channel;
public GrpcClient(String host, int port) {
this.channel = ManagedChannelBuilder.forAddress(host, port)
.intercept(new SentinelGrpcClientInterceptor()) // 注册拦截器
.build();
}
}
资源名规则
- 服务端:
方法全限定名,如com.example.MyService/getUser - 客户端:
方法全限定名,与服务端相同
注意:Sentinel gRPC 适配目前只支持 unary call。
Spring WebFlux 适配
Sentinel 提供与 Spring WebFlux 的整合模块,支持响应式 Web 应用的流量控制。
引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-webflux-adapter</artifactId>
<version>1.8.8</version>
</dependency>
配置
import com.alibaba.csp.sentinel.adapter.spring.webflux.SentinelWebFluxFilter;
import com.alibaba.csp.sentinel.adapter.spring.webflux.exception.SentinelBlockExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@Configuration
public class WebFluxConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelBlockExceptionHandler sentinelBlockExceptionHandler() {
return new SentinelBlockExceptionHandler();
}
@Bean
@Order(-1)
public SentinelWebFluxFilter sentinelWebFluxFilter() {
return new SentinelWebFluxFilter();
}
}
自定义限流处理
import com.alibaba.csp.sentinel.adapter.spring.webflux.callback.WebFluxCallbackManager;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
@Configuration
public class SentinelConfig {
@PostConstruct
public void init() {
// 自定义限流处理
WebFluxCallbackManager.setBlockHandler((exchange, t) -> {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
String result = "{\"code\":429,\"message\":\"系统繁忙\"}";
return exchange.getResponse().writeWith(
Mono.just(exchange.getResponse().bufferFactory().wrap(result.getBytes()))
);
});
}
}
Reactor 适配
Sentinel 提供对 Reactor 的适配,可以在响应式应用中接入 Sentinel。
引入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-reactor-adapter</artifactId>
<version>1.8.8</version>
</dependency>
使用方式
import com.alibaba.csp.sentinel.adapter.reactor.SentinelReactorTransformer;
import reactor.core.publisher.Mono;
public class ReactiveService {
public Mono<String> doSomething(String param) {
return Mono.fromCallable(() -> {
// 业务逻辑
return "result: " + param;
})
// 使用 Sentinel 转换器包装
.transform(new SentinelReactorTransformer<>("resourceName"));
}
// 对于 Flux
public Flux<String> doSomethingFlux() {
return Flux.range(1, 10)
.map(i -> "item-" + i)
.transform(new SentinelReactorTransformer<>("fluxResource"));
}
}
HTTP Client 适配
Apache HttpClient
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-apache-httpclient-adapter</artifactId>
<version>1.8.8</version>
</dependency>
import com.alibaba.csp.sentinel.adapter.httpclient.SentinelHttpRequestRetryHandler;
import com.alibaba.csp.sentinel.adapter.httpclient.SentinelHttpClientBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
// 创建 Sentinel 包装的 HttpClient
CloseableHttpClient httpClient = new SentinelHttpClientBuilder()
.setRetryHandler(new SentinelHttpRequestRetryHandler(3, true))
.build();
// 执行请求时会自动进行限流
HttpGet request = new HttpGet("http://example.com/api");
HttpResponse response = httpClient.execute(request);
OkHttp
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-okhttp-adapter</artifactId>
<version>1.8.8</version>
</dependency>
import com.alibaba.csp.sentinel.adapter.okhttp.SentinelOkHttpInterceptor;
import okhttp3.OkHttpClient;
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new SentinelOkHttpInterceptor("okhttp-resource"))
.build();
RocketMQ 适配
在 RocketMQ 消费场景中,Sentinel 可以实现消息的削峰填谷,平滑处理突发消息。
使用场景
当消费者处理消息时,可能会遇到消息突刺。Sentinel 的匀速排队模式可以将消息均匀处理,避免系统负载过高。
配置示例
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
public class RocketMQConsumer {
private static final String KEY = "myGroup:myTopic";
public static void main(String[] args) {
// 初始化限流规则
initFlowControlRule();
// 创建消费者
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("myGroup");
consumer.subscribe("myTopic", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
Entry entry = null;
try {
// 资源名为 groupName:topicName
entry = SphU.entry(KEY);
// 处理消息
processMessage(msg);
} catch (BlockException e) {
// 被限流,等待重试
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
} finally {
if (entry != null) {
entry.exit();
}
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
}
private static void initFlowControlRule() {
FlowRule rule = new FlowRule();
rule.setResource(KEY);
rule.setCount(5); // 每秒处理 5 条消息
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER); // 匀速排队
rule.setMaxQueueingTimeMs(5000); // 最大排队时间 5 秒
FlowRuleManager.loadRules(Collections.singletonList(rule));
}
private static void processMessage(MessageExt msg) {
// 消息处理逻辑
System.out.println("Processing: " + new String(msg.getBody()));
}
}
削峰填谷效果
配置匀速排队后,消息处理的效果:
- QPS 设置为 5,则每 200ms 处理一条消息
- 多余的消息在队列中等待
- 等待超过
maxQueueingTimeMs的消息会被拒绝
Feign 整合
Feign 的适配整合在 Spring Cloud Alibaba 中,使用时需要开启 Sentinel 支持:
feign:
sentinel:
enabled: true
定义 Feign Client 并配置 fallback:
@FeignClient(name = "user-service", fallback = UserServiceFallback.class)
public interface UserServiceClient {
@GetMapping("/api/users/{id}")
User getUser(@PathVariable Long id);
}
@Component
public class UserServiceFallback implements UserServiceClient {
@Override
public User getUser(Long id) {
return User.defaultUser();
}
}
注意事项
1. 资源数量控制
不同 URL 会作为不同资源处理,对于 REST 风格 API,务必使用 UrlCleaner 进行资源清洗,避免资源数量超过阈值(默认 6000)。
2. 接入控制台
适配模块只提供适配功能,若希望接入 Sentinel 控制台,需要额外引入 sentinel-transport-simple-http 依赖并配置控制台地址。
3. 版本兼容性
选择适配模块时,注意与框架版本的兼容性:
| 适配模块 | 支持的框架版本 |
|---|---|
| sentinel-apache-dubbo3-adapter | Apache Dubbo 3.0.5+ |
| sentinel-apache-dubbo-adapter | Apache Dubbo 2.7.x |
| sentinel-dubbo-adapter | Dubbo 2.6.x |
4. 性能考虑
适配模块会增加一定的性能开销,在高性能场景下需要评估影响。
最佳实践
1. 选择合适的适配模块
根据项目使用的框架选择对应的适配模块,避免引入不必要的依赖。
2. 合理配置资源粒度
- 对于 Web 接口,建议按业务模块清洗 URL
- 对于 RPC 调用,建议在接口和方法两个级别分别配置规则
3. 提供有意义的降级响应
针对不同框架的特点,提供合适的降级响应:
- Web 接口:返回 JSON 格式的错误信息
- RPC 调用:返回默认值或抛出业务异常
- 消息队列:返回重试状态
4. 监控与告警
接入 Sentinel 控制台,实时监控各资源的调用情况,配置告警规则。
下一步
- Spring Cloud 整合 - 学习与 Spring Cloud Alibaba 的开箱即用整合
- 最佳实践 - 了解生产环境最佳实践