Qiniu Manager 开发记录
GitHub项目地址:https://github.com/hellflame/qiniu_manager
具体使用方法:参考
由于在GitHub上已经写的很清楚了,所以对于项目细节就不再赘述了,以下只是作为记录。
首先,整个事情还要从貌似大二之前的暑期结束之后的那个学期开始(嗯,应该就是大二的第一学期)。
在那个暑假,青传的服务器貌似都换成了Linux(一台CentOS、一台Ubuntu),虽然都是老机器,而且重要的是那时候开始,青传的所有网站都基本上终于开始了重写,然而两台服务器貌似都运行了6年还是10年(具体时间已经忘了=。=),硬盘也没有更换记录,之前的Windows系统已经崩溃过好几次了,每次都是重启之后就完事了。在同一个机房里,还运行着一台DNS服务器,windows,崩溃的几率比另外两台web服务器要小一点,当然这也十分说的通,毕竟web服务器每天要接收无数的设备访问,这台DNS服务器只需要提供整栋楼的服务就够了。总而言之,web服务器的硬盘岌岌可危,而且也才经历过一次大的硬盘故障导致数据丢失,所以需要经常把网站的重要数据备份到远端,由于手上只有这两台服务器可用,而且都年事已高,所以只能考虑备份到远端了。
最终选择了使用七牛的云存储。因为当年一直在用Python,SDK也就选择了python版本,虽然官方文档的确挺差,但是基本的资源操作按照文档的指引,还是可以完成的,这也是第一个版本的qiniumanager。当年七牛还没有用户实名制,只要是注册的用户,都有免费的10G空间,10G下载流量,10w次请求。虽然服务器上跑着大概十几个项目,10G的存储空间还是够的,当然因为每个项目彼此都独立,所以脚本每小时执行一次,每次只备份1个网站,所以每个网站的备份周期是项目数量 x 1小时。
第一个版本在完成之后就直接去执行一个py
文件。好吧,当时觉得使用Pypi是一件很复杂的事情,当年见过的英文文档可能也比较少,觉得要发布到pypi巨难,于是在服务器上crontab定期执行py文件的方法在青传使用了很久,直到后来第一次把YoudaoDict
(有道词典)上传到Pypi,就觉得一切其实都很简单,于是准备好把qiniumanager
也上传到了Pypi上。
在发布到Pypi之后,也出现了很多问题。个人项目中的可执行脚本名字叫qiniu
,然而官方SDK也有一个可执行脚本,名字叫qiniupy
(时至今日,我依然不知道这个py能用来干什么)。这就导致在终端输入qini
然后使用tab之后,终端不能自然的空出一格,因为自动补全还找到了可选的qiniupy
,此时,我就萌生了想要自己写一个不依赖官方SDK的程序了=。=当然,要解决这个问题只需要删除qiniupy
这个文件就好了,虽然每次官方SDK更新都要删除一次就是了。
然而直到某一次更新官方SDK之后,在终端打印出了很多数组和消息,并且快速的挤满了scroll
作为一个终端用户,当然不能容忍自己的终端被一堆无用信息遮挡了视野,在给官方提了issue之后的确改过来了,不过这也更确定了要自己独立调用接口的想法了=。=
当然,重写整个SDK应该是在腾讯实习期间,当时手上的事情做的比较快,周末也基本上在公司。在腾讯期间被各种大神熏陶,发现他们查看问题基本上都深入了源代码,所以自己也应该在造轮子上面造的更精密一些,于是决定应该从HTTP报文开始造轮子。于是整个项目决定自己用TCP连接手动发送HTTP报文,这样的话,只需要保证与服务器接口对接就好了,并且还能精确的获得进度条(重点是进度条,但是官方在这块貌似做的并不好)。
手动处理HTTP报文,讲真的确挺烦的=。=或者说,手动处理任何网际间的通信都存在互相不知道要发送多少信息的问题。
首先,当然不能将整个HTTP报文都接收下来,因为,自己并不知道HTTP报文会有多大,如何设置socket要recv多少字节呢,当然,也不能全部接收下来,万一服务器给我传了一个100G的游戏呢=。=
各种协议都有自己结束的约定(最近刚刚知道的有smtp协议表示邮件内容结束的\r\n.\r\n
),HTTP请求也是如此。在发送请求中,GET请求是最简单的。
GET请求,请求的参数和数据都在url中(?xxx=yyy&zzz=aaa
),服务器只需要解析完URL就可以得到参数,其他信息在Headers
中可以获得,一条Header
以一个\r\n
结尾,最终以一行\r\n
作为整个请求的结束。
POST请求,并不妨碍利用GET的方法,将部分参数放在URL中,但POST请求允许将不方便放在URL中的数据放进body中。当然,一定要有一个Header,Content-Length
来指出这个body有多长,否则服务器会以Header结束后的\r\n
作为整个POST请求的结尾,默认Content-Length: 0。
在这些header的\r\n
之间,存在某种web攻击方式,具体细节就不再展开了。
发送这些请求的确挺简单,因为自己知道自己要发送多少东西,用固定的格式发送就好了,然而服务器肯定也会发送消息过来,来告诉客户端某些消息。问题就来了,客户端要准备接收多少字符串呢?
就个人而言,知道的有两种传输方式,比较复杂的一种是分块传输(chunked),另一种相对的就是整块传输(不知道有没有官方名字)了。
个人默认第一次recv,先接收1k数据,不排除某些请求会超过1k,比如有特别多的cookie,或者有特别多其他的Header。将这1k数据接收进一个StringIO
,之所以放进一个StringIO缓存的话,是因为它可以通过readline来判断是否是一个\r\n
,免去了自己写正则的麻烦,这样也方便将后续的内容拼接起来,一个get_value就好了。如果没有读到空白行,便继续接收1k,以此类推。可以判断的是只要读到空白行,Header部分就结束了,在里面一定有一个content-length,由此来判断接下来要读多少content。在最后一次读取header的recv中,从空白行开始,减去已经读到的content,content-length 减去这部分长度,就是要读取的整个HTTP响应的报文了。
对于chunked编码,在读取header部分和非chunked编码一样,但是对于实体内容,存在一些复杂的情况。尤其是在最后一次读取Header的时候,可能会出现读取的内容中有不完整的块、完整的一个块、多个块,还有多个块后结一个不完整的块(好吧,这么慢慢分析起来还是挺清晰的样子)。总之,由于在qiniu manager的开发过程中没有遇到过chunked编码,于是整个项目就完全没有考虑chunked编码了,直到什么时候qiniu准备升级了再说好了=。=毕竟,我还没有准备开发一个足以替代urllib或者requests的库。
以上,基本上就是整个项目的开发过程了。有更新之后再更细记录好了。
本文作者 : hellflame
原文链接 : https://hellflame.github.io/2017/05/14/qiniu-manager-dev-rec/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!