SentinelZuulInboundFilter
- 路由提取器提取资源信息进行限流
- 根据api等信息进行限流
- 发生限流则返回
- 构建ZUUL_CTX_SENTINEL_ENTRIES_KEY信息交由OutBound完成sentinel工作现场清理
public class SentinelZuulInboundFilter extends HttpInboundFilter {
@Override
public Observable<HttpRequestMessage> applyAsync(HttpRequestMessage request) {
限流执行入口
if (executor != null) {
return Observable.just(request).subscribeOn(Schedulers.from(executor)).flatMap(this::apply);
} else {
return Observable.just(request).flatMap(this::apply);
}
}
应用sentinel限流逻辑
private Observable<HttpRequestMessage> apply(HttpRequestMessage request) {
SessionContext context = request.getContext();
Deque<EntryHolder> holders = new ArrayDeque<>();
String routeId = routeExtractor.apply(request);
String fallBackRoute = routeId;
try {
提起路由id
if (StringUtil.isNotBlank(routeId)) {
构建context
ContextUtil.enter(GATEWAY_CONTEXT_ROUTE_PREFIX + routeId);
执行限流逻辑
doSentinelEntry(routeId, RESOURCE_MODE_ROUTE_ID, request, holders);
}
提取匹配的api信息
Set<String> matchingApis = pickMatchingApiDefinitions(request);
if (!matchingApis.isEmpty() && ContextUtil.getContext() == null) {
ContextUtil.enter(SentinelZuul2Constants.ZUUL_DEFAULT_CONTEXT);
}
限流拦截
for (String apiName : matchingApis) {
fallBackRoute = apiName;
doSentinelEntry(apiName, RESOURCE_MODE_CUSTOM_API_NAME, request, holders);
}
return Observable.just(request);
} catch (BlockException t) {
发生限流
context.put(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_BLOCKED_FLAG, Boolean.TRUE);
context.put(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_FALLBACK_ROUTE, fallBackRoute);
if (fastError) {
context.setShouldSendErrorResponse(true);
context.setErrorEndpoint(blockedEndpointName);
} else {
context.setEndpoint(blockedEndpointName);
}
return Observable.error(t);
} finally {
交给出栈过滤器如何清空entry
if (!holders.isEmpty()) {
context.put(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_ENTRIES_KEY, holders);
}
ContextUtil.exit();
}
}
执行限流逻辑
private void doSentinelEntry(String resourceName, final int resType, HttpRequestMessage input, Deque<EntryHolder> holders) throws BlockException {
Object[] params = paramParser.parseParameterFor(resourceName, input, r -> r.getResourceMode() == resType);
AsyncEntry entry = SphU.asyncEntry(resourceName, ResourceTypeConstants.COMMON_API_GATEWAY, EntryType.IN, params);
holders.push(new EntryHolder(entry, params));
}
}
SentinelZuulOutboundFilter
public class SentinelZuulOutboundFilter extends HttpOutboundFilter {
@Override
public Observable<HttpResponseMessage> applyAsync(HttpResponseMessage input) {
return Observable.just(apply(input));
}
public HttpResponseMessage apply(HttpResponseMessage response) {
SessionContext context = response.getContext();
不存在entry则直接响应
if (context.get(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_ENTRIES_KEY) == null) {
return response;
}
存在entry则通过exit清理entry
boolean previousBlocked = context.getFilterErrors().stream()
.anyMatch(e -> BlockException.isBlockException(e.getException()));
Deque<EntryHolder> holders = (Deque<EntryHolder>) context.get(SentinelZuul2Constants.ZUUL_CTX_SENTINEL_ENTRIES_KEY);
while (!holders.isEmpty()) {
EntryHolder holder = holders.pop();
if (!previousBlocked) {
Tracer.traceEntry(context.getError(), holder.getEntry());
holder.getEntry().exit(1, holder.getParams());
}
}
return response;
}
}