摘要:第一个 是从工作的角度考虑,原有工具核心功能也是下载邮件,不过是用PHP语言写的,每个账户fork出一个进程(子进程用于处理这个账户的所有下载任务),遇到两个核心问题,子进程很容易僵死,然后导致主进程也不能工作了。IMAP指令中最耗时的操作其实是fetch指令,有些邮件可能有附件,内容越大网络传输就越慢,所以主要的思路就是fetch指令操作放到一个进程池中运行,具体结构如下图:。

最近写了一个emaildownloader工具并放到GitHub上(github.com/ywdblog/emaildownloader),工具的功能很简单,就是从配置文件中读取多个电子邮件账户,然后下载邮件,按照邮件夹的位置,存储为eml文件。

为什么要写这个工具呢?主要有两个目的。

第一个 是从工作的角度考虑,原有工具核心功能也是下载邮件,不过是用PHP语言写的,每个账户fork出一个进程(子进程用于处理这个账户的所有下载任务),遇到两个核心问题,子进程很容易僵死,然后导致主进程也不能工作了;每个子进程都是顺序执行(一封封下载),由于邮件账户的服务商都是第三方的,很容易遇到block和timeout的问题,导致下载速度过慢且不可控。

第二个 目的很单纯,想学习新的知识,那是继续用PHP语言呢?还是其他的?但不管使用什么语言开发,核心目的是希望能够支持并行和并发,加快程序处理速度。

在PHP中,多进程和多线程的函数都是以 pcntl 和 thread 开头的,感觉就是对底层API的封装,是否有成功的案例?因为自己没弄过,也没发言权。

Go语言受人欢迎,很大原因归功于其支持并行和异步,但由于自己还是想在python上耕耘,决定使用python实践,python对并行和异步的支持也不错。

在更新这个GitHub仓库的同时(不完善,但可以督促我加快学习),我也会随时写一些文章,分享下python多进程和多线程编写的一些思路,希望对大家有一些帮助。

如何看待python多进程,多线程,异步编程?多进程和多线程是操作系统的核心功能,如果可能的话,尽量使用多线程,不过在CPython中,由于有GIL的限制,threading只能支持并发,不能运行在多核上;而multiprocessing虽然比较耗费资源,但支持并行,所以 目前版本 我使用multiprocessing方式编写这个工具。

multiprocessing和threading这二个库在使用上差不多,库接口尽量保持一致,multiprocessing看上去更容易编写,但其实进程之间共享对象是弱势(尤其是网络资源,后面我会写文章说明)。

在编写过程中,主要参考python官方手册,里面细节很多,有的时候理解起来也很费劲,所以学习过程不能着急。

imaplib,python标准IMAP库,这个工具主要使用IMAP协议下载邮件(亦可认为是一个IMAP客户端),这个库尽量引用原生的 IMAP 指令,目前我用到的指令很少,具体见下图:

list指令就是列出你的账户有多少个夹子(比如INBOX),在操作邮件之前先要用select指令选择某个夹子(默认返回这个夹子下的所有邮件ID),然后fetch指令根据邮件ID获取邮件内容。

其中search指令可以搜索特定条件(criteria)的邮件ID(比如特定日期),功能很强大,不过很多邮件服务商都屏蔽了很多criteria,所以emaildownloader工具没有使用该指令,等后续功能越来越多的时候,再研究更多的imap指令和参数。

主要设计思路?IMAP指令中最耗时的操作其实是fetch指令,有些邮件可能有附件,内容越大网络传输就越慢,所以主要的思路就是fetch指令操作放到一个进程池中运行,具体结构如下图:

可以看出,fetch这个最耗时的操作放在线程池中运行,而且互相隔离,整个椭圆是一个账户所有逻辑处理,比如有m个账户,那么就有m个椭圆逻辑处理,细心的同学发现了,只有处理完上一个账户,才能继续运行下一个账户的任务,对于不同账户来说,并没有并发运行,所以更好的思路其实如下图:

可以看出不同的账户可以并行运行,而且fetch子进程可以随机运行不同账户的任务,对每个账户来说,这种设计是合理的。

不过进程池的资源消耗是很大的,CPU核数也是有限的,所以进程越大,整个机器的负载就很大,多线程方式相对更合适这种场景。

这种思路更好,但我不打算实现,因为python多进程有一些问题(以后会说),最终我会采用多进程+多线程的方式。

相关文章