data/threadcache 论坛页面缓存(针对游客的优化)
data/sendmail.lock 发送邮件锁。Discuz默认通过类似home.php?mod=misc&ac=sendmail&rand=1379315574这个隐藏页面调用,由用户的浏览行为触发邮件发送流程(浏览器侧用一个300秒的cookie控制频率,服务器侧通过sendmail.lock文件的mtime控制频率5秒)。如果可以控制 服务器,应该优化掉这个机制。
data/update.lock 系统升级锁。执行版本升级程序(如x2升级到x3)时,会生成这个文件锁。
系统配置项缓存 表pre_common_syscache
我们假设部署两台web服务器的场景(且web服务器也是php应用服务器)。我们需要解决data目录共享的问题,引入NFS服务可以简单解决这个问题。服务器复用,此处不表。这里会有一个选择,哪些目录放置在NFS上,从上面的分析来看,将data目录放置在NFS上即可,即各web服务器均独立部署程序文件,将NFS挂载到data目录节点,缺点是需要将程序文件部署到每一台web服务器上,要解决程序文件更新部署的问题,优点是可以节省web服务器通过网络取NFS上的程序文件的开销。如果图方便,也可以把程序文件也放到NFS上,则所有文件都只有一个副本了,程序更新也很方便,缺点是会增加web服务器通过网络取程序文件的开销。这两者需要权衡,建议第一种。
上面的方案存在一些问题。当用户访问一个附件时,WEB服务器都需要通过网络从NFS上取文件,这给内网网络带来了压力和一些不必要的开销,这可以通过在web前端增加缓存机制来缓解(如squid,nginx的proxy cache等)。为静态资源配置单独的域名供访问也是值得实施的工作,Discuz可以很简单的做到这一点,通过配置“本地附件URL地址”项就可以实现附件类(data/attachment目录里的)文件URL重构,但后台发布的广告不行,有BUG(在X3版本测试)。
在使用文件锁,且依赖于文件的mtime等时间值执行逻辑时,请务必保证服务器时钟的一致性。
上面的方案简单,且对Discuz的改造很小,维护成本低,适合单台服务器向多台服务器(数量较少)扩展时选择。随着访问量和web节点的增加,内网流量,NFS,MySQL均需要进行扩展。MySQL的扩展有较成熟的方案,如主重复制机制。NFS这个稍稍麻烦一点,且很多人诟病NFS的文件共享机制不安全;论坛附件以较小尺寸(几百字节不等)的文件居多,而linux的ext文件系统的块大小一般是4K,从而浪费了存储空间,对inode的利用率也不好;从长远来看,NFS终将成为系统的瓶颈,我们有必要重新规划文件共享/同步机制。
目前很多公司都有解决大量小文件存储的方案,如国内某大互联网公司使用基本MangoDB的GridFS等。实现的细节不在讨论范围,其基本思想就是构建文件存储服务,把附件类静态文件存储到远端(远端系统返回一个URL供访问),并且由这个远端系统处理用户访问的各种优化等。我们讨论如果已经有了这样的一个服务,Discuz接入到这样的一个服务需要注意些什么。
帖子附件的上传和帖子的发布是异步的。附件上传后的实体文件会被存储到类似data/attachment/forum/201307/20/路径,同时会在附件表中(pre_forum_attachment_unused)添加相应记录,发布帖子时,这些记录被散列分布到相应的附件表(pre_corum_attachment_[0-9])中,并标识其所属的pid, tid。这是附件本地存储的流程。
Discuz支持“远程附件”功能(全局-上传设置-远程附件)。“远程附件”功能支持将附件通过FTP的方式存储到远端系统,如果网站当前没有文件存储服务,但又想将文件存储分离,使用这个内建功能也是一种不错的选择,毕竟FTP很好维护,也不需要对Discuz进行改造。虽然Discuz默认只支持FTP方式,但远端存储在功能接口层面基本是共同的概念,添加、删除之类。所以当要把“远程附件”扩展到自有的远端文件存储服务时,一个比较好的实践是继承Discuz的ftp类,用远端文件存储的功能重写Discuz的ftp类定义的各方法,然后在ftp类实例化的地方,调用这个新的子类;如果不打算保留默认的FTP机制,甚至可以直接修改Discuz的ftp类实现,这样连ftp类实例化的地方也不用修改了。这样的处理对Discuz的改造最小,细节都隐藏在了ftp类的实现中,遵守与ftp相同的行为模式。
远程附件将大部分的静态资源流量分离,我们可以分别的优化两个系统。但Discuz的远程附件的行为模式仍需要我们注意,我们必须清楚它是怎样运行的,是否有某种陷阱,以便于系统的某些功能行为表现异常时,心里有底。
从上面关于“远程附件”的讨论我们看到,即使启用了“远程附件”机制,但Discuz仍然会在众多功能上用到data目录下的多个子目录,且有的子目录还必须在多个web服务器间共享(如data/attachment)。所以如果不对这些功能点进行改造,我们仍然需要NFS这个设备,毕竟远程附件已经将大部分的流量分走了,NFS是保证业务正常运行的最简单的办法,谁知道还有多少其它功能会依赖于此呢?
2)仅将需持久化的数据同步到从库。
如pre_common_session、pre_common_admincp_session, pre_forum_threadaddviews这类数据是不应该同步到从库的,它们更新非常频繁,且都是临时性的,它们应该被配置为忽略同步的表(replicate_wild_ignore_table选项),或者更好的办法是通过其它机制处理(如memcache、redis等)来实现,从而彻底从数据库中分离。从库同步主库的写操作时,同样会使用写锁,而这些性能开销是不必要的、应该优化,以使从库最大限度的服务于核心内容的读取查询。
其它一些多web部署时要注意的问题
改造内置计划任务。
默认Discuz内置的计划任务是通过用户浏览行为触发的,如果能控制 服务器,这应该改成用操作系统的计划任务驱动,Discuz!提供了api.php?mod=cron,稍作改造即可。仅在某一台WEB服务器上部署,并且将Discuz的每个任务单独部署成操作系统计划任务的一项,有一种选择是只部署一个“每分钟”周期的计划任务,然后由这个任务每分钟的轮询操作来驱动Discuz内置的计划任务机制,不建议这种做法,计划任务的数量毕竟是很有限的、执行的频率也是有计划的。
建议使用文件缓存这部分数据,缓存文件跟着程序文件一起部署,每套程序文件都有一套配置项的缓存文件的副本,从而完全优化掉了这部分开销。优点:将配置项的缓存写入文件是Discuz内置的机制,无需改造;磁盘文件IO稳定性是最好的,成本是最低的,避免中心节点故障带来的风险;上线时间点所有配置项缓存已经生成,客观上达到了“暖缓存”效果。缺点:在系统上线部署之前,需要生成全部的配置项缓存文件,Discuz默认的生成缓存文件的策略(Discuz默认的策略是“找不到缓存再生成”,对于短时间并发较高的系统,这种策略往往会造成多个处理进程同时触发写缓存的情况)不能满足这个需求,这需要一些开发量。
在具体实施时,有一些建议。生成的缓存文件同程序源代码一样纳入版本控制,以便于跟踪配置变化。而通过后台UI操作,存储在数据库中的配置数据则没有这么方便。配置项缓存的生成比较简单,按照pre_common_syscache表的记录生成即可。
同理,模板缓存文件也可以在上线部署前完成生成,只是模板缓存文件初始化会麻烦一些,建议收集最常用页面的入口,建立脚本来触发。
如果要覆盖Discuz默认的配置项的值,建议启用一个配置文件,用新值覆盖旧值,尽量避免管理后台UI操作,特别是线上环境,因为配置参数最终需要与配置项缓存文件同步才能起作用。