上篇文章介绍了sentinel的简单示例和一些架构、基本原理。本文重点看看如何和一些已有系统整合使用。
框架适配
servlet
其实很好理解,做一个filter就可以了。官方已经有对应适配:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
<version>x.y.z</version>
</dependency>
CommonFilter代码:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest sRequest = (HttpServletRequest)request;
Entry entry = null;
try {
String target = FilterUtil.filterTarget(sRequest);
UrlCleaner urlCleaner = WebCallbackManager.getUrlCleaner();
if (urlCleaner != null) {
target = urlCleaner.clean(target);
}
String origin = parseOrigin(sRequest);
ContextUtil.enter(target, origin);
entry = SphU.entry(target, EntryType.IN);
chain.doFilter(request, response);
} catch (BlockException e) {
HttpServletResponse sResponse = (HttpServletResponse)response;
WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e);
} catch (IOException e2) {
Tracer.trace(e2);
throw e2;
} catch (ServletException e3) {
Tracer.trace(e3);
throw e3;
} catch (RuntimeException e4) {
Tracer.trace(e4);
throw e4;
} finally {
if (entry != null) {
entry.exit();
}
ContextUtil.exit();
}
}
类型一
/hello
resourece: hello
context:hello
origin: ""
类型二
如果是restapi方式如:/user/{id}
,此时如果不做配置,会根据id出现很多个资源,但其实资源是user。此时可以注册一个url清理器:
WebCallbackManager.setUrlCleaner(new UrlCleaner() {
@Override
public String clean(String originUrl) {
if (originUrl.startsWith("/user/")) {
return "/user/*";
}
return originUrl;
}
});
这样,他的资源名就变成了/user/*
类型三 如果我想对某一个调用我的源限流,比如我只限制pc访问,或者只限制某个系统过来的请求,origin就不能是”“,怎么设置能,sentinel提供了解析方式,从header读取资源作为origin。至于读取什么header,由自己定义。
WebCallbackManager.setRequestOriginParser(new RequestOriginParser() {
@Override
public String parseOrigin(HttpServletRequest request) {
String origin = request.getHeader(headerName);
return origin != null ? origin : "";
}
});
如果限流后我希望跳转到指定页面,也可以配置:
WebServletConfig.setBlockPage(redirectUrl);
spring mvc
和servlet一样,引入相同包,配置过滤器即可:
@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;
}
}
doubbo
官方是配置
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-dubbo-adapter</artifactId>
<version>x.y.z</version>
</dependency>
原理和servlet差不多,也是自定义一个两个filter,一个消费的,一个提供者的。比如:
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
// Get origin caller.
String application = DubboUtils.getApplication(invocation, "");
Entry interfaceEntry = null;
Entry methodEntry = null;
try {
String resourceName = getResourceName(invoker, invocation);
String interfaceName = invoker.getInterface().getName();
// 链路名为resourceName,origin是applicationname
ContextUtil.enter(resourceName, application);
// 两个entry,分别是`接口`和`接口:方法`。
interfaceEntry = SphU.entry(interfaceName, EntryType.IN);
methodEntry = SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
Result result = invoker.invoke(invocation);
if (result.hasException()) {
Tracer.trace(result.getException());
}
return result;
} catch (BlockException e) {
return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
} catch (RpcException e) {
Tracer.trace(e);
throw e;
} finally {
if (methodEntry != null) {
methodEntry.exit(1, invocation.getArguments());
}
if (interfaceEntry != null) {
interfaceEntry.exit();
}
ContextUtil.exit();
}
}
还有grpc适配什么的,反正原理都差不多,如果自定义是配置,统一注意几个名词就好:链路,源,资源切入口。
WebCallbackManager设计比较赞,让用户自定义解析方法。
自定义slot
上篇文章有讲,sentinel使用责任链方式,把一个一个插槽连起来。
// spi的方式,让使用方实现SlotChainBuilder。
private static final ServiceLoader<SlotChainBuilder> LOADER = ServiceLoader.load(SlotChainBuilder.class);
private static void resolveSlotChainBuilder() {
List<SlotChainBuilder> list = new ArrayList<SlotChainBuilder>();
boolean hasOther = false;
for (SlotChainBuilder builder : LOADER) {
if (builder.getClass() != DefaultSlotChainBuilder.class) {
hasOther = true;
list.add(builder);
}
}
if (hasOther) {
builder = list.get(0);
} else {
// 没有自定义slotbuilder就使用DefaultSlotChainBuilder。
builder = new DefaultSlotChainBuilder();
}
}
从代码可以看到,主要是通过spi方式实现。所以实现接口,并定义services即可:
public class DemoSlotChainBuilder implements SlotChainBuilder {
@Override
public ProcessorSlotChain build() {
ProcessorSlotChain chain = new DefaultSlotChainBuilder().build();
// 把子顶一个DemoSlot加入链中
chain.addLast(new DemoSlot());
return chain;
}
}
新建META-INFO/services
目录,指定spi实现。
数据源
sentinel已经提供了很多数据源的实现,比如zk,redis,appollo等。自己实现主要做两件事:
- 实现AbstractDataSource。主要是获取数据源连接,load配置,监听配置等(注意更新方式)。
- 指定配置解析器,比如json解析。