分享FarBox.com的技术构架

2013-01-17 18:55:08 +08:00
 FarBox
原文地址, http://blog.farbox.com/post/farbox-structure 格式看起来可能会好一些。


## WEB服务器

### 分布式

- 我们的数据库使用的是MongoDB,是单独的服务器,可集群;
- 文件存储使用的是Amazon S3。

如此一来,真正迎接WEB端的流量纯粹就是堆服务器了,并且可以将WEB服务器堆在地球上的任一个角落,只要能连上网即可。

一台Web服务器中,由是由以下的一些技术方案组成的:

- Nginx,直面访客,并且处理掉一些垃圾请求;
- Gunicorn + Gevent,跑WEB APP的服务器,基于Gevent,并发能力较强;
- memcached, 自动缓存一些渲染(计算)好的结果,可以有效避免重复的请求与计算。


> btw, MongoDB的数据,我们使用的是AWS的弹性存储(EBS),(同区)定期备份数据,还不定期的跨区备份数据。





### WEB框架

WEB框架,我们使用了Flask,对应的模板引擎是Jinja2。

其实,我们之前最熟悉的是Django。但它太厚重了,已无法满足我们的需求。

我们需要在一个既有的框架上,补充与修改不少东西。比如FarBox的模板呈现,真实的数据存在于MongoDB中,而实际运行中,则会编译后存于内存,整个loader的逻辑,是我们自己重写的。

像`flask.g` `flask.request`这些proxy性质的方式,也让我们能按照自己的逻辑,对一些函数进行分类、分离,并能最后按需汇总。

而Jinja2对比Django的模板语法,更加pythonic;重要的是,它提供的沙盒环境,可相对有效的保证用户代码是可信任的。

所有的这些,如果使用Django,估计想死的心都有了。当然,不能否认的一点,Flask开始的时候,要自己写不算少的大大小小的组件,也是有点痛苦;虽然可以用一些别人的扩展,但感觉会是overhead,干脆就自己重写。

btw, MongoDB的驱动,我们使用原生的pymongo,而不是一些(伪)ORM的包。但我们也尽可能把所有的query都集中在一个文件中处理,因为这也涉及到了MongoDB的索引,分离太开,后期的维护会很痛苦。如此一来,完全没有必要使用ORM,同时还能减少query过程中的overhead.


- - - - - - - - - -

## 第三方API同步服务器

FarBox还需要主动同步第三方(比如Dropbox)上的数据,如果是图片,还会做一些额外的处理,比如exif信息读取,图片效果的微量优化等。我们希望这个过程也是能分布式的,因为很有可能同步工作会超出一台服务器所能承受的极限。

这时候,想当然要用到队列服务了。但是这次,我们没有选择既有的工具,而是直接自己重写了一套为FarBox定制的。其实也很是简单,就是把所有的任务颗粒度降低到单文件,并且根据实际情况(比如以account为一个group、API请求限制等等)来并发处理,以及处理各个worker之间可能的锁定、释放。

做这样的决定,是因为我们依赖的技术方案中,有两个基础:`MongoDB`+`Gevent`。

如此一来,同步服务器也可以多台分布进行工作,因为数据库是唯一、集中的。

其实,目前还存在的问题是,当多个连接连到MongoDB的时候,一来可能存在一定程度的不同步;二来,如果没有对多台同步服务器进行有效分派,有可能会产生MongoDB内部的读写冲突。但也不是什么大的问题,到时候遇到了,再解决也好。

我们使用`supervisor`(一个python写的进程管理工具)来控制同步脚本,同时,也将`supervisor`设置为开机启动的一个服务。



- - - - - - - - - -


## 错误跟踪与测试

### 错误跟踪

- [sentry](https://github.com/getsentry/sentry)

> Disqus出产的工具,可以跟踪服务器运行过程中的异常。

> 我们并没有使用他们提供的在线服务,而是自己直接搭在一台服务器上。考虑可能哪天犯二,单bug但大批量地被捕获,费用就很高了,或者阻塞了其它正常需捕获的……

- Google Analytics

> 但我们在程序中处理的异常,sentry是不会捕获的;这些异常通常是404、403、500等页面。

> 对异常的处理,其逻辑也有可能是错误的;所以,我们这里又用到了Google统计的事件捕获脚本。


### 测试框架

其实,我们偷懒了,或者说时间不够。目前还没有写过一个测试用例,但慢慢这块需要补上。我们会使用两个技术方案,nose + tox,另外可能会上pylint + pep8.

我们前期开发的过程中,常见的情况就是修改完bug --> 直接部署 ---> 继续有bug --> 再修改再部署。

Web端,我们依赖于Gunicorn,可以不停止服务,平滑的重启服务。命令如下行,即能平滑重启:

sudo kill -HUP `cat /tmp/gunicorn.pid`



- - - - - - - - - -


## CDN服务

如论如何优化速度,国外的服务器,在面对静态文件、图片的处理时候,硬伤就出现了。

这方面,我们使用@Livid 同学的[ORCA](http://orca.io),目前来看,不能算极其稳定,但很过得去;并且价格极其厚道。

其实,我们也有考虑过一些不限流量的云端服务,比如香港的某云,美国的某云;但什么都不限制的,感觉总是不靠谱的。

另外,我们目前的流量并不算大。足以应对。

最重要的是,`自建服务`,是很凶残的方式,伤人伤己。我们深信不疑。


- - - - - - - - - -



## 自动部署

开始的时候,觉得人肉部署就好了, 省下自动部署的麻烦。

但慢慢地,人肉部署,在重复的劳作中,积累起来要消耗掉不少时间;而且还是有部署出错的概率。

另外,配置服务器,让我们崩溃了。所以,我们使用[Fabric](http://fabfile.org)来实现自动部署。



以下的脚本,可以让我们自动创建一个AWS的EC2服务器,并且完成生产环境的部署。

:::python
instance = create_instance(ec2_type='m1.small', tag='farbox-web', security_groups=['WEB']) # 创建服务器
env.host_string = instance.public_dns_name
cook('instance_init') # init
cook('git') # 安装git以及相应的key
cook('packages') # 安装一些packages
cook('web_server') # 安装nginx等web服务器需要的软件
cook('web_supervisor') # 安装supervisor以及相应针对web的配置
hostname('farbox-web') # 修改hostname


yes,解放了!

等真需要新上一台服务器的时候,不用手工敲键盘了!


### [不?]停机维护

在自动部署实现之前,有一次,我们在优化MongoDB的连接池,需要重启下服务器。然后说,`1分钟`后回来,结果是个把小时后回来……

这个感觉有点丢人。

现在,在真的需要停机维护的时候,我们采用这样的方案:

1, 新建一台Web服务器,并配置好 --> 自动的
2,新建一台数据库服务器, 并配置好 --> 自动的
3,克隆一份已有的数据库盘,mount到新服中
4,切换(就是换个IP,基本上属于无缝的)
5,在旧服中完成升级 --> 自动的
6,一切顺利,再切换回来

如此一来,基本上不需要停机了。AWS的云端,在这个时候彰显了力量。

不过,随着数据的增加,这个流程要消耗的时间也会增加。



- - - - - - - - - -


## DNS

### DNS加速

DNS是一件非常重要的事情。别人访问你服务器内容的第一步是`域名解析`,然后才会被指向某个IP所对应的服务器。

DNS的优化有两个方面。一是DNS本身的优化,但并不算太重要,因为一般客户端都会做DNS的缓存;然仍存在优化的余地的,如果我们DNS解析是在国外服务器的,那么算上连接时间,单次查询会在0.5秒左右,而国内的DNS解析一般只要0.05秒左右。

我们比较看重DNS另外一方面的作用,就是节点加速以及负载均衡。负载均衡,不言而喻,比如现在有2台服务器,IP分别为A与B;那么第一次DNS查询返回A,第二次DNS查询返回B,或者平均概率随机返回A、B中的一个IP,这样就能实现WEB层次的负载均衡。

最能能起到加速的作用其实用DNS来自动匹配各个节点。比如现在的情况是:`两台服务器,I号位于纽约,II号位于香港`,那么某个IP是来自大陆的,它走哪个节点速度最快呢?肯定是香港。这个时候DNS起到的作用就是根据IP的来路,匹配最近的服务器节点。一般情况下,通过这个优化,能加快0.3秒左右的速度。

### DNS防范

这个防范本身是有特指的,至于指向哪个,不言而喻。

域名被封,这个基本无能为力。所幸的是,FarBox本身的结构是去中心化的,即使FarBox被完全封掉,也不会对其它的站点产生影响(甚至其它站点也能提供FarBox的服务);某个用户的域名被封,也同样仅影响其自身。

我们真正需要的是防止被封IP。所以,DNS记录的TTL(Time To Live)一般都设置在5分钟以内。以及绑定域名是通过CNAME的形式。

> 关于这段,其实挺让人心酸的。


### FarBox现状

我们目前并没有启用GEODNS,而是托管了两套DNS记录,国内的走DNSPOD,国外的走AWS Route53.

AWS Route53上有比较基础的GEO DNS的功能,但仅仅是匹配大区的,比如东亚、美国西部等等,而没有匹配到国家。

如果规模允许,我们会自建DNS服务;同时,我们也会考虑提供域名托管的服务,这样就再也不用用户自己去改DNS记录了。

Any way,DNS方面确实有可能自建服务,但取决于规模;否则效益太低。


- - - - - - - - - -


## 小心坑

- 我们把gevent从0.13.6升级到1.0rc2的时候,虽然之前听一些朋友说会有坑,但实际情况很稳定,比老版本要稳定很多。
- 当我们把pymongo从2.3升级到2.4的时候,结果遭遇了一个大的BUG,查询基本失败,后来找到原因,给他们的开发者报完BUG后,老老实实降级。
- Flask的路由配置中,int\float是不支持负数的,这个翻werkzeug的源码才发现的;结果呢,实际生产环境中,我们拒绝了所有西半球时区的用户(虽然现在也没有几个)……
- 因为小小的代码洁癖,结果导致bug; 因为一个机制的变更,结果不停导致bug; 因为设计逻辑与底层冲突,还是停不了的bug。
- 反正,就勇敢地冒险吧!
9637 次点击
所在节点    程序员
28 条回复
zythum
2013-01-17 18:59:48 +08:00
看上去很高级的样子
jason52
2013-01-17 19:00:02 +08:00
楼主,我的test.farbox.com最近上不去了。肿么回事啊。
zxsky1
2013-01-17 19:03:30 +08:00
高端,等我都能读懂的时候再详细读一下。
levon
2013-01-17 19:19:27 +08:00
对很多人来说,这些都不实用,一个网站成功与否主要不在部署上
FarBox
2013-01-17 19:41:30 +08:00
@jason52 能打开的呀。

@levon 同意。善用工具工具很重要,这些技术方案在解决我们已经碰到的问题。:)
qiongqi
2013-01-17 19:48:21 +08:00
mark, 虽然是写php的
summer
2013-01-17 21:46:33 +08:00
感觉lz着魔了。
xiaojay
2013-01-17 21:59:58 +08:00
很好的服务,很好的分享。
会不会加入sns元素,类似tumblr?
twm
2013-01-17 22:19:30 +08:00
mk
koon_kai
2013-01-17 22:51:18 +08:00
LZ的技术架构很不错,很值得参考学习,正在用farbox的服务。
feihu
2013-01-17 23:07:27 +08:00
一个小建议:讲这个的时候能否加一个成本进去,这样看完后也能知道大致情况。
subdragon
2013-01-17 23:12:22 +08:00
create_instance应该是libcloud的函数?
lepture
2013-01-17 23:26:38 +08:00
pylint + pep8 建议换作 flake8。这应该是在编码时就做的事,而不是事后的弥补。

如果你使用版本控制工具,可添加 precommit hook ,在提交之前做好校验。
ameba
2013-01-17 23:40:17 +08:00
rookie 各种读不懂
ipconfiger
2013-01-18 00:44:44 +08:00
创建站点后没有默认的站点文件生成,很是不爽
liuhang0077
2013-01-18 03:03:19 +08:00
看完了 不错 可以试试我们的香港vps 对速度提升有一定作用
sarices
2013-01-18 08:51:49 +08:00
...这个网站没有退出功能
yyai3
2013-01-18 09:41:17 +08:00
很用心,值得学习~ 感谢
zzcflying
2013-01-18 19:09:48 +08:00
楼主。mango的性能我一直不敢恭维呀。为什么不用redis。
coosir
2013-01-18 23:48:52 +08:00
@ipconfiger 有的啊,就是反应比较慢
内容存在网盘是我以前一直渴望的,可是文章的分类标签等操作感觉还不是很灵活

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/57925

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX