白话Dubbo(四):微服务治理

2020-03-21  本文已影响0人  空挡

前言

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

Filter详解

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

Filter原理

首先来回顾下Dubbo的调用关系图:


Consumer Cluster

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透明化。
大概的逻辑图如下:


FilterChain

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机制提高了用户扩展的灵活性,而自身也受益于该机制来满足微服务治理的需求。
通过前面4篇文章简单的分解了下Dubbo框架,希望能够对Dubbo建立一个大体的框架图,使阅读源码或者理解源码分解文章更有帮助。当然,dubbo也一直再发展过程中,很多特性暂时没有提及,比如最新版本支持将配置信息从url中分离出来放到配置中心;还有异步化调用支持等。在白话Dubbo部分,Dubbo的核心SPI机制也没有涉及,个人认为放到源码详解部分更为合适,感谢继续关注后续文章。

上一篇下一篇

猜你喜欢

热点阅读