Mudkip Mud Sport

Mudkip's Mud Sport Journal

0%

MediaWiki 最初为运行维基百科而设计的开源程序,目前服务于超过一万的 Wiki 站点。神奇宝贝百科自2010年9月起运行于 MediaWiki,3年多来积累了很多心得,虽然内容和访问量迅速增长,但服务器开支依然保持在负担得起的范围。水跃希望在这里记录和分享。

日志标题中「用得起」的假设是运行在一台 1GB RAM 虚拟(云)服务器,日页面访问量在一百万以内。市场上 1GB RAM 的 VPS 价格约 10 ~ 30 美元每月。MediaWiki 并不适合运行在共享虚拟主机,或内存不到 512MB 的服务器中。

另外,MediaWiki 官方网站提供了一些基本的优化方式,非常值得参考。

MediaWiki 很耗资源么

是的。相较其他流行的 PHP 开源应用,MediaWiki 可以认为更耗资源,尤其是 CPU 使用,一篇包含复杂模板嵌套的文章,在无缓存状态下的渲染时间可能长达数秒,甚至数十秒。

经历了十多年的发展,MediaWiki 也是一个架构设计优雅的工程,具有非常强大的功能和可定制性,其性能开销往往是必要的。合理运用多重优化手段方式,可以有效降低 MediaWiki 的性能开销。

使用高性能的 Web 服务器程序

如果在生产环境使用最新版本的服务器程序,往往会引起很大的争议。但事实上,开源程序往往可以保持对较新版本系统环境很好的兼容性,新版本的系统环境往往具有更少的 Bug 和非常大的性能提升。

52Poké 所在的虚拟服务器采取了较为激进的做法,自2011年后一直使用 Arch Linux 操作系统,其滚动升级的特性可以让包管理(pacman)安装的应用都保持在最新版本,同时 Linode 也默认提供了最新版本的 Linux 内核。虽然对基础服务大版本的升级危险较大,但两年多来数十次 pacman -Syu 并未带来很大麻烦,除了在某几个凌晨造成数十分钟的不可用(都是由于较长时间未升级造成跨度较大并且操作失误)。

回到主题,对 MediaWiki 而言,高性能的 Web 服务器程序除了意味着 PHP 5.5,MySQL/MariaDB 5.5+;也包括使用 PHP 5.5 内置的 OPcache 加速,以及使用 Nginx + php-fpm 代替 Apache,Memcached 和/或 Redis 缓存等。

当前支持神奇宝贝百科的服务器程序为 PHP 5.5.7,MariaDB(MySQL 对社区友好的一个变体)5.5.34,Nginx 1.4.4 以及 Memcached 1.4.17。

合理的配置文件

MediaWiki 的默认配置(LocalSettings.php)并不适合生产环境,这里简要介绍一个低能耗的 MediaWiki 系统需要增加的配置项:

  • $wgDisableCounters = true; 关闭内置的统计功能,如需要统计访问量可以使用 Google Analytics 等外部服务。
  • $wgWellFormedXml = false; 以及 $wgHtml5 = true; 打开 HTML5 标签输出并关闭兼容 XML 语法,可以降低页面的体积。
  • $wgShowIPinHeader = false; 关闭未登录用户在右上角显示 IP 地址,以便使用静态缓存。
  • $wgUseGzip = true; 开启 gzip 压缩,降低页面体积,另外也可在 Web 服务器中配置开启 gzip。
  • $wgMiserMode = true; 关闭实时生成特殊页面;建议同时在 cron 里配置定时任务,定期生成特殊页面。
  • $wgJobRunRate = 0.01; 减少执行任务的频率(即每次请求有1%的几率运行任务);MediaWiki 的任务往往需要耗费数秒的时间执行,默认运行频率为1,一旦插入任务就可能导致 PHP 进程全部被占满;这里同样建议在 cron 里配置定时任务运行 MediaWiki 的任务,以免使任务队列积累过多。
  • $wgInvalidateCacheOnLocalSettingsChange = false; 关闭修改 LocalSettings.php 使所有缓存失效的特性;MediaWiki 的缓存完全失效是非常可怕的,几乎可以迅速致服务器僵死。

服务器端缓存

MediaWiki 的稳定运行,opcode 缓存、object 缓存和页面缓存都是必不可少的。

opcode 缓存

opcode 缓存用于加速 PHP 脚本的执行。PHP 5.5 内置了 OPcache(即之前的 ZendOptimizer+),比起 eAccelerator、XCache 和 APC 有更好的性能,只需在 php.ini 中引用 opcache.so 即可使用,同时 OPcache 也可单独安装在 PHP 5.4 或 5.3 环境中。另外需要注意 PHP 5.5.1 及 5.5.2 内置的 OPcache 和 MediaWiki 存在兼容问题,5.5.3 以上版本已解决。

object 缓存

MediaWiki 可以利用 object 缓存存储包括界面语言文字、文件信息、模板生成的中间产物、最终页面渲染 HTML 等数据。MediaWiki 支持使用 APC、XCache、Memcached、Redis、MySQL 数据库方式缓存数据,但配置中默认未开启 object 缓存功能。在这几种缓存方式中,除 MySQL 数据库外都需要在内存中存储,其中前3种存储方式无法持久化。

这里推荐使用是 Memcached 和 MySQL 结合的方式,配置方式如下:

$wgMainCacheType = CACHE_MEMCACHED;   
$wgMemCachedServers = array('127.0.0.1:11211');  
$wgParserCacheType = CACHE_DB;

Memcached 由于将数据存储在内存中,读写速度非常快;存储相同内容的数据占据的内存空间也是几种方式中最小。但 Parser Cache 由于包含了页面完整的 HTML 文本,需要的体积非常大,同时一旦失效则会需要重新解析所有页面,带来巨大的 CPU 开销;在内存有限的条件下更适合存储到较为持久的 MySQL 数据库中。

神奇宝贝百科在2013年初使用 Memcached 曾频繁遇到 Segfault,因此更换到 Redis + MySQL 结合的方式,但 Redis 耗费了更多内存(仅指在 MediaWiki 的使用方式下),也有相对较大的 I/O 使用。2013年12月 Memcached 发布了 1.4.17 更新,解决了不稳定的问题,因此目前又换回使用 Memcached。

页面缓存

作为一个 Wiki,对未登录用户而言页面几乎是静态的,所以页面缓存可以非常有效;MediaWiki 提供了 File cache 功能,另外也可以使用 SquidVarnish 的功能。而对于只有一台服务器的站点而言,这里更推荐使用 Nginx 自带的 FastCGI 缓存功能。

MediaWiki 默认会根据请求头中的 Accept-Language(即用户浏览器语言)、Cookie 等因素区分不同的页面缓存(可见响应头中的 Vary 行)。但对于单一语言、没有文字变种的 Wiki,只要已登录用户不使用页面缓存,未登录用户访问则看到相同的页面内容即可。

以下是神奇宝贝百科的 FastCGI 缓存配置,为便于介绍去掉了中文简繁处理相关的内容。这里缓存有效期为5小时,可以根据实际情况修改。另外,特殊页面不宜开启静态缓存(如最近更改和随机页面)。

http {
    ...
    fastcgi_cache_path  /var/cache/nginx/wiki levels=2:2 keys_zone=wiki:128m inactive=5h max_size=2048m;
    
    server {
        ...
        if ($http_cookie ~* "52poke_wikiUserID") {
            set $do_not_cache 1;
        }
        if ($uri ~ "^/wiki/Special:") {
            set $do_not_cache 1;
        }
        if ($args ~ "Special:") {
            set $do_not_cache 1;
        }
        fastcgi_cache wiki;
        fastcgi_ignore_headers Cache-Control Expires;
        fastcgi_hide_header Vary;
        fastcgi_cache_key $uri$is_args$args;
        fastcgi_cache_bypass $do_not_cache;
        fastcgi_no_cache $do_not_cache;
        fastcgi_cache_valid  200 5h;
        fastcgi_cache_valid  301 0;
        fastcgi_cache_valid  404 20m;
    }
}

使用页面缓存就会遇到缓存更新的问题,好在 MediaWiki 提供了 PURGE 功能,配置后只要相应的页面有更新就会发出 HTTP PURGE 请求。LocalSettings.php 中的配置如下:

$wgUseSquid = true;
$wgSquidServers = array('127.0.0.1’);

这里只配置了 127.0.0.1 因 52Poké 只有一台前端服务器,并且和 MediaWiki 程序是同一台。不过 Nginx 默认并不支持 PURGE 请求,可以编译安装 ngx_cache_purge 扩展实现,具体操作步骤可以参考这里

对于具有文字变种,如中文简繁之分的 Wiki,则情况较为复杂,今后这里会详细讨论 MediaWiki 处理中文简繁的问题,本篇就不详述了。

减少模板和文章内容渲染的开销

MediaWiki 提供了非常强大的模板和语法处理功能,尤其是内置的 ParserFunctions 扩展,但语法解析需要非常大的 CPU 开销。

这里建议尽可能减少不必要的模板使用和嵌套。例如对于一些通用的文字、背景颜色、边框样式,更适合在 MediaWiki:Common.css 或小工具中使用 CSS 实现。如果文章在编辑后需要等待很长的时间才能加载出来,则需要对其中使用的模板进行优化,必要时可以考虑拆分文章。

MediaWiki 解析图片信息也需要较大的资源消耗,对于较为固定的图片、尤其是尺寸较小的图片,推荐使用 CSS Sprites 而非通过 Wiki 上传。

MediaWiki 也会对包含过多模板嵌套、或者使用了过多语法呼叫(例如 switch 和 if 语法)的文章自动添加警示的分类。

选择合适的服务商

基础硬件的性能往往是第一位的,对运行 MediaWiki 而言,CPU、内存、I/O 性能、网络带宽都至关重要。其实早在2006年 52Poké 就搭建了 MediaWiki,但在有上百篇文章之后当时所在的虚拟主机就不堪重负,不得不在后来转型使用自建的程序,直到2010年开始使用 Linode。

就 52Poké 使用 Linode(推介链接) 的经历而言,Linode 是一家不错的 VPS 提供商,但并不算完美。Linode 在2013年进行了多次升级,在流量、网络速度、CPU 性能和性价比方面有较大优势。

VPS 有一个不稳定因素是 CPU 和 I/O 性能会受同一台物理服务器的邻居影响,52Poké 曾遇到过 CPU %Steal 过高导致性能较差的问题,不过联系客服很快迁移了另一台物理服务器解决。Linode 的 I/O 性能比提供了 SSD 的服务商要差,目前 Linode 只在纽瓦克数据中心提供了测试版 SSD 服务,亚洲用户较多使用的东京数据中心则尚未提供。

其他和未来

到这里已经介绍了高性能 MediaWiki 站点的必要条件。不过对于拥有较多图片的 Wiki 来说,可能很容易遇到流量超限或 I/O 消耗较大的问题,推荐如有必要添加一台流量较为廉价的 VPS 存放或缓存图片;另外可以考虑将图片延迟加载,即当浏览者滚动到图片位置时再加载,该功能的 MediaWiki 扩展在完成开发后会开源。

关于 MediaWiki,除了性能之外,还有很多值得讨论的话题。比如扩展,小工具(Gadget),中文的繁简问题,条目命名和内容规范,自动化批量建设内容,版权问题,以及作为一个 Wiki 社群的建设和发展。这些就留在以后继续讨论吧。

对于未来,如何支持移动设备,如何进一步减少服务器端模板的开销是水跃在继续思考的问题。MediaWiki 官方提供了 MobileFrontend 扩展,但根据设备输出不同的内容会带来更多的复杂度和资源消耗,而且可能降低了移动设备的体验;所以我更希望有响应式的方案。对于复杂的表格如果可以服务器只在页面中输出元数据,由 JavaScript 在浏览器中渲染的话,就可以减轻服务器的压力。但修改 Wiki 的 JavaScript 和 CSS 的权限往往不会开放给普通用户,这样做对社区并不利。

2014年的第一篇终于啰嗦完了,这里祝各位新年快乐,也希望 MediaWiki 的同好有所收获吧: )

已经完全被XY惊呆了。
片尾一刻的歌曲意外被震撼到了,曲子好美好沉醉,歌词华丽地无法形容。虽然只顾着急忙录歌词都来不及看职员表。

You and I were born right here in the same world.
For this one brief life, we’re beneath the same sky.
The great flow of time. The wide expanse of space.
We are lucky enough to share this lifetime we get.
We can gain more if we give. By taking, we only lose.
Let us make this a new age where we show our gratitude.
There’s a fragile bud of hope, blooming in each of our hearts.
Don’t you take that away. Our dreams are meant to be shared.
Let it grow. Let it live. Let us see what it will bring.
When we share in our love, we make a beautiful world.
Search it out, and find the way: the point where we can all meet.
The point where we’re all the same. There it lies: the future we seek.
Start from there, and then we’ll forge a world where all can be free.
Free to dream, and free to smile. Free to be who we will be.
Let’s make sure we create…
A world of our hopes and dreams.
In our brief lives,
we’ve managed to meet.
Treasure this gift…
This precious time that we have.
In our brief lives,
we’ve managed to meet.
Treasure this gift…
This precious time that we have.
KISEKI

XY发售前夕所有空余时间都投入了神奇宝贝乐园V6,几乎没有看到任何偷跑和剧透,所以在这35小时5分钟的旅行中总是惊喜不断。看到不认识的神奇宝贝,看到各种惊艳的景色,发现某道馆训练家竟然是谁,冠军竟然是谁,旅店里不同地方的人提到自己家乡的事情,以及对战的时候冲浪把身后的柱子冲倒这样的细节。

为了挽回重要的神奇宝贝而不惜一切代价,不仅生灵涂炭,也让自己和所珍爱的神奇宝贝备受痛苦。AZ让我想起了特别篇里的柳伯。

因追求完美的世界走上歧路的人,强蜀不是第一个也不是最后一个。这个世界纵然有很多不完美,甚至不平和不公,但有理想的生命要做的不是要创造一个所谓的新世界,而是通过自身的努力和一点点感染周围的每个生命,让这个已经很美的世界变得更好。「所有的生命和其他的生命相遇,一定会创造出什么」。或许,赤日强蜀他们,在追求自己理想的过程中,早已不知不觉被私欲吞噬了。

一度觉得博士是爆炎队地下队员,还好大概只是交友不慎,不过直到看到他给未来自己的信才相信他是个好人。
「给正在读这封信的人:你现在怎么样呢?你是否成为了曾经想要成为的人呢?在一开始,你想要成为的人是什么样子的呢?如果能全力以赴地度过每一天,大概会是很棒的事情。——从幻想着未来的法桐,给未来的法桐」

剧情中无法接受的是按下按钮的时刻,虽然做好了随时S/L的准备,虽然选择了无效的按钮,但那一刻真的好痛苦。

好喜欢莎娜,旅行中不仅有和她一起看烟花一起看米亚雷市的光棱塔点亮的美好回忆,更被她一直以来的坚强乐观、勇敢和好奇心所感动。只是在14号道路的时候,看到她认为大家比她更努力优秀怀疑自己的时候,好想重复博士告诉我们的话「我们并不需要为互相比较而烦恼,每个生命都是独一无二的」,而且从许多方面,莎娜是我们几个中最优秀的一员。

然后瑟蕾娜可以说是最好的劲敌、邻居和好友,比起历代的劲敌更能看到「自己」的影子。在粉碎爆炎队的阴谋中作为搭档,在冠军之路对战的时候她说「我们有太多相似东西,所以我把你当作劲敌,所以我不想输给你,因为你是我的劲敌,所以我希望你成为更强大的神奇宝贝训练家,我们就会一起变强」。好期待未来动画中的表现呢。

还想提一下XY延续了B2W2中「接纳多种多样的想法」,博士说「在完成图鉴的过程中,你们会遇到有许多种生活方式的神奇宝贝和许多种想法的人。首先要接纳和你们自己不同的生活方式和想法,然后认真思考什么是对自己最重要的」,瑟蕾娜说「我们和强蜀的立场不同,也不能完全说谁对谁错」。一起开始旅行的大家,各自走上了不同的轨迹,但又站在米亚雷市同一个盛大舞台上。

总之XY之中,神奇宝贝世界更加「真实」了呢。

The Internet has became one of the essential parts of my life since late last century. I still remember many things in the early days of my Internet life, when I was still a elementary or junior middle school child.

There are two things essentially formed my worldview, letting me know what this world is really like, whether I was told or learned from textbooks is true or false.

The first one is when I was in an Internet cafe in March 2000. That day many people there kept refreshing the Traditional Chinese version of Yahoo news, because there was a major election across the strait. I was interested and also enlightened.

The second one is, reading an article on Wikipedia. Wikipedia was still young and incomplete at that time, but that article was quite detailed and convictive. I was shocked.

As the years went by, I still remember.

经过了45个小时,美版神奇宝贝白版2终于进入殿堂,完成一周目。这次旅行留下的回忆非常非常多。

青海波道馆训练家水葱说,他不会凭着大家认为等离子队是错的,就认为等离子队是错的,但他会帮助阿相找回扒手猫,并且说用冰柱袭击双龙市是不对的。不会人云亦云是一种难得的品质,尤其是当两年前的等离子队凭着所谓正义的演说俘获许许多多人心的时候,坚持自己的想法而不被大众左右就非常重要。而对道馆训练家来说,这份责任所守护的东西,正是可以拒绝履行这份责任的权利。

黑白2是一个完美的故事,但没有黑白2的黑白是不完整的。两年后的N,从和雷希拉姆的交流中懂得了一切,明白了人类和神奇宝贝不可分割,明白了真实和理想可以融合,明白了神奇宝贝对战的真谛,明白了他要如何守护这个世界。从幼年接受被误导的教育中清醒过来,即使是曾经登上反派组织首领的人也只需要两年。

无比憎恨蔚欧,甚过傀奇士。憎恨他说的每一句话。作为一个走卒,他把这个故事中所有的恶都发挥到了极点。在他说出「Compare those feelings against the majesty of this ship!」的时候,我想起了在这个世界说出「大象走路,它能顾得了蚂蚁螳螂」的无耻之徒。

工作与兴趣的平衡,或者说多种兴趣之间的平衡。有些人可以处理的很好,比如霍米加、小菊儿、亚堤;有些人会选择更重要的东西,比如哈奇库;也有些人无法做到平衡,比如霍米加的爸爸。我想这不是一个需要「平衡」的问题,而是可以建立一种彼此依赖的关系,就像真实与理想一样。

海野隆雄说,为神奇宝贝世界创造一个可信赖的真实非常重要。在黑白2中,更能感觉得到这种真实,每个人都有自己的不同的生活,每位训练家都有自己的风格和策略。村庄桥的歌者,笼目镇的传说,背后都有一段不为人知的过去。而当完成一段漫长的旅行后,回到桧扇市瞭望台上,看着云层浮动,繁星闪烁,轻拂的微风切身感觉的到。

在合众地方,以至整个神奇宝贝世界,存在着各种各样的人、各种各样的神奇宝贝,各种各样的想法、个性和价值观。合众为一并不是要把所有想法归一,而是在不对其他生命造成伤害的前提下,接纳他们全部。对不同价值和想法的包容,以及对个体生命和情感的尊重,才是合众的「一种」。

其实何止神奇宝贝世界。在走向文明的数百年后,我们这个世界依然有无谓的纷争,9月里愚昧和疯狂席卷了从东亚到北非的许多城市。半月前看到PETA亵渎自己重要的东西的时候,也曾经有过愤怒,后来明白了那只不过是一种不同的想法。合众地方的「一种」,在所有世界、所有地方、所有时代,都是普世的。

It’s not necessary… Separating black from white and humans from Pokémon! If you think in terms of each individual life, this world was in a state that couldn’t divided any further. Possibilities are born out of combining and fusing these different lives! There are some things we can understand only by doing this. It will give form to unseen things. These formulae will restructure the world and make it richer! From their Poké Balls, I can hear the many different feelings Pokémon have about their Trainers. More than anything, I can hear their joy that they met people who need them!