查看原文
其他

过往工作中的一些趣事

果冻虾仁 编程往事 2022-08-22

点击蓝字关注我吧~

腾讯篇

腾讯BG各自为战,每个BG甚至某个部门都有自成一派的研发体系。我当时所在部门也不例外,它自己有一套从研发到上线的内部系统。当时我们写的代码提交之后都会触发云端的自动编译,编译出来bin供给后续部署上线。这套CI/CD的流程大家想必在各个公司都有体验。

笔者当时利用这个自动编译平台的设计漏洞做过的一件趣事。由于多为C++模块,各个团队模块编译方式千差万别,没有统一的构建工具。所以每个模块(指一个代码库)需要在这个自动编译的平台网站上设置编译方式,比如make命令,或者指定执行一个shell脚本。因为你可能要在脚本中做一些准备工作才能让自动编译的机器正常编译,当然前提是把脚本也放到代码库中。

当编译任务触发时,编译机上的脚本会用这个账户来下载代码,然后执行设置的编译命令来执行编译动作。最后将结果打包成zip,然后上传获取一个FTP地址。

通常来说,代码仓库都是有权限控制的,即使是一个部门,也不会让所有源码默认对所有人开放,这不难理解。有趣的地方就出在这个自动编译机的原理上,自动编译的机器可以编译整个大部门所有团队的代码,当时他们是创建了一个具有极高代码权限的虚拟账户,用来下载代码。

在我后来的其他工作经历中CI/CD的流程中,直线构建任务时,都是以触发任务的那个用户的身份来执行编译。换句话说也就是没办法下载没有权限的代码。

本来我和这个平台也相安无事,直到后来。

回说一些工作背景,我们组负责的业务是两年前开辟的新业务,整个后端都是从头架设的。初创之时(在我来之前)为了快速上线,其中一些基础模块是直接搬运的成熟业务中的模块,后来加以修改。后来我们这块的业务逐渐成熟,和之前业务的相关团队分道扬镳,代码和人员都慢慢独立出来。其中有个模块的代码是直接拿过来的,单独存了一份代码(也就是一份旧版代码),比较基础的模块,没有过多业务上的逻辑,我们只修改配置,并不迭代代码。但这个模块在之前的团队中后来又迭代许久,功能十分丰富。彼时他们的代码我自然没有权限访问,没有办法克隆。我曾和他们询问该模块最新代码,但是他们敝帚自珍,把我拒绝。

这时候我想起了这个自动编译系统。它是允许每个指定自己指定编译脚本的,且是用高权限的账户来下载代码的。这可就有意思了,这是妥妥的“OS”注入漏洞啊,当然这是对内系统,外部是无法访问的。**请注意这不代表整个腾讯,只是我们部门用的自动编译平台当年有此BUG。**我呢,自然也不会用它来搞破坏,我只是一心为了工作。

我找了一个我自己的模块,在里面放进去了一个脚本。然后在自动编译网站上设置编译命令为执行这个脚本。这个脚本除了正常的编译流程以外,还有一部分是调用svn命令(当时还未迁移git)去克隆那个我没有权限的模块及其依赖的代码,然后将源码复制某个目录中。后面被自动编译机打包,生成FTP路径。

而我通过FTP下载zip包,最终剥离出了那个模块的源码……

后来我虽然看过他们代码,不过最终没有给我们业务使用,想来想去,毕竟也名不正言不顺,权当学习了。一年以后,我们团队和他们达成了合作,他们给出了源码,并提供了技术支持,指导我们如何配置使用最新版的模块。其实我早已可以随时查看他们的代码了,在后来的工作中我再也没有遇到过这种漏洞。这权当是一则趣事。

百度篇

在百度工作期间,我所在的部门是对外号称“凤巢”的商业化广告部门。众所周知,在商业化部门,做数据分析是必不可少的工作,即使我是C++后台开发也不例外,因为我们日常上线的feature都关乎着CTR、CPM等广告指标。

对于日常的数据分析,内部有一个在线的数据分析网站。这个网站的后端存储是基于Spark的。我们可以在这个网站上输入SQL,来查询数据。还可以导出成文件,或者转储到HDFS。当然这里SQL是Hive/Spark的SQL,尽管很像关系型数据库的SQL但是支持的语法会更多样些。

在大数据领域、BI领域常用的DB都是列式存储而不是MySQL那种行式存储的。为避免一次查询的数据量过大,所以这个网站也有同时查询的任务个数的限制(多了会排一会队)和单次最多只能查询三天数据的限制。

题外话:如果是行式存储要避免查询查询过多,一般是限制返回的行数,也就是让你自己写limit,offset。

当然有其他途径可以跑MR任务在Hadoop集群一次性导出更长时间范围的数据,但是集群太慢,排队太多,跑个小任务完全没必要。所以一般可以在这个在线分析网站上查询,但是蛋疼的是,只能三天三天的查,如果要查询一个月的数据,只能分成10次查询,每次修改WHERE条件后面的起止时间,然后导出了10次查询的结果。

某日在组内工作群里,一个同事被安排查一两个月的数据,他说很不好办。靠人工三天三天地查询,经常这样太费时间。我多嘴说了一句,不用人工,其实可以写程序做到自动化查询。然后被问:“怎么实现?你帮忙写一个?”

得……祸从口出。大话已出,搞吧。

我为什么说有办法自动化呢?按理说我们可以和这个网站交互,输入SQL,点按钮查询,最后网页能自动把结果渲染出表格,应该都是有API的,内部网站反爬虫功能都不强。话不多说,给Chrome按F12吧,找找找,果然找到几个HTTP请求。

打开Postman做测试,发现是需要鉴权的,不然直接给发请求会报错。尝试是Chrome中把Cookie复制出来,塞到Postman的Header里,OK,成功拿到结果JSON。

在理论上验证确实可行之后,我开始改写成python脚本。首先将复制的Cookie写入到文件中,供脚本读取。第一版脚本功能如下:

  1. 读取文件中的SQL语句,其中WHERE的起止日期是变量(可以用python的字符串format功能,方便后续替换)。
  2. 脚本中指定好查询的时间范围。
  3. 脚本执行时会自动以每三天为一个单位生成一条有效的SQL,然后携带Cookie 给SQL查询的HTTP API发请求。
  4. 给获取查询结果的API轮询发起请求,等待查询结果ready。
  5. 结果ready,给HDFS转储的HTTP API发送请求转储到HDFS。

不多时,一两个月的数据就自动出现在HDFS上了。可以用hadoop fs -getmerge 自行下载下来。

这个脚本完成后就给了那位同事和他们的业务使用。

许久之后我曾用这个脚本帮经理(百度的经理就是其他公司的组长)回溯过整整半年的数据。

故事到这里就结束了么?没有,后来我还在一直改良我的这个脚本,但这和工作就无关了,完全是我在业余时间的玩味之作。

我把这个网站上能调用的HTTP API都在脚本里包装成了函数,可以很方便的当成RPC函数调用。

很快我遇到了Cookie的有效期问题,每次Cookie过期后,我就要重新从浏览器复制Cookie,也太不自动化了。Cookie的生成可以通过一个内网统一的登录页面来获取,登录方式有输入用户名密码、邮箱验证、短信验证三种。而且有一些反爬虫的限制,没办法通过抓包找HTTP API,脚本模拟发起请求的方式来获取Cookie。

这个问题曾困扰了我一段时间,后来终于想到了解决办法。那便是自动化测试工具Seleninum 以及“无头浏览器”PhantomJs。无头是我的说法……原是Headless,通常译作:无界面浏览器。就是可以在没有GUI的操作系统上运行的虚拟浏览器。虽然没有实际的界面,但是可以通过Seleninum脚本执行浏览器界面上的操作,来控制完成输入、点击按钮等事件。

于是我在我的Linux开发机上,启动了一个脚本,并让其常驻后台。比如这般nohup python auto_login.py & 。这个脚本就是每隔一段时间打开PhantomJs浏览器打开登录的网址,自动完成用户名和密码的输入,点击登录按钮,而后获取Cookie,接着将Cookie写入到固定的文件中。这样我之前的脚本就能自动化使用了,而无需每次自己复制Cookie啦!

我是一个有强迫症的人,自动化登录获取Cookie的事情虽然做完了,但是有一点我不满意,那就是我的密码需要写到脚本里或者文件里。尽管可以对密码做一下加密,在脚本中做一下解密,但是对于能看到脚本源码的人来说这无疑形同虚设。即使你把密码设置成脚本的启动参数,别人通过ps aux命令也能看个一目了然。因为ps看到的进程信息是带着启动参数的。

我想了想可以在脚本中读取标准输入,在标准输入中键入的密码,后续他人无法看到。可是如果让脚本启动的时候读取标准输入,那么怎么变成后台常驻进程呢。我一般都是用nohup python auto_login.py & 将其丢到后台常驻的。nohup & 是没办法读取标准输入的啊。

这时候我想到了一道Linux C面试题:Linux如何生成守护进程?对,就是不用nohup,不用&,让程序自己变成后台(background)常驻的守护进程(daemon)。python也有和Linux C类似的API,我就用同样的思路实现了。

于是这个自动登录脚本的用法是这样:不加nohup &,直接执行python auto_login.py,出现一个用户名和密码的输入提示,输入之。然后脚本会自动变成一个daemon进程在后台常驻,根据一定的频率定时刷新Cookie。于是我可以在不泄露密码的情况下,完成这个常驻后台刷Cookie的进程啦!

这个自动登录的脚本,算是一个意外收获。百度内部系统用到都是同一套内部鉴权方案,所以后来我又陆续给其他的内部网站抓包分析,写了一些例行的自动化脚本,比如我们每次上线之前都要在一个平台填表达登记,上线完成又要在这个平台上进行确认,不然没办法发起第二天的上线。在有了自动登录脚本之后,这些都很容易实现了。它们的鉴权都是得益于这个自动登录的脚本获取的Cookie

各位看官看到这,可能发现无论是自动登录的脚本,还是自动做数据查询的脚本都基本成熟了。但这也还不是故事的结束,一段时间以后,我又做了一件有趣的事。

前面提到我将数据查询的脚本根据原网站上的各类功能包装成了不同的函数。我又做了一些完善,使之成为了一个很方便的第三方库。我将里面一些函数的返回类型替换成了pandas的DataFrame。于是乎,我可以方便地在Jupyter/JupyterLab里使用啦!

然后我可以在JupyterLab里import我自己的库,然后调用函数去发起SQL查询,然后直接将查询结果在JupyterLab渲染成表格(因为它是DataFrame类型)。也可以在Jupyter里面进行HDFS转储。另外呢,Jupyter可以执行shell命令,我可以wget下载一些其他的文件,比如某些数据的词典映射文件,从而可以将数据中的枚举值换成字符串名称。接着我可以继续数据分析,并且用matplotlib直接渲染成图表。

这些其实都不是我的本职工作,并不是被安排的任务,纯属我的个人兴趣和个人探索。虽然后来我把最终版向几个同事安利过,但是他们也并不感冒,我的第一版他们后来微调了下已然够用。

不过我倒也并不失望,我在不停打磨自己脚本的过程中,克服一个又一个的困难,实现一个又一个的功能,看着它日臻完美,已然心满意足。而最终有没有人使用它,则只是结果,这个过程已然足够有趣。感觉自己就像乘兴而来,兴尽而去的古人王子猷。

这是两则趣事,谢谢观赏。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存