美文网首页
云米客户端APM性能监控组件分析(五)

云米客户端APM性能监控组件分析(五)

作者: 捉影T_T900 | 来源:发表于2020-04-09 15:55 被阅读0次

上一篇文章已经分析了内存的监控原理,现在来分析网络IO的原理和技巧。

网络通讯,本质上是IO流的处理。既然是IO方面的内容,那么就有输入和输出,就可以在整个过程中进行拦截,获取我们想要的信息。目前这个组件只针对普通Http/Https通讯进行拦截获取数据,所以数据来自于Http包体的各种信息(Header、Request、Response等)。另外网络通讯框架用的是OKHttp,相信绝大多数的Android网络通讯框架都是这个,所以主要实现方式是通过增加自定义的拦截器,获得通讯过程中的各项数据。

OKHttp怎么增加自己编写的拦截器,请自行百度...

1、实现自定义拦截器

    private OkHttpData okHttpData;

    @Override
    public Response intercept(Chain chain) throws IOException {

        long startNs = System.currentTimeMillis();

        okHttpData = new OkHttpData();
        okHttpData.startTime = startNs;

        if (Manager.isDebug()) {
            ApmLogX.i(APM_TAG, SUB_TAG, "okhttp request startTime:" + TimeUtils.getFormatTime(okHttpData.startTime, TimeUtils.DATETIMEMILLIS_SPLIT));
        }

        Request request = chain.request();

        recordRequestData(request);

        Response response;

        try {
            response = chain.proceed(request);
        } catch (IOException e) {
            if (Manager.isDebug()) {
                e.printStackTrace();
                ApmLogX.e(APM_TAG, SUB_TAG, "http faild:" + e.getMessage());
            }
            okHttpData.errorMessage = e.getMessage();
            DataRecordUtils.recordNetinfo(okHttpData);
            throw e;
        }

        okHttpData.costTime = System.currentTimeMillis() - startNs;

        if (Manager.isDebug()) {
            ApmLogX.e(APM_TAG, SUB_TAG, "request costTime:" + TimeUtils.millsToTime(okHttpData.costTime));
        }

        recordResponseData(response);

        if (Manager.isDebug()) {
            ApmLogX.d(APM_TAG, SUB_TAG, "okhttp chain.proceed end");
        }

        DataRecordUtils.recordNetinfo(okHttpData);
       
        return response;
    }

    private void recordRequestData(Request request) {
        if (null == request ||
                null == request.url() ||
                TextUtils.isEmpty(request.url().toString())) {
            return;
        }

        okHttpData.url = request.url().toString();
        okHttpData.headers = request.headers().toString();
        okHttpData.method = request.method();
        if (Manager.isDebug()) {
            ApmLogX.d(APM_TAG, SUB_TAG, "okhttp request url:" + okHttpData.url);
            ApmLogX.d(APM_TAG, SUB_TAG, "okhttp request headers:\n" + okHttpData.headers);
            ApmLogX.d(APM_TAG, SUB_TAG, "okhttp request method:" + okHttpData.method);
        }

        RequestBody requestBody = request.body();
        if (requestBody == null) {
            okHttpData.requestSize = request.url().toString().getBytes().length;
            if (Manager.isDebug()) {
                ApmLogX.d(APM_TAG, SUB_TAG, "okhttp request upload datasize:" + okHttpData.requestSize + " byte");
            }
            return;
        }

        long contentLength = 0;
        try {
            contentLength = requestBody.contentLength();
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (contentLength > 0) {
            okHttpData.requestSize = contentLength;
        } else {
            okHttpData.requestSize = request.url().toString().getBytes().length;
        }
    }

    private void recordResponseData(Response response) {
        if (response == null) {
            return;
        }

        okHttpData.responseCode = response.code();

        if (Manager.isDebug()) {
            ApmLogX.d(APM_TAG, SUB_TAG, "okhttp response code:" + okHttpData.responseCode);
        }

        ResponseBody responseBody = response.body();
        if (responseBody == null) {
            return;
        }

        long contentLength = responseBody.contentLength();

        if (contentLength > 0) {
            if (Manager.isDebug()) {
                ApmLogX.d(APM_TAG, SUB_TAG, "直接通过responseBody取到contentLength:" + contentLength + " byte");
            }
        } else {
            BufferedSource source = responseBody.source();
            if (source != null) {
                try {
                    source.request(Long.MAX_VALUE);
                } catch (IOException e) {
                    e.printStackTrace();
                }

                Buffer buffer = source.buffer();
                contentLength = buffer.size();

                if (Manager.isDebug()) {
                    ApmLogX.e(APM_TAG, SUB_TAG, "通过responseBody.source()取到contentLength:" + contentLength + " byte");
                }
            }
        }

        okHttpData.responseSize = contentLength;

        if (Manager.isDebug()) {
            ApmLogX.d(APM_TAG, SUB_TAG, "okhttp response download size:" + okHttpData.responseSize + " byte" );
        }
    }

我觉得监控网络的数据真的超简单的,OkHttpData是一个数据实体,里面定义各种想保存的数据属性。在chain.proceed前记录上行数据,包括header、url、method、参数、数据量大小等。在chain.proceed后记录下行数据,包括返回码、返回数据大小、返回数据内容等。拿到这些数据后,后面的处理逻辑就悉随君便了...


这是第五篇分析文章,核心功能已经分析完了,剩下的就是拿到数据之后存储、上报、旧数据清理等的功能,这里不赘述。写程序、做功能,不应只停留在把这个功能写完了,测试测过能通就算完成了,还要关注整个过程的数据细节,是不是有哪些地方做得不足,还可以改善,能不能协同其他部门一起把事情处理成一个闭环,让整个业务在这个闭环内流畅运转,从而产生实际收益,这才是一个专业的技术人员应有的职业素养及眼光。而不是慵懒、狭隘地认为只要做完功能就好了,多一步都不去想,拿工资就完事。最终受损的还是你自己,企业一直在向上,而你却原地转圈......

下一个系列是“插件化”相关专题

相关文章

网友评论

      本文标题:云米客户端APM性能监控组件分析(五)

      本文链接:https://www.haomeiwen.com/subject/vcduyctx.html