V2EX 从过去一年半中学到的几件事
2012 年三月末时,启动了一个计划,用一个月的时间,把 V2EX 从当时的服务商 Google App Engine 中迁出,因为当时 Google App Engine 的一些疯狂价格调整已经进行了半年。关于这次迁移,可以具体看 V2EX 2to3 这篇文章。
而现在已经过去一年半,V2EX 依然存在,并且各项数值也都有所发展。而在这一年半的运转过程中,我也得到了很多新的教训。我打算在这里稍微谈谈这些教训,虽然这些教训,或许对于那些已经参与过大型网站项目的人都没什么新鲜的,我只能希望这篇东西能够尽量有用吧。
首页时间轴
随着注册用户增多(虽然比起那些更大型的网站,我们的增长只是人家的一个零头),网站的首页时间轴就越来越成为一个瓶颈。在网站早期,因为注册用户们的志趣大多接近,所以首页时间轴大部分时候是小而美的。而发展至今,我们不得不把首页时间轴进行拆分,拆成了几大不同模块,默认呈现的是 技术 模块。而实际上大部分登录用户,去的是 全部 模块。
一个好的首页时间轴应该是怎样的?这是一个开放而无解的问题。
如果把一个以内容和交互为主的网站比作一本杂志的话,首页时间轴所呈现的内容应该基本体现的是杂志的主题。
我不太看好通过算法算出来的时间轴,因为在我们这个领域,内容的多样性恐怕永远都不会丰富到 Amazon 那样的程度。
而如果首页只呈现自己关注的人的内容,那么这条时间轴只会越来越无聊,内容的多样性也会越来越小。
或许,终极解决方案还是在于解答终极问题:这个网站到底能够带来什么价值?
我不希望 V2EX 成为无聊经济的一部分,我希望这个网站上的绝大部分信息能够有用。从专业领域来说,我希望 V2EX 上的信息会是关于软件和硬件的新进展。同时因为这里聚集了一群能够把事情搞定的人,所以围绕这个人群的信息服务,比如招聘和二手交易,也是有意义的。
MySQL or Redis
如果下次要开始一个像 V2EX 这样的网站项目,我会尽量从技术选择中把 MySQL 去掉。
作为一种存储数据的方式,MySQL 很成熟,其本身和大部分第三方库都已经存在了十年以上。尤其是在配合一些 ORM 使用时,MySQL 很简单。
但问题在于当数个表的数据量突破 100 万行,页面上同时需要读取的动态数据增多时,要将单个页面的生成性能优化到 100ms 以下就会变得越来越复杂。如果你有很多台双 8 核 CPU / 64G 内存的服务器,你或许会更晚遇到这些性能问题,但是随着网站规模变大,为了优化和 MySQL 有关的操作而付出的代价只会越来越大。
那如果有一天网站上有很多张表都有上亿行数据时,整个架构恐怕就需要重新思考。
Instagram 的技术团队在 Scaling Instagram 这个幻灯片中分享了很多有用的技巧,其中一条就是大量使用 Redis 来解决问题。Redis 并不只是一个简单的 KV 数据库,它的各种功能其实足够用于构建一个像 V2EX 这样具有大量动态数据交互的网站:
- 非常适合存储时间轴的 Sorted Set / List 类型
- 非常容易进行数据复制和读写分离
- 如果要上新的数据类型,不需要经历 ALTER TABLE 时纠结的锁表
- 网站部署新功能时,不需要考虑如何 migrate 数据库的表结构
- 和 Memcached 几乎一样好的性能,但是支持更多的数据类型,同时也没有 Memcached 的 1MB 单块数据容量限制
同时最关键的:性能。Redis 可以在单台服务器上实现每秒数万次的读取和写入,并且因为其非常容易实现复制和读写分离,所以如果要加入更多硬件,也会比较简单。
防攻击
动态网站的各种脆弱面,是很多教你如何编程的书中不会涉及的,这些编程类书籍中,通常只会教你一些最基本的 CRUD,及有限的性能优化。一些好的框架中会有一些这方面的考虑,但是如果你对各种攻击没有概念,那么你其实也很难把框架中的防攻击功能用好。
举个例子,如果登录页面上对尝试登录的次数没有限制的话,那么攻击者就可以不停地尝试,直到发现一个正确的密码。而如果用户用了弱密码,那么或许这个密码很快就可以被尝试出来。
而另外一个例子是,如果所有的 POST 请求,不去检测发起者究竟是真实用户还是机器,那么网站很可能瞬间就被各种用程序发出的修空调或是网络赚钱的垃圾给填满了。
又比如,如果一个新账号,在注册完毕后,马上就开始发布大量重复的内容,那么只会有一种可能——Spammer。
这种事情如果经常发生,那么用户就会对这个网站逐渐失去兴趣。
所以,一种最基础的保护方式就是,对于所有的涉及数据写入的页面,比如所有的 POST/PUT 请求,都应该有频次限制机制。如果是针对 IP 来做这样的限制的话,可以用 Redis 来实现计数。用 timestamp + IP + 页面的 URI 来作为 key,每有一次访问 value 就 +1,如果在特定的 timestamp 内访问的次数超过限制,就可以禁止这个 IP 在接下来一段时间的后续访问。限制访问的机制同样可以借助 Redis,比如在所有的请求的构造函数中,检查 deny:1.2.3.4 这个 key 是否存在,如果存在,就拒绝 1.2.3.4 的访问。因为 Redis 可以为每个 key 设置 TTL,所以如果你为 deny:1.2.3.4 设置了 3600 的 TTL,那么也就可以实现在 1 个小时内拒绝这个 IP 对于站点内任何动态页面的访问。
这样的保护方式也可以适用于 API 的频次限制,或是保护一些生成代价比较大的页面不被 flood。
上面讨论的是应用层面的攻击,而另外一类攻击就是协议层面的攻击,比如 UDP Flood 和 SYN Flood 之类。对于这类攻击,如果只靠调整 iptables 和内核的一些参数,基本上是无济于事的。应对这类攻击你需要的是机房的基础设施。如果机房本身有比如 Juniper 或是华为的防火墙设备,那么像 SYN Flood 这样的最基础的攻击方式,应该是机房层面就可以自动帮你过滤掉的。只是可惜的是,很多 VPS 和 Colo 提供商在机房里只放了一些最基础的网络设备,当这类攻击到来时,他们能做的就是停服务或者拔网线。
把网站放到 CDN 背后也是一个办法,这样当攻击来时,首先遇到的是 CDN 的服务器。不同的 CDN 厂商在防攻击这件事情上能做到的程度不同,如果预算充足的话可以考虑 Akamai 的 KONA,如果没有预算的话,就用 Incapsula 吧。
CloudFlare 也是一个经常被谈论起的方案,但是这个方案要求你将整个域名的管理,即 NS 也托管到他们。但是如果你需要的某种 DNS 功能 CloudFlare 不提供怎么办?这就是这个方案的纠结之处。
说到 DNS,一个确实值得推荐的方案就是 DynECT Managed DNS。Dyn 建了一个由全世界接近 20 个数据中心构成的 Anycast 网络,这样可以保证你的域名在全世界大部分地区的初次解析耗时都在 100ms 以下,甚至 40ms 以下。同时,如果有人想攻击你的 NS 的话,Dyn 的 NS 被打下的难度也是比较大的。目前 V2EX 在用就是 Dyn 的 NS。
虚拟化和自动化
自从发现了 Vagrant 这个工具的存在,我就几乎每天都在用。
简单来说,Vagrant 可以让你用一个文本文件 Vagrantfile 描述一个虚拟机环境,然后根据你的描述启动这样的一台虚拟机,同时将 Vagrantfile 所在的目录的所有内容,和虚拟机中的 /vagrant 这个目录自动实时同步。
这样的工具的最大好处,就是让你可以在 Mac OS X 上获得一个轻量级的 Linux 环境,你可以将这台 Linux 虚拟机配置得尽可能接近你的实际 production 环境。从而在每天的开发和测试中,尽量避免因为本地 OS X 和线上 Linux 环境不同而造成的 bug。
Vagrant 可以使用几种不同的技术来启动虚拟机,包括 VirtualBox,VMware,KVM,AWS 和 LXC。而其中 VirtualBox 是默认的方式,也是免费的方式。而如果要使用 VMware 来作为后端的话,除了需要购买 VMware Workstation 或 Fusion 外,还需要从 Vagrant 开发者那里购买一个售价为 $79 的闭源插件。我买过这个插件,但是在实际用起来的时候遇到过很多问题,比如在网络方面和 VirtualBox 就不太相同,因此 Vagrantfile 其实没法在两个插件间完全无问题的复用。这也是一直以来很多人在争论的一个问题:开源软件和闭源软件究竟哪个质量更好?而从 Vagrant 的这个例子来看,因为免费且开源,从而有更多用户的 VirtualBox 插件,在质量上,是大大胜过用户要少得多的 VMware 插件。
Vagrant 还支持的另外一个有用特性,就在虚拟机每次启动时执行一个 Puppet Manifest。这样你就可以在这个 Puppet Manifest 中进一步描述你需要的软件包 / 服务 / 目录 / 文件,从而省掉很多绝无必要的敲键盘时间。
我在过去一年的大量项目中使用了 Puppet。Puppet 是一个复杂的系统,其核心是一门描述机器状态的语言。你可以用这门语言描述你想要一台机器上应该有的软件包、服务、目录、文件和用户等资源的状态,然后 Puppet 在每次运行时,就会检查这台机器的实际状态和你的描述之间的区别,如果存在不同,Puppet 就会进行一些必要的更改,以保证机器状态符合你的描述。
Puppet 是一个得到了 Google,Cisco,VMware 等公司投资的创业项目,其商业模式就是提供 Enterprise 版本的付费支持,根据你部署机器数量,每台机器大概每年 $99 到 $159 之间。
和 Puppet 类似的项目还有 Chef 和 Salt,选择誰,不选择誰,很容易就变成没有结论的宗教问题。这个时候其实可以到 Google Trends 里对比一下搜索量,或是看看他们各自的 Customer 名单,然后你就会有结论。
本文讨论地址 › https://www.v2ex.com/t/82642