Dubbo Monitor 源码学习(九)

笔记简述
dubbo 的monitor是一个监控服务的模块,用来统计服务的调用次数以及调用的健康情况。接下来来学习monitor的实现原理
更多内容可看[目录]Dubbo 源码学习

目录

Dubbo Monitor 源码学习(九)
1、Filter入口
2、Monitor监控 & 收集
3、总结

1、Filter入口

作为服务提供方,monitor肯定是在invoker.invoker的时候判断是否存在monitor监听者从而判断是否进行统计情况。根据之前学习的内容,直接定位到ProtocolFilterWrapper类

ProtocolFilterWrapper 类

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        return protocol.export(invoker);
    }
    // 如果url的协议不是注册协议(一般情况是dubbo)
    return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    // 注意这个里面的Constants.PROVIDER,意味着是服务提供方,后面会具体说明的
}

public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
    if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
        return protocol.refer(type, url);
    }
    return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
    // 同上,只是是服务调用方
}

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    // 获取到合适的Filter链,其中包含了invoker对象
    Invoker<T> last = invoker;
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
    if (filters.size() > 0) {
        for (int i = filters.size() - 1; i >= 0; i --) {
             // 这个循环就好像是形成了一个链条,每一个对象都是一个filter,这样就可以利用这些filter完成特定的需求了
             // 同时,我们此小节说的monitor其实也是一个filter罢了
            final Filter filter = filters.get(i);
            final Invoker<T> next = last;
            last = new Invoker<T>() {

                public Class<T> getInterface() {
                    return invoker.getInterface();
                }

                public URL getUrl() {
                    return invoker.getUrl();
                }

                public boolean isAvailable() {
                    return invoker.isAvailable();
                }

                public Result invoke(Invocation invocation) throws RpcException {
                    // filter的invoke调用
                    return filter.invoke(next, invocation);
                }

                public void destroy() {
                    invoker.destroy();
                }

                @Override
                public String toString() {
                    return invoker.toString();
                }
            };
        }
    }
    return last;
}

当处理的url不是注册协议时,服务提供方和服务调用方都会去调用buildInvokerChain去构建一个调用链,调用链是根据dubbo spi获取到的合适的Filter对象串起来的,其中提供方和调用方就是根据Constants.CONSUMER以及Constants.PROVIDER选择合适的filter的,例如下面几个类的截图

image

image

image

很明显MonitorFilter类均会被服务提供方和服务调用方发现并使用,这里就是MonitorFilter正式进入他自己处理数据的范畴了

如下图就是生产的Filter链条,高亮的地方就是MonitorFilter


image

2、Monitor监控 & 收集

MonitorFilter 类

// 调用过程拦截
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    if (invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) {
        // 如果url参数中配置了monitor参数,并且有效
        RpcContext context = RpcContext.getContext(); // 提供方必须在invoke()之前获取context信息
        long start = System.currentTimeMillis(); // 记录起始时间戮
        getConcurrent(invoker, invocation).incrementAndGet(); // 并发计数
        // 这个是统计className+mathodName的调用次数,并存储在
        // ConcurrentMap<String, AtomicInteger> concurrents 集合中
        try {
            Result result = invoker.invoke(invocation); // 让调用链往下执行
            collect(invoker, invocation, result, context, start, false);
            // 收集数据
            return result;
        } catch (RpcException e) {
            collect(invoker, invocation, null, context, start, true);
            throw e;
        } finally {
            getConcurrent(invoker, invocation).decrementAndGet(); // 并发计数
        }
    } else {
        return invoker.invoke(invocation);
        // 没有设置monitor,则直接进行下一个filter的invoker调用
    }
}

就是通过该方法完成对请求的监控作用,当监控完成之后就是collect收集

private void collect(Invoker<?> invoker, Invocation invocation, Result result, RpcContext context, long start, boolean error) {
   // error字段表示了是否出现错误,在监听的catch代码块中该参数填充的true,意味着调用出现异常
    try {
        // ---- 服务信息获取 ----
        long elapsed = System.currentTimeMillis() - start; // 计算调用耗时
        int concurrent = getConcurrent(invoker, invocation).get(); // 当前并发数
        String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);  // 应用民称
        String service = invoker.getInterface().getName(); // 获取服务名称
        String method = RpcUtils.getMethodName(invocation); // 获取方法名
        URL url = invoker.getUrl().getUrlParameter(Constants.MONITOR_KEY);
        Monitor monitor = monitorFactory.getMonitor(url);
        // 利用静态工厂方法创建一个monitor,一般情况下是DubboMonitor对象
        int localPort;
        String remoteKey;
        String remoteValue;
        if (Constants.CONSUMER_SIDE.equals(invoker.getUrl().getParameter(Constants.SIDE_KEY))) {
            // ---- 服务消费方监控 ----
            context = RpcContext.getContext(); // 消费方必须在invoke()之后获取context信息
            localPort = 0;
            remoteKey = MonitorService.PROVIDER;
            remoteValue = invoker.getUrl().getAddress();
        } else {
            // ---- 服务提供方监控 ----
            localPort = invoker.getUrl().getPort();
            remoteKey = MonitorService.CONSUMER;
            remoteValue = context.getRemoteHost();
        }
        String input = "", output = "";
        if (invocation.getAttachment(Constants.INPUT_KEY) != null) {
            input = invocation.getAttachment(Constants.INPUT_KEY);
        }
        if (result != null && result.getAttachment(Constants.OUTPUT_KEY) != null) {
            output = result.getAttachment(Constants.OUTPUT_KEY);
        }
        monitor.collect(new URL(Constants.COUNT_PROTOCOL,
                            NetUtils.getLocalHost(), localPort,
                            service + "/" + method,
                            MonitorService.APPLICATION, application,
                            MonitorService.INTERFACE, service,
                            MonitorService.METHOD, method,
                            remoteKey, remoteValue,
                            error ? MonitorService.FAILURE : MonitorService.SUCCESS, "1",
                            MonitorService.ELAPSED, String.valueOf(elapsed),
                            MonitorService.CONCURRENT, String.valueOf(concurrent),
                            Constants.INPUT_KEY, input,
                            Constants.OUTPUT_KEY, output));
        // 拼接的url数据是count://host/interface?application=foo&method=foo&provider=10.20.153.11:20880&
        // success=12&failure=2&elapsed=135423423
    } catch (Throwable t) {
        logger.error("Failed to monitor count service " + invoker.getUrl() + ", cause: " + t.getMessage(), t);
    }
}

进行真正monitor.collect操作的是DubboMonitor类中,在进行collect调用的时候,会把数据存在ConcurrentMap<Statistics, AtomicReference<long[]>> statisticsMap对象中,DubboMonitor本身自带的scheduledExecutorService定时任务,1分钟处理一下收集到的统计情况调用MonitorService的collect方法。最后可以使用MonitorService的query方法查询到具体的统计结果

这个MonitorService类在dubbo本身是getProxy动态生成的了,在dubbo-ops中提供了一个SimpleMonitorService方法可以看看其具体的如何展示数据的

3、总结

总的来说,monitor本身的原理还是比较简单的,利用filter完成对请求的监控,后续利用定时任务定时采集监控的数据,后续利用MonitorService实现对数据的收集和监听

同时,我们也可以添加自定义的Filter以及特定的MonitorService完成自身特定的需求等

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容

  • Dubbo是什么 Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式...
    Coselding阅读 17,208评论 3 196
  • 本文转自:Dubbo架构设计详解,原作者是:时延军 Dubbo是Alibaba开源的分布式服务框架,它最大的特点是...
    程序熊大阅读 3,458评论 3 45
  • 感恩 曾经一度陷入了困局中,就是樊登老师所讲的失意忘形的状态。 所以我遇到了吸引力法则,遇到了台湾的秋恺老师克服A...
    Zandra梓桐阅读 284评论 0 0
  • 精诚所至,金石为开 【释义】精诚所至,金石为开,意指人诚心所到,能感动天地,使金石为之开裂。比喻只要专心诚意去做,...
    那些貌似无用的喜欢阅读 766评论 0 0
  • 尽管我们对这个时代依旧会存在很多不满,但不得不说的是,我们正处于一个最好的时代。 国家富足,物质丰裕,个人有着无数...
    李金梅ljm阅读 575评论 0 0