分享FarBox.com的技术构架 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
FarBox
V2EX    程序员

分享FarBox.com的技术构架

  •  
  •   < href="/member/FarBox">FarBox 2013-01-17 18:55:08 +08:00 10534 次点击
    这是一个创建于 4720 天前的主题,其中的信息可能已经有所发展或是发生改变。
    原文地址, 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。
    - 反正,就勇敢地冒险吧!
    28 条回复    2014-08-01 16:10:23 +08:00
    zythum
        1
    zythum  
       2013-01-17 18:59:48 +08:00
    看上去很高级的样子
    jason52
        2
    jason52  
       2013-01-17 19:00:02 +08:00
    楼主,我的test.farbox.com最近上不去了。肿么回事啊。
    zxsky1
        3
    zxsky1  
       2013-01-17 19:03:30 +08:00
    高端,等我都能读懂的时候再详细读一下。
    levon
        4
    levon  
       2013-01-17 19:19:27 +08:00
    对很多人来说,这些都不实用,一个网站成功与否主要不在部署上
    FarBox
        5
    FarBox  
    OP
       2013-01-17 19:41:30 +08:00 via iPhone
    @jason52 能打开的呀。

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

    如果你使用版本控制工具,可添加 precommit hook ,在提交之前做好校验。
    ameba
        14
    ameba  
       2013-01-17 23:40:17 +08:00
    rookie 各种读不懂
    ipconfiger
        15
    ipconfiger  
       2013-01-18 00:44:44 +08:00
    创建站点后没有默认的站点文件生成,很是不爽
    liuhang0077
        16
    liuhang0077  
       2013-01-18 03:03:19 +08:00
    看完了 不错 可以试试我们的香港vps 对速度提升有一定作用
    sarices
        17
    sarices  
       2013-01-18 08:51:49 +08:00
    ...这个网站没有退出功能
    yyai3
        18
    yyai3  
       2013-01-18 09:41:17 +08:00
    很用心,值得学习~ 感谢
    zzcflying
        19
    zzcflying  
       2013-01-18 19:09:48 +08:00
    楼主。mango的性能我一直不敢恭维呀。为什么不用redis。
    coosir
        20
    coosir  
       2013-01-18 23:48:52 +08:00
    @ipconfiger 有的啊,就是反应比较慢
    内容存在网盘是我以前一直渴望的,可是文章的分类标签等操作感觉还不是很灵活
    skyahead
        21
    skyahead  
       2013-01-18 23:55:19 +08:00
    请问farbox多少用户/流量??
    ledzep2
        22
    ledzep2  
       2013-01-19 11:28:23 +08:00
    用python的人口味都惊人的一致啊.
    goinaction
        23
    goinaction  
       2013-01-19 14:35:07 +08:00
    来两张图也许更直观:D
    jo32
        24
    jo32  
       2013-01-27 23:43:19 +08:00 via iPad
    Great work!
    jinwyp
        25
    jinwyp  
       2013-01-28 00:46:15 +08:00
    学习
    m
        26
    m  
       2013-01-29 03:39:03 +08:00
    @FarBox 我放了一个普通zip文件在网站根目录,但下载下来储存的文件名成了一串sha1

    另外我注意到http://farbox.com/pages/price 里价格按照请求数计算,那我现在在哪能看到我的farbox的请求数呢?
    hit9
        27
    hit9  
       2013-04-13 14:16:12 +08:00
    很好的服务,希望加入社区化功能
    tylr
        28
    tylr  
       2014-08-01 16:10:23 +08:00
    @FarBox 请问免费用户两个月必须付费才能继续使用吗?5刀一年价格倒是不贵,但请问能提供支付宝支付途径吗?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2486 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 10:40 PVG 18:40 LAX 02:40 JFK 05:40
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86