分布式
如此一来,真正迎接WEB端的流量纯粹就是堆服务器了,并且可以将WEB服务器堆在地球上的任一个角落,只要能连上网即可。 一台Web服务器中,由是由以下的一些技术方案组成的:
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设置为开机启动的一个服务。 错误跟踪与测试 错误跟踪
我们并没有使用他们提供的在线服务,而是自己直接搭在一台服务器上。考虑可能哪天犯二,单bug但大批量地被捕获,费用就很高了,或者阻塞了其它正常需捕获的……
对异常的处理,其逻辑也有可能是错误的;所以,我们这里又用到了Google统计的事件捕获脚本。 测试框架 其实,我们偷懒了,或者说时间不够。目前还没有写过一个测试用例,但慢慢这块需要补上。我们会使用两个技术方案,nose + tox,另外可能会上pylint + pep8. 我们前期开发的过程中,常见的情况就是修改完bug --> 直接部署 ---> 继续有bug --> 再修改再部署。 Web端,我们依赖于Gunicorn,可以不停止服务,平滑的重启服务。命令如下行,即能平滑重启: sudo kill -HUP `cat /tmp/gunicorn.pid` CDN服务 如论如何优化速度,国外的服务器,在面对静态文件、图片的处理时候,硬伤就出现了。 这方面,我们使用@Livid 同学的ORCA,目前来看,不能算极其稳定,但很过得去;并且价格极其厚道。 其实,我们也有考虑过一些不限流量的云端服务,比如香港的某云,美国的某云;但什么都不限制的,感觉总是不靠谱的。 另外,我们目前的流量并不算大。足以应对。 最重要的是,自建服务,是很凶残的方式,伤人伤己。我们深信不疑。 自动部署 开始的时候,觉得人肉部署就好了, 省下自动部署的麻烦。 但慢慢地,人肉部署,在重复的劳作中,积累起来要消耗掉不少时间;而且还是有部署出错的概率。 另外,配置服务器,让我们崩溃了。所以,我们使用Fabric来实现自动部署。 以下的脚本,可以让我们自动创建一个AWS的EC2服务器,并且完成生产环境的部署。 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方面确实有可能自建服务,但取决于规模;否则效益太低。 小心坑
转载请保留固定链接: https://linuxeye.com/architecture/1624.html |