邮件服务器-POP3服务器邮件索引/UIDL简单、高效的缓存方案

对于POP3服务器来说,最重要的衡量标准就是单台服务器所能承担的用户服务,这其中会涉及用户并发数、网络带宽流量等因素影响。

POP3服务目前在国内的企业邮箱应用里面是一个比较普及的功能,毕竟outlook、foxmail客户端做的确实很不错,而且这么多年的用户习惯,还是习惯把邮件拉到自己的本地办公电脑上来操作和查找,当然,为了在多地点的访问,用户又习惯在服务器保留副本,至少在出差或是家里还是可以通过webmail来处理一下邮件,毕竟不能总是背着办公电脑跑,其实这就带来一个问题,用户的 UIDL 列表会很多,在10W这级别算是正常(关于降低 UIDL 列表,也有两种方案,以后再介绍吧),那么如何高效的为用户送 UIDL 列表就成为POP3服务器的一个性能指标。

其实高性能网络服务器的几个改进重点,无外乎几种手段,采用异步模型,增加用户并发访问能力,增加缓存(内存、SSD、磁盘),提高数据的快速获取和响应,对于用户认证这块,估计现在大家基本都是 Memcached/Redis 了,那么对于POP3这种特殊服务,其实还有一个地方需要缓存,并且可以显著提高POP3的响应性能,那就是 STAT/UIDL 缓存。

现在市场的邮件服务器的索引实现基本上也就这几种方式,二进制结构化文件、bdb/sqlite/gdbm用户级数据库文件、MySQL/PostgreSQL几种方式吧,当然开源的Mailbox/Maildir还是原始的无索引方式,不过Dovecot已经自己增加了二进制结构文件缓存(这个以后研究一下),所以在将用户邮件索引加载为 STAT/UIDL 缓存时,可以采用有一个简单、高效的方式,为没一个用户建立一个 STAT/UIDL 缓存文件(当然是在 TMP/HASHDIR 下,这个就不介绍了),在文件内直接将 STAT和UIDL列表直接存储在里面,以供POP3会话使用,那么为什么采用这样的方式存储呢?现在主要说明一下,首先文件系统的实现是非常稳定和安全的,文件的定位访问也是非常快速的,而且一个用户一个缓存文件,就直接隔离了用户数据,然后最重要的就是,这种方式可以灵活的部署缓存方式,我们可以指定这个缓存以内存(/dev/shm)、SSD、磁盘来存储,采用每种方式,就完全取决于服务器的物理状况,这样调整起来非常容易和方便。

下面介绍一下这个缓存文件在POP3会话中的使用方式,在STAT状态时,进行 popcache.loads() 加载缓存,如果无缓存的话,就直接通过索引服务器接口直接获取状态信息(存在会话句柄中),继续进行 UIDL 状态(LIST/UIDL),此时也是直接通过索引服务器接口获取(存在会话句柄中),接下来用户会进行 RETR/DELE 等很多操作,这个过程都是和服务器的实际邮件文件操作,最后,用户会发送 QUIT 结束会话,此时将最终的状态信息和UIDL列表信息进行 popcache.dumps() 存储到缓存文件,这样用户在下一次(通常情况是30s、1分钟)的 CHECK 操作中,就不需要进行索引服务器信息加载,如果缓存文件在内存或SSD中,这个速度是相当快的。

目前可能还会有个疑问,这个缓存文件在什么时候删除呢?在用户索引条目变化的时候(也就是有新邮件,或是webmail/imap的删除、移动时),这时为了方便起见,直接采用暴力的方式 popcache.clean () 删除掉缓存文件,用户的再次进行POP3服务时会自动加载缓存了,同时也可以为缓存文件建立一个 TIMEOUT 机制,在读完缓存后检查一个更新时间,超过 TIMEOUT 也可以直接删除,这个就取决于用户对 POP3 实时响应时间的要求了(也可以两种方式结合),其中对于独立的POP3服务器来说,驱动 popcache.clean() 的最好方式就是扩展一个POP3的 X-CLEAN 命令了,不过这个命令肯定要进行一个 allow ip的鉴权或是防火墙保护。

这个算是一个初步方案,其实就是提高在有新邮件到来前,用户频繁连接服务器CHECK状态时的响应速度,并降低服务器的复杂和索引的频繁查询。毕竟普通用户不会每分钟都有新邮件,但是foxmail/outlook的定时、频繁pull操作,服务器的开销还是很大的。

嗯,差不多这样就可以了,畅想一下,用Libevent内嵌一个Python解释器实现一个POP3 Server会如何呢?权衡开发难度和服务器性能会不会好些呢?有机会就实现一个试试吧!

 

你可能感兴趣的:(邮件系统,Python编程,服务器,C编程)