Dubbo漫谈之微服务治理

前面几篇文章结合rpc框架的基础需求,讲解了Dubbo中对于这些需求的抽象。Dubbo现在能够被如此多的线上应用所采用,跟最近几年微服务的广泛推广有很大的关系。微服务绝不仅仅是把服务拆小,改成远程调用这么简单,必须有配套的服务治理的功能,比如监控、熔断限流等。这篇文章就来分解下Dubbo是怎样来支持这些功能的。 

Filter详解

之前讲到Dubbo对注册中心的支持是通过抽象出一个Directory接口来实现的。相对来说,注册中心的职责功能是比较明确的,主流的注册中心实现对外的接口相对统一,区别是在内部实现上。但是对于微服务相关的其它功能,因为需求个性化太强,却很难做这样的抽象。Dubbo是通过Filter来实现对扩展功能的支持。 

Filter原理

首先来回顾下Dubbo的调用关系图: 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透明化。 大概的逻辑图如下: Dubbo漫谈之微服务治理 

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机制提高了用户扩展的灵活性,而自身也受益于该机制来满足微服务治理的需求。
上一篇:运维编排场景系列-----每日统计多Region实例的运行状态


下一篇:【Android】自定义view-拖动小球移动