• Dubbo漫谈之微服务治理


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

    Filter详解

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

    Filter原理

    首先来回顾下Dubbo的调用关系图:
    0
    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透明化。 大概的逻辑图如下:
    0 

    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机制提高了用户扩展的灵活性,而自身也受益于该机制来满足微服务治理的需求。
  • 相关阅读:
    python之函数一
    python之字典
    分支与master切换 | MyEclipse git怎么提交代码
    gitignore的使用详细图解
    1.1(学习笔记)Servlet简介及一个简单的实例
    10.4(java学习笔记)CLOB,BLOB基本操作
    10.3(Java学习笔记)JDBC时间操作
    10.2(java学习笔记)JDBC事务简述
    10.1(java学习笔记)JDBC基本操作(连接,执行SQL语句,获取结果集)
    9.1(java学习笔记)正则表达式
  • 原文地址:https://www.cnblogs.com/johnvwan/p/15649200.html
Copyright © 2020-2023  润新知