前面几篇文章结合rpc框架的基础需求,讲解了Dubbo中对于这些需求的抽象。Dubbo现在能够被如此多的线上应用所采用,跟最近几年微服务的广泛推广有很大的关系。微服务绝不仅仅是把服务拆小,改成远程调用这么简单,必须有配套的服务治理的功能,比如监控、熔断限流等。这篇文章就来分解下Dubbo是怎样来支持这些功能的。
Filter详解
之前讲到Dubbo对注册中心的支持是通过抽象出一个Directory接口来实现的。相对来说,注册中心的职责功能是比较明确的,主流的注册中心实现对外的接口相对统一,区别是在内部实现上。但是对于微服务相关的其它功能,因为需求个性化太强,却很难做这样的抽象。Dubbo是通过Filter来实现对扩展功能的支持。
Filter原理
首先来回顾下Dubbo的调用关系图:
Consumer端所有的远程调用通过Invoker来发起,而Dubbo通过在Invoker上加上Filter链,在调用前后可以添加扩展逻辑。同样,在服务提供端的Invoker上也有一样的逻辑。熟悉web开发的肯定对这种方式不陌生,Servlet中的Filter就是这么实现的。 首先来看下Filter接口的定义:
@SPI public interface Filter { /** * Make sure call invoker.invoke() in your implementation. */ Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException; }
Filter接口在Invoker被调用的前后被调用,在添加自己的逻辑之后,通过调用invoker参数的invoke()方法,来将请求继续传下去,这个跟Servlet中的FilterChain有点类似,但又不完全一样,下面会解析源代码。
Filter和Invoker集成
Filter要对用户无感知,就要将Filter和Invoker集成到一起,伪装成一个Invoker,这一点是通过ProtocolFilterWrapper类来实现的,这个类是Protocol的实现类。之前的文章已经讲过,Dubbo在初始化代理的时候,是通过Protocol来获取Invoker的,比如服务提供方使用dubbo协议来暴露服务,那么Consumer端就通过DubboProtocol来获取Invoker的引用。 其实,在DubboProtocol的外层还有一个装饰类ProtocolFilterWrapper来将Filter集成进去。至于代理在获取的时候是怎么得到ProtocolFilterWrapper的,这个跟Dubbo的扩展加载机制有关。
ProtocolFilterWrapper
public class ProtocolFilterWrapper implements Protocol { private final Protocol protocol; public ProtocolFilterWrapper(Protocol protocol) { if (protocol == null) { throw new IllegalArgumentException("protocol == null"); } this.protocol = protocol; } @Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { if (UrlUtils.isRegistry(invoker.getUrl())) { return protocol.export(invoker); } return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER)); } @Override public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException { if (UrlUtils.isRegistry(url)) { return protocol.refer(type, url); } return buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER); } }
从上面的代码可以看到,export和refer都是调用的封装的protocol的方法,对于dubbo协议,这个protocol就是一个DubboProtocol的实例。这里用了buildInvokerChain()方法来将Filter和原始的Invoker做了绑定。export()方法中,只会绑定用于Provider端的Filter,refer()方法中只会绑定用于Consumer端的Filter。
Filter链
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) { Invoker<T> last = invoker; //加载所有可用的Filter List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group); if (!filters.isEmpty()) { //从后往前连接 for (int i = filters.size() - 1; i >= 0; i--) { final Filter filter = filters.get(i); final Invoker<T> next = last; //将filter装饰成一个invoker last = new Invoker<T>() { ... ... @Override public Result invoke(Invocation invocation) throws RpcException { Result asyncResult; try { //调用filter的invoker方法,filter完成自己的逻辑后必须调用next.invoke() asyncResult = filter.invoke(next, invocation); } catch (Exception e) { ... ... throw e; } finally { } return asyncResult.whenCompleteWithContext((r, t) -> { ... ... }); } ... }; } } return last; }
上面的代码为了简单,将异步处理的代码省略掉了。跟Servlet中额外定义一个FilterChain,通过FilterChain来调用Filter不同,Dubbo中直接将Filter封装成一个Invoker,然后将多个Invoker连接在一起。对于Consumer端的代理类来说,从protocol获取到一个的Invoker,调用invoke()方法,实际上是调用的这个链条的header,然后header再把请求传递下去。这样就把Filter的整个逻辑对Proxy透明化。 大概的逻辑图如下:
Filter列表
除了用户可以自定义自己的Filter之外,Dubbo自身很多功能也依赖了Filter方式来实现,比如服务监控。下面简单列举下比较重要的:
Filter
|
注释
|
AccessLogFilter
|
访问日志记录,默认写到文件中
|
ActiveLimitFilter
|
Consumer端限流,限制并发请求数
|
ExecuteLimitFilter
|
Provider端限流,作用同ActiveLimitFilter
|
MonitorFilter
|
Dubbo监控,将收集到的指标数据发给MonitorService
|
MetricsFilter
|
对接Ali开源的Metric监控,类似于dropwizard metrics,是现在比较主流的监控指标收集上报方式
|
GenericFilter
|
泛化调用支持,多用于调用方没有服务提供方api的情况,只需要使用map传递参数就可调用。典型应用如一个支持dubbo的 job 调度中心,不需要把api的jar上传就可以调用dubbo接口
|
TokenFilter
|
token校验,用于限制接口的访问权限,只允许携带合法token的consumer调用
|
总结
Dubbo通过Filter机制提高了用户扩展的灵活性,而自身也受益于该机制来满足微服务治理的需求。