该文原创为新潮质量保障技术团队中的 “上进的中年软件测试从业者”,用于技术交流分享

上一篇给自己立了一个 flag,这周内把测试平台 - 慎用 PRC 返回的数据(一)的第二篇文章发出来,原本的计划是在公司周天下午开员工大会时来写好。结果今天下午发了通知出来,本周天的员工大会取消,另行通知。那么问题来了,我是周五晚上写完,还是周末在家写。为了周末过得纯粹一点,还是决定现在就开始写。

昨天晚上在西瓜视频看到一个自媒体作者在说自己为什么能够成功,第一个是坚持,第二个是研究了抖音的智推机制,还有一个是亲力亲为,不管是拍摄、剪辑、后期制作还是运营。每天一早上起来想的就是今天的拍摄主题是什么。所以如果不知道怎么做,何不如参照成功的案例呢。

开篇

之前我们讲到了如何更方便的使用 RPC 服务端返回的 Response,核心就是序列化的过程。

为了探索 RPC 服务端在通信过程中是如何区别对待不同类型数据的,我们开始去研究源码。不知道大佬们都怎么追源码(相对层级比较多的),我的方式相对就非常的简单粗暴,直接根据返回的各种有用的信息,在源码包里面全局搜索,借助 Pycharm 可以非常便利的定位到想到的信息,还可以借助 grepWin 进行硬盘扫描,当然 grepWin 更适合的场景是查找包含某些特性关键字的文件。

调研

从服务端返回的字典类型的,那我们就直接通过关键字 “<netref class ” 进行搜索,然后就发现了:

class NetrefMetaclass(type):
slots = ()

def repr(self):
if self.module:
return "" % (self.module, self.name)
else:
return "" % (self.name,)

repr是做什么的,与str有什么区别,感兴趣的可以去搜一下看看。然后经过一系列的追踪 NetrefMetaclass->BaseNetref->class_factory->netref 模块->???->???, 然后就没有然后了。也就是根本不知道追到哪里去了。

感谢伟大的百度和广大的爱好者,找到了另外一种可以 Google 的稳定方式,避免广告嫌疑,自行百度(一键脚本搭建科学上网 Trojan 梯子 (自建 ***) 详细教程)。然后 Google 搜了一遍,最后决定放弃,不想再分析了。

过了几天无意看到之前打开的一个 rpyc 官网,看到了一个非常重要的信息介绍,也就是我们上一篇提到的 box, 下图是官网的原文。

根据上面的介绍,我们知道了有些时候服务端是直接返回值的(By Value), 有些时候是直接返回引用(By Reference)。这是一个很重要的突破,那我们就可以通过所谓的 Box 设计来追踪究竟什么情况下会 By Value, 什么情况下会 By Reference。然后就顺利的找到了 box 方法:

def _box(self, obj): # boxing
if brine.dumpable(obj):
return consts.LABEL_VALUE, obj
if type(obj) is tuple:
return consts.LABEL_TUPLE, tuple(self._box(item) for item in obj)
elif isinstance(obj, netref.BaseNetref) and obj.____conn__ is self:
return consts.LABEL_LOCAL_REF, obj.____id_pack__

根据上面的代码,我们猜测:

- 如果 obj 可以被 dump, 那么直接返回。

- 否则会返回 reference。

为什么这样做?

根据官网的介绍,如下:

也就是 RPC 的机制并不能传输除字节和文本以外的对象,所以只能传递 reference,也就是需要用的时候,再去调用。

至此,原理搞清楚了,但是还是没有从调用过程上面来找到完整的调用链路。

解决

考虑到以后还是会经常的去查看源码,纯手动去追踪链路过程太繁琐,然后就 Google 找到了一种调用关系追踪过程 trace,具体的命令如下:

python -m trace --ignore-dir=.../lib/python3.7 --trace test.py >result.txt

然后我们就发现了非常全面的根据记录:

结语

trace 同样支持管道过滤,可以过滤出自己关心的文件,也可以忽略追踪的路径。除了 trace 还有很多开源的工具,也可以很方便的追踪执行过程,后续我们会写一篇关于调用过程的追踪。

今天就介绍到这里,或许能安心的过个周末,或许也不一定。谁又知道呢。


↙↙↙阅读原文可查看相关链接,并与作者交流