标签归档:技术架构

从企业级架构到互联网架构迁移的工程实践-演道网

猿哥解读

本文使用Java语言描述,但对于想熟悉互联网后端架构的小伙伴绝对是大有裨益。


摘要:随着业务的快速增长,一个线上交易系统在有限的时间内,不但需要维持线上系统的稳定,还要支撑新需求的开发,否则将由于技术支撑不利错失业务发展关键时间窗口。本文分享了一次从企业级架构到互联网架构迁移的工程实践。

因工作变动接手了一个云平台改造项目,该项目属于己经上线且每月有大量交易订单的云平台,之前采用的是SpringMVC+Hibernate+FreeMarker+MySql架构,集web前端和接口为一体。经过对业务增长趋势的评估,预计将在数月之后无法支撑原有业务的增长。

当前架构主要存在如下问题:

1、扩展维护困难

2、性能逐渐缓慢。

随着业务的快速增长逼迫我们对现有架构进行重构。由于是线上交易系统,留个我们改造的时间非常有限,不但需要维持线上系统的稳定还要支撑新需求的开发,否则将由于技术支撑不利错失业务发展关键时间窗口,基于务实的原则我们制定如下步骤进行逐步改造。

一、去hibernate迁移至mybatis

从hibernate迁移至mybatis, DAO层基本上需要重写一遍,其中主要工作量为理解原hibernate DAO层逻辑并翻译成sql,主要是细心活。其中需要注意的是mybatis动态表名的传入,需要将mapper的statementType类型修改为STATEMENT,并将SQL语句中#{}都改为${}。在使用${}传参过程中,需要特别注意SQL注入攻击危险。一般会在SpringMVC层将敏感字符转义。比如 “>” 用“>”表示,网上有很多封装函数,或者apache common lang包的StringEscapeUtils.escapeHtml()。

二、取掉sql之间的表关联

去掉sql之间的表关联,传统关系型数据库理论中的三范式在互联网的数据库模型中是不适用的,主要造成的问题是无法进行分表分库。这就要求所有dao方法必须保持单表操作。

保持单表操作为分表改造奠定了改造基础。

三、Service层对原子DAO业务逻辑进行组装

在取掉表关联后需要改造所有实体结构。首先取掉实体之间的一对一,多对一,多对多关联关系,将实体之间的引用关系修改为对实体ID的引用。同时为了上层方便使用需要引入业务BO对象,在service层调用多个原子的dao方法并组装成业务BO对象。

四、分表

在单张表超过2000万条记录后,mysql的查询性能开始降低,表变更字段等待时间漫长。分表后提升性能和扩展性后又带点以下问题:

  1. 路由策略的选择。

  2. 如何根据主键、订单号等路由到正确的表。

  3. 分表后分页查询

  4. 如何保证上线后分表数据平滑从老库过渡到新库。

1、路由策略的选择

首先,我们对数据库中所有的表记录进行分析,统计每张表的数据量大小。经过统计后我们发现随着业务的增长业务数据也会快速进行增长的表主要为订单表、订单明细表。其它的表在近两年内并不会随着业务的增长而快速增长。所以只需要对订单表、订单明细表进行拆分。

路由策略选择不可能做到完美,世界本来也是不完美的,关键是在合适的阶段选择合适的策略,即能满足商业战略时间窗口点又能在追求技术完美型中寻找平衡点。我们预测了业务近5年的发展目标为现有业5倍的增长,发现按月进行拆分可以保证每月数据量均低于2000万条,基于务实的原则我们选择了按月进行分表的路由策略。

经过多方面考虑在能够兼固效率和降低改造复杂度的思路提出老数据老办法,新数据新办法。老数据中的主键己经生成,如果按新的主键策略重新生成,会牵扯到所有关联表中的ID都需要进行替换,这样会增加改造的复杂度和工作量,所以最终考虑将新数据按照新的主键生成策略进行生成。当按月分表仍不能满足业务支持要求时,可以再次以日信息计算更细粒度的拆分策略,例如可按周为单位进行表折分。

2、根据主键或订单号选择正确的表

主键的生成算法  自增ID的生成,参考twitter  Snowflake算法

在Java语言系统中,可以通过Long来表示主键,Long类型包含64个位,正好可以存储该ID, 1至41位的二进制数值用来表示日期时间戳,43至53位可以表示1024台主机,我们可以为每台API服务器分配一个工作机器ID,43至55位可以生成线程唯一的序列号。预留的工作机器ID可以作为南北双活机房的路由判断条件,如1,2,3,4号工作机器ID路由到北机房API服务器,5,6,7,8工作机号ID路由到南机房API服务器。

当按订单号查询时系统首先根据订单号长度的不同,来选择是路由到新的切分订单表,还是路由到原订单表。因为新主键ID会包含日期信息,系统会根据主键解读出日期信息,根据月份的不同来选择该数据库对应的月份表,如果读取不出日期信息就可以判断出为原订单表。

3、如何保证上线后分表数据平滑从老表过渡到新表

首先系统配置统一的分表切割时间公共变量,在插入订单时先判断是否在分表切换时间点之前,如果在分表切换时间点之前则将订单数据插入到老表,否则将订单数据按当前月份不同插入到新的拆分月表。

4、分表后分页查询

在订单分表后存在的主要难点是分表后数据的分页查询操作。假设以2016-07-26 00:00:01 开始按月分表,查询2016-05-01 00:00:01至2016-09-11  12:00:05期间的所有订单分解为如下几步:

(1)通过开始时间、结束时间、分表时间计算出需要的路由信息集合。

  1. 格式:表名|起始日期|结束日期

  2. 路由集合:

Order|2016-05-01 00:00:01|2016-07-26 00:00:01

Order_2016_07|2016-07-26 00:00:01|2016-07-01 23:59:59

Order_2016_08|2016-08-01 00:00:01|2016-08-31 23:59:59

Order_2016_09|2016-09-01 00:00:01|2016-09-11 23:59:59

(2)按分页信息(pageNo,pageSize)及路由信息集合查询订单基本信息集合。

a.遍历路由集合返回总记录数及表概括信息集合。

        i. 表概括信息定义:表名、起始行数、记录数、路由信息;

        ii. 表概括信息集合:

  1. Order、1、137、Order|2016-05-0100:00:01|2016-07-26 00:00:01

  2. Order_2016_07、137、10、Order_2016_07|2016-07-2600:00:01|2016-07-01 23:59:59

  3. Order_2016_08、147、32、2016-08-0100:00:01|2016-08-31 23:59:59

  4. Order_2016_09、179、10、2016-09-0100:00:01|2016-09-11 23:59:59

        iii. 方法描述:

private RouteTableResult getRouteTableResult(OrderSearchModel searchModel,List routeTables) {

        Integer sumRow = new Integer(0);

        Map routeTableCountMap = new TreeMap();

        RouteTableResult routeTableResult = new RouteTableResult();

        for (String routeTable : routeTables) {

            String[] routeTableArray = routeTable.split(“\\|”);

            if (routeTableArray.length == 3) {

                String tableName =getTableByRouteTableAndSetSearchModel(searchModel, routeTableArray);

                Integer orderCount = ticketOrderDao.searchOrderCount(tableName,searchModel);

                Integer startIndex = sumRow.intValue();

                RouteTable routeInfo = new RouteTable(startIndex, orderCount, routeTable);

                routeTableCountMap.put(tableName, routeInfo);

                sumRow += orderCount;

            }

        }

        routeTableResult.setRouteTableCountMap(routeTableCountMap);

        routeTableResult.setSumRow(sumRow);

        returnrouteTableResult;

    }

b.根据分页信息,查询出该分页需要跨越的表路由信息集合,具体算法如下:

    i. 遍历概括信息集合

    ii. 当开始行和结束行与当前路由区间有交集则说明有数据在该表内并将该表加入遍历路由集合;

    iii. 如果路由表信息集合中有数据且不满足上述条件则退出;

    iv. 返回需要跨越的表路由信息集合;

    v. 方法描述:

private List getRouteTables(OrderSearchModel searchModel,Map routeTableCountMap) {

        List routeTableInfoList = new ArrayList();

        Integer startIndex = (searchModel.getPageNo() – 1) * searchModel.getPageSize();

        Integer endIndex = startIndex + searchModel.getPageSize() -1;

        for (Entry entry : routeTableCountMap.entrySet()) {

            RouteTable routeTable = entry.getValue();

            //开始行和结束行与当间路由区有交集

            if( !(startIndex > routeTable.getEndIndex()) && !(endIndex < routeTable.getStartIndex())){

                routeTableInfoList.add(routeTable.getRouteInfo());

            //如果路由表信息集合中有数据且不满足上述条件则退出

            }elseif (routeTableInfoList.size()>0) {

                break;

            }

        }

        returnrouteTableInfoList;

    }

c.查询该分页下的订单列表,具体算法如下:

    i.首先设置最后一次遍历的表为需要跨越路由信息集合的第一张表;

    ii.设置己读条数readCount等于0;

    iii.遍历需要跨越路由信息集合;

    iv. 根据路由信息返回表名及设置搜索条件;

    v. 根据表名获取路由概要信息;

    vi.计算开始行号,如果当前表名和最后遍历的表名相同,则开始行号等于(当前的页数-1)*原请求页面大小(originalPageSize)-当前表路由概要信息起始行,否则开行号设置为0;

    vii. 计算当前页面大小pageSize为原请求页面大小(originalPageSize) – 己读条数(readCount);

    viii. 设置搜索条件起始行号、当前页面大小;

    ix.设置最后一次遍历的表为当前表;

    x.根据当前表名、搜索条件调用dao返回订单基本信息列表,并加入订单总列表集合;

    xi.己读数增加当前订单列表大小;

    xii. 如果己读数大于等于原请求页面大小则跳出循环,否则继续循环;

    xiii.返回订单总列表集合;

    xiv. 方法描述:

private List getOrderListByRoutePageTable(OrderSearchModelsearchModel,

            Integer originalPageSize, Map routeTableCountMap,

            List routePageTables) {

        Integer readCount = 0;

        List orderList = new ArrayList();

        if (routePageTables != null && routePageTables.size() > 0) {

            String[] routeTableArrayFirst = routePageTables.get(0).split(“\\|”);

            String lastTableName = null ;

            if (routeTableArrayFirst.length == 3) {

                lastTableName = routeTableArrayFirst[0];

            }

            for (String routeTable : routePageTables) {

                String[] routeTableArray = routeTable.split(“\\|”);

                if (routeTableArray.length != 3) {

                    break;

                }

                String tableName = getTableByRouteTableAndSetSearchModel(searchModel, routeTableArray);

                RouteTable routeTableInfo = routeTableCountMap.get(tableName);

                Integer startRow = 0;

                if( tableName.equals(lastTableName)){

                    startRow = (searchModel.getPageNo()-1)*originalPageSizerouteTableInfo.getStartIndex();

                }

                Integer pageSizeoriginalPageSizereadCount;

                searchModel.setStartRow(startRow);

                searchModel.setPageSize(pageSize);

                lastTableName = tableName;

                List orderListPage = orderDao.searchOrderList(tableName,searchModel);

                orderList.addAll(orderListPage);

                readCount += orderListPage.size();

                if (readCount.intValue() >= originalPageSize) {

                    break;

                }

            }

        }

        returnorderList;

    }

(3)根据Order集合组装OrderBo集合

(4)根据返回的总数及分页信息组装分页结果

五、mysql主从分离、引入三级缓存

为了提高性能,首先配置mysql主从分离,通过Spring多数据源来实现动态切换。三级缓存主要分为:(1)、线程级:当同一线程请求时,线程级缓存绑定在线程间ThreadLocal变量上,可以降低线程间切换造成的时间开销。(2)、进程级:进程级缓存在同一jvm中共享缓存,减速少跨进程间网络开销。(3)、跨进程的集中式缓存:一般使用redis、memcache内存缓存来降低对数据库系统的冲击。在做完以上优化后,我们的接口响应速度提高了近5倍。

六、结束语

关于分布式服务化、异地南北机房双活,这里留个作业,日后成文和大家分享。

关注PHP技术大全,成长不止一点点!

分享出去,让知识地带无限蔓延!

转载自演道,想查看更及时的互联网产品技术热点文章请点击http://go2live.cn

【干货】大众点评运维架构的图文详解

张冠宇在大众点评做运维架构师。他在本文中介绍大众点评整个的运维架构。目前他们的运维分为4个组,相信跟大部分公司一样,运维团队分为:应用运维、系统运维、运维开发和监控运维,当然还有DBA团队和安全团队。整个运维团队全算上目前是不到40人规模。

嘉宾介绍

张冠宇:花名关羽,目前在大众点评做运维架构师一职。在大众点评这几年时间,见证了点评运维从无到有,从低效向高效的转变过程。

分享内容

今天分享专题大纲如图所示,从5个方面跟大家一起探讨:

1、点评运维团队的配置

目前我们运维分为4个组,相信跟大部分公司一样,运维团队分为:应用运维、系统运维、运维开发和监控运维,当然还有DBA团队和安全团队,这里就不一一罗列了。整个运维团队全算上目前是不到40人规模。

我们团队分工是这样的:

  1. 应用运维:负责支持线上业务,各自会负责对应的业务线,主要职能是保证线上业务稳定性和同开发共同支撑对应业务,以及线上服务管理和持续优化。

  2. 运维开发:帮助运维提升工作效率,开发方便快捷的工具,实现运维平台化自动化。

  3. 系统运维:负责操作系统定制和优化,IDC管理和机器交付,以及跳板机和账号信息管理。

  4. 监控运维:负责发现故障,并第一时间通知相关人员,及时处理简单故障和启动降级方案等。

2、点评的整体架构

先看下点评的机房情况。

点评目前是双机房结构,A机房主跑线上业务,B机房跑测试环境和大数据处理作业,有hadoop集群、日志备份、灾备降级应用(在建)等。点评目前机房物理机+虚拟机有近万台机器。

点评的整体架构,还是跟多数换联网公司一样,采用多级分层模式,我们继续来详细看下点评整体架构。

上面这幅图基本概括了点评的整体架构环境:

  1. 用户引导层用的是第三方的智能DNS+CDN。

  2. 负载均衡首先是F5做的4层负载均衡 之后是dengine做的7层负载均衡(Dengine是在tengine基础上做了二次开发)。再往后是varnish做的页面缓存  之后请求到web端 web端通过内部协议调用service(RPC)。

  3. 图片存储用的是mogileFS分布式存储 。

  4. 所有业务,全部有高可用方案,应用全部是至少2台以上。

  5. 当然,具体业务要复杂很多,这里只是抽象出简单层面,方便各位同学理解。

目前,点评运维监控是从4个维度来做的:


  1. 业务层面,如团购业务每秒访问数,团购券每秒验券数,每分钟支付、创建订单等(cat)。

  2. 应用层面,每个应用的错误数,调用过程,访问的平均耗时,最大耗时,95线等(cat)。

  3. 系统资源层面:如cpu、内存、swap、磁盘、load、主进程存活等 (zabbix)。

  4. 网络层面: 如丢包、ping存活、流量、tcp连接数等(zabbix cat)。

3、点评运维系统介绍


点评的运维和平台架构组做了很多实用的工具,这些工具组成了点评的整体运维体系。

目前自动化运维比较热,但自动化运维个人觉得是一种指导思想,没必要硬造概念和生搬硬套。自动化在很多公司百花齐放,各家有各家的玩法。但不管怎么定义,运维人员都必须从不同纬度去面对和解决企业所存在的问题。

点评在这方面,也是摸着石头过河,我们的思路是先造零件,再整合,通过零件的打造和之间的整合,慢慢勾勒出一条适合自己的运维自动化框架。

我们运维的理念是:

  1. 能用程序干活的,坚决程序化、平台化;

  2. 能用管理解决的问题,不用技术解决;

  3. 同一个错误不能犯三次;

  4. 每次故障,都是学习和提升的机会;

  5. 每个人都要有产品化思维,做平台产品让开发走自助路线;

  6. 小的,单一的功能,组合起来完成复杂的操作(任务分解);

所以,我们将自己的理念,融入到自己的作品中,做出了很多工具。

首先整体做个说明,点评运维工具系统汇总:

  1. 全方位监控系统:覆盖业务、应用、网络、系统等方面,做到任何问题,都可直观反馈。对不同应用等级,做到不同监控策略和报警策略。

  2. 自动化工具系统:对重复的、容易出错的、繁琐的工作尽可能工具化,通过小的策略组合,完成大的任务。

  3. 自动化工具系统:对重复的、容易出错的、繁琐的工作尽可能工具化,通过小的策略组合,完成大的任务。

  4. 记录和分析系统:对发生的问题和数据做记录并分析,不断的总结、完善和提升。

下面就跟大家来一一介绍下:

3.1 全方位监控系统

Zabbix大家应该非常熟悉了,这里就不做介绍,主要介绍下cat监控。

a.  业务监控:


这张是cat的应用监控图表,可直观从业务角度看出问题,可跟基线的对比,发现问题所在。如图所示,此时支付远偏离基线,流量正常,可能后端出了问题。

除了这些,还有创建订单、支付、首页访问、手机访问等业务数据。

这张图是从业务角度来监控的。

这张也是从业务层面来监控的,该图展示的是手机的访问量趋势图,下面包括延迟、成功率、链接类型、运营商等都有明确数据,该监控可全方位覆盖业务。

b.  应用监控:

从业务层面往下,就是应用层面。

应用状态大盘可清晰表示当前业务组件状态,如果某个业务不可用,其下面某个应用大量报错,说明可能是该应用导致。

该监控大盘十分清晰明的能展示业务下面的应用状态,可在某业务或者某域名打不开的时候,第一时间找出源头。

下图为应用报错大盘,出问题的应用会实时登榜(每秒都会刷新数据),当出现大故障时,运维人员可一眼看出问题;而当多个不同业务同时报错时,则可能是公共基础服务出了问题。


再看下图的这个功能,是Cat最强大的功能,能完整显示应用之间调用过程,做了什么事情,请求了那些,用了多长时间,成功率是多少,访问量是多大,尽收眼底。 Cat几乎无死角的覆盖到业务和应用层面的监控,当然还可做网络等层面监控,总之非常强大。这也是点评的鹰眼系统。

c.  Logscan日志扫描工具:



Logscan系统,是一套日志扫描工具,可根据你定义的策略,对日志内容进行定时扫描,该工具可覆盖到基于日志内容的检测,结合zabbix和cat,实现无死角覆盖。 比如有一些攻击的请求,一直遍历你的url,通过cat、zabbix等都无法灵活捕获,有了日志扫描,便可轻松发现。


3.2 自动化工作系统

首先介绍下点评的流程系统-workflow系统

顾名思义workflow是一套流程系统,其核心思想是把线上所有的变更以标准化流程的方式,梳理出来。

我们遵循一个理念,能用程序跑,就不去人操作。

流程有不同状态的转化,分别为发起、审计、执行、验证等环节。用户可自行发起自己的需求变更,通过运维审核,操作(大部分是自动的),验证。 如扩容、上线、dump内存、封IP等都为自动化流程。

以我们线上自动化扩容流程为展示,用户使用时,需要填写对应信息,提交后,运维在后台审核过后,就完全自动化扩容,扩容完成会有邮件通知,全程运维不需要登录服务器操作。(自动化倒不是太复杂的技术难题,通过小的任务组合,设置好策略即可). 几十台机器的扩容,运维只需点个审核通过按钮,数分钟而已。




经过长时间的推广,点评现在98%以上变更都是通过工作流平台完成的,所有变更全部有记录,做到出问题时 有法可依,违法可纠。

而且通过流程单的使用频率,可做数据分析,了解哪些操作比较频繁,能否自动化掉,是否还有优化空间。 这才是做平台的意义,以用户为导向。


流程系统就介绍到这里,朋友们可关注下其中核心思想。

下面介绍另一套重量级核心系统:Button系统

Button是一套代码管理、打包、部署上线系统,开发可完全自主化进行上传代码,自动化测试,打包,预发,灰度上线,全部上线,问题回滚等操作。 全程运维不用干预,完全平台自主化。

点评的运维,除了有些没法自动化的手动配置下,其他基本都是开发自助。 这就是自动化的威力!

Go平台系统,是一套运维操作系统,其中包含了很多常规操作、如批量重启、降级、切换、上下线、状态检测等。

该系统主要是解决运维水平参差不齐,工具又各有各的用法,比如说批量重启操作,有用ssh、有用fabric、有自己写shell脚本的。 干脆直接统一,进行规范,定义出来操作,通过平台化进行标准化。 由于长时间不出问题,偶尔出一下,运维长时间不操作,找个批量重启脚本还要找半天。 哪些不能自动化的,我们基本都做到go里了,在这里基本都是一键式的傻瓜操作了。

现在,我们监控团队就可以灵活操作,不需要有多高的技术含量,并且每次操作都有记录,做好审计和授权。


所有后台基本都是python、shell脚本实现,小的脚本组再整合成任务,这也是我们的重要理念之一。 对于比较复杂的任务,我们进行分解,然后用小的,单一的功能,组合起来完成复杂的操作(任务分解)。 其实我们实现自动化也是这个思路,先造零件,再拼装。

尽管有了puppet,go等工具,但对于一些job作业的管理,也显得非常吃力,我们架构组的同学做出一套任务调度系统。相当于分布式的crontab,并且有强大的管理端。 完全自主化管理,只需要定义你需要跑的job,你的策略,就完全不用管了。会自动去做,并且状态汇报、监控、等等全部都有记录,并实现完全自助化。

以上这些系统都非常注重体验,都有非常详细的数据统计和分析,每过一段时间,都有人去看,不断改进和优化,真正做到产品自运营。还有一些自动化系统就不一一介绍了。

3.3 配置和管理系统

先介绍下puppet管理系统,相信不少同学对puppet语法格式深恶痛绝,并且也领教过一旦改错造成的故障严重性。

而且随着多人协同工作后,模板和文件命名千奇百怪,无法识别。

针对这些问题,点评就做了一套管理工具,主要是针对puppet语法进行解析,实现web化管理,并进行规范化约束。

跟go系统一样的想法,将puppet中模块进行组合,组合成模块集(方法集),可方便识别和灵活管理。


下面展示的是我们的软负载均衡管理页面,该系统是线上SLB的管理系统。 其核心在于把nginx语法通过xml进行解析,实现web化管理,傻瓜式配置,规范化配置,避免误操作,版本控制,故障回滚等。

点评系统很多,基本上遇到个痛点,都会有人想办法把痛点解决。

下面就介绍下点评另一套强大配置系统,lion。



Lion是一套应用配置管理系统,点评的所有应用用到的配置,不在本地文本文件存储,都在一个单独系统存储,存储以key/value的方式存储。并且也是完全平台化,运维负责做好权限控制和审计。开发全部自助。

其核心是用了zookeeper的管理机制,将配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中。

是不是在点评做运维轻松很多?各种操作都工具化,自助化,自动化了。那运维还需要做什么。

3.4 记录和分析系统

此类系统虽然不怎么起眼,但对我们帮助也是特别大的,我们通过一些系统的数据记录和分析,发现了不少问题,也解决不少潜在问题,更重要的是,在这个不断完善总结的过程中,学习到了很多东西。

这个是我们故障分析系统,所有的故障都会做记录,故障结束后都会case by case的进行深入分析和总结。其实以上很多系统,都是从这些记录中总结出来的。

该系统为故障记录系统,每个故障都有发生的缘由和改进的方案,定期有人review。

运维起来很轻松吗?也不轻松,只是工作重点有了转移,避开了那些重复繁琐的工作,和开发同学深度结合,共同注重运营质量和持续优化。

再来看下图所示是点评的DOM系统,即运营质量管理平台,该平台汇总了线上的服务器状态、应用响应质量、资源利用率、业务故障等全方位的数据汇总平台。


并通过同比和环比,以及平均指标等数据,让各开发团队进行平台化PK,性能差的运维会去推动改进。




最后一个需要介绍的是雷达系统,该系统是我们最近在做的,一个比较高大上的项目。

朋友们也感受到了,我们系统之多,出问题查起来也比较费时。 不少同学生产环节也遇到过类似问题,出了问题到底是什么鬼?到底哪一块引起的呢? 结合这个问题,我们把线上的问题做了个分类,并给了一些策略层面的算法,能快速显示。 可让故障有个上下文的联系,如:上线时间、请求数下降、错误数增多等,哪个先出现,哪个后出现? 当然,这块功能还在做,目标是实现 出问题的时候,一眼就能从雷达系统定位问题类型和范围。


以上向大家演示的就是点评的运维系统,相信我们点评的运维思想都在里面体现了。

运维点评这几年的发展,主要目标是实现平台规范化、运维高效化、开发自主化 。

之前也是通过运维root登录,然后写脚本批量跑命令的低效运维。也经历过CMDB系统信息不准确,上线信息错乱的尴尬局面。也遇到过出了很大问题,运维忙来忙去,找不到rootcase。

好在,通过努力,这些问题现在都有了很大改观,相信朋友们通过展示的系统,能感觉出点评运维的进步。

4、运维踩过的坑和改进的地方

我就这些年,点评运维出的一些case案例,跟大家聊一聊我们做了哪些具体工作:

  1. 变更不知道谁做的,无法恢复,变更完也找不到根据,造成重大故障。//之前线上puppet通过vim的管理方式,由于运维同学失误推了一个错误配置,导致全部业务不可用1个小时,我们后面通过规范puppet配置修改并做成工具,进行权限控制,还加了流程系统,进行避免。

  2. 出了问题,开发说代码没问题,运维说环境没问题,该找谁?//我们后面做了工具,通过DOM和cat系统,可进行深度诊断,基本很容易定位问题所属。

  3. 执行了个错误命令,全线都变更了,导致服务不可用。//我们通过go系统,进行日常操作梳理,并做成工具,运维90%操作都可通过自动化流程和go平台完成。大大缩减故障产生率,并且之后进行权限回收。

  4. 出问题了,各种系统翻来查去,无法快速定位,找不到rootcase。//点评正在做雷达系统,就是将历史存在的问题,进行复盘,将一些故障类型,进行分级,然后通过策略和算法,在雷达系统上进行扫描,出问题环节可快速第一时间优先显示。

  5. 运维天天忙成狗,还不出成绩,天天被开发吐槽。点评这两年完全扭转了局势,现在是运维吊打开发,因为我们目前,大部分系统都实现了开发自助化,运维被解放出来,开始不断完善平台和关注业务运营质量,我们dom系统是可定制的,运维每天都把各业务的核心指标报表发到各位老大那里,哪些服务质量差,响应慢,开发都会立即去改。(当然,需要老大们支持)。


5、未来关注的领域和方向

点评也有些前沿的关注点,比如比较热的Paas技术。

PaaS和云很热,还有docker技术,点评也不能掉队,目前点评有数千个docker的实例在跑线上的业务。


上图java都是跑的docker实例


目前点评Docker这块可做到10秒内快速部署业务并可响应用户请求。30秒内可完成一次实例无缝迁移。 个人感觉docker技术不在于底层这块,在于上层管理系统的构造。底层一方面是持续优化,挖掘性能,但更重要的是在策略层和调度层。 如何快速部署、迁移、恢复、降级、扩容等,做好这些还有不少挑战。

点评这两年成长很多,但需要走的路也很多,未来关注的点会在多系统的有机整合和新技术的尝试以及发展,还会更多的关注智能策略层面。

结束语

在最后结束时,感谢各位到场朋友捧场,也感谢点评运维和平台架构的每一位同事,有了你们,点评运维才走到了今天,我们共同努力,来创造新时代的运维体系。

点评很多系统都是第一次拿出跟大家分享,大家可看一下设计理念和思想。



如果您喜欢这篇文章,请记得点赞哦



架构师在精益企业中的角色是什么?

没人带,自学慢,不在BAT怎么学产品?人人都是产品经理联合200+BAT资深产品经理带你学 点此查看详情

作为一名企业架构师,当自己的组织开始向精益或者敏捷实践转变的时候,很有可能会感觉到一些失落。在转变之前,你经过了非常努力的工作才达到了架构师的位置,你可能编写了保持企业运行所需的大部分关键系统软件,帮助实现、甚至是设计了整个架构。你知道架构中的哪些地方使用了比较老的技术,哪些地方比较脆弱,但是由于没有资源和时间去改变而不得不对现实妥协。

goujiashi

同时,由于只有你知道如何保持事情运转,所以很多事情不得不亲力亲为,导致自己没有精力去研究探索最新的趋势。而为了提升时间的利用效率,你制定了统一的标准,并且尝试着在架构评审或计划会议上控制需求。但是开发人员由于不清楚公司良好运转的条件,对每天重复的老套工作和繁琐流程充满了抱怨,使得你不得不加强政策从而尽量保持控制。

现在,新的领导或者咨询公司来了,他们声称组织应当 “变得敏捷”。对此,开发者的理解是,敏捷和灵活性允许他们做自己想做的任何事情。于是,他们开始将你看作是陈旧事物的代表,开始颠覆或者忽略你。他们引入了可能会破坏基础设施稳定性或者可能会在关键时刻引发系统故障的实践和技术。虽然你清楚组织依然需要你,但是却感觉每一个人都在和自己做对。

架构师在精益企业中的角色是什么?

事实上,他们可能比以往任何时候都更加需要你。需要你的知识和经验将风险和管理成本最小化,将技术任务与业务对齐。只不过虽然任务还是那些任务,但实现方式与之前相比有一点不同。因为无论是精益还是敏捷,它们都关注于价值创建、成本缩减和快速反馈,因此,如果想在新环境中取得成功那么你必须接受一些新的实践。例如,共享简单的愿景、建立桥梁、对齐业务、提供指导等所有可以促进创新的努力。

那么如何实现这种转变呢?总体来看,企业架构师和其团队需要从传统的实践中进行转变。架构师将成为信息的影响者和聚合者,同时也是信息的传播者,其角色定位不再是自己做决定,而是帮助其他人做出正确的决定。而要实现这一目标需要一些新的工具和技术。下面将会介绍一些关于如何扮演好这一新角色的途径,虽然想法比较高层,并非适合所有的组织,但是每个组织的目标是灵活的,通过技术实验和效果衡量,团队可以从中选择适合自己的,舍弃不适合的。

拥有并共享同一个愿景

保持一致性的第一步,也是非常重要的一步,就是让整个组织拥有一个长期的目标。想清楚当前和将来的架构是让项目保持协调一致的关键。应该从评估现在的架构开始,找出目前都有哪些系统,它们的作用是什么?这一步不需要深入详细的描述,也不用找到它们都部署在哪台服务器上,只需要理清楚应用程序和产品以及它们之间的关系即可。

整个架构可能分很多层。如果是大型组织,那就先将问题分解成功能区,然后再一个个的找出来。如果有基础的架构模式或者策略,那就识别出来,看看哪些地方遵循该模式,哪些地方没有。例如,如果组织采用的是面向服务的架构,那就看看哪些应用程序基于该架构直接访问主数据?它们如何与常用的数据库通信?

在搞清楚系统当前的状态之后,接下来就需要考虑清楚将来的架构是什么样子。是否应该保留与现在一样的基础架构?完全采用全新的架构是否会更好?当前架构有哪些优势和劣势?如果要演化当前的架构,那就创建一张架构更新图,在图上将变化的部分与保留的部分区分开。如果整体架构的变化是必要的,那就要清楚理想情况下最终状态是什么样。要记住,这是一个比较长远的、需要技术组的其他人遵循的愿景。

有了愿景之后,还需要确保组织中的技术领导者能够理解它。这就需要向关键的开发者介绍愿景,获取他们的反馈。通常,他们比你更清楚某些事情的来龙去脉,也更能帮助你更好地理解架构。你需要愿意并且渴望基于这些反馈调整愿景。如果要对整体架构或者某个特定区域做出革命性的改变,尽量让团队认可这种转变,因为这会让愿景更容易实现。尽量不要让架构成为一种任务,而应该将其看做是一种能够让开发团队建立共识的工具。要让开发团队成为你的合作者或者同盟。因为他们积极地参与远比完全按照自己的想法推进愿景更有价值。

在达成某种程度的共识之后,一定要让所有人都知道当前的架构和将来的架构分别是什么样子。这并不是说要将它们放到磁盘上的某个文件夹、SharePoint 网站或者 Wiki 上,而是要制作海报或者一整面墙的涂鸦,在很多地方展示它们,确保每个人都能够了解该愿景,并激励他们不断地向该目标努力。在架构演进的过程中,这些图画也需要随之改变以反映当前的工作进展。要展示出那些正在提升的地方并认可为之付出的团队。如果其他人对一起构建伟大架构的工作感到自豪,那么他们就会支持你的工作。

建立桥梁

有了愿景之后,你就想它成为现实。但是既然你或者你的团队并不开发或者管理项目,这又如何实现呢?最好的方法就是成为开发团队的合作者和资源。你的目标并不是限制或者阻碍工作的进展,而是促进它。当某个团队开始开发的时候,与他们的技术经理和项目经理沟通,向他们展示更新后的企业架构图,讨论如何让他们的项目实现这一愿景。通常情况下,团队从事的工作与企业正在进行或者已完成的项目相似。架构师应该确保团队负责人了解这些项目,以便于能够在实际的代码和产品中利用共享的经验。尽量不要关注实现细节,不要关心使用的类库及其版本,要关注高层目标和项目设计以及它们与整体愿景的对齐方法。

架构师在精益企业中的角色是什么?

在讨论项目的时候,不可避免的会遇到技术选型的问题。大部分情况下,团队会倾向于使用与公司其他项目相似的技术。但是,技术人员偶尔也会学习一些新技术,并想使用它们来解决问题。

此时,不要立即对新技术说不,或者主观地认为技术人员选择新工具的原因仅仅是因为它新或者它非常有趣。虽然这种情况确实存在,但也有可能人们为解决问题而创建的新工具正好恰逢其时。应该与技术团队讨论决定使用新技术的理由是否充分。确保团队理解将新平台带到产品中的成本和困难,以及这些付出的回报。

架构师必须要学会倾听,并在给出结论之前做一些探索,通过实际的测量和边界做一些时间可控的概念验证,以此来确定可行性。如果最终发现新技术并不是正确的选择,那就试着与开发人员或者他们的领导沟通,达成一致意见。尽可能地不要让变化成为一种任务,那样不利于你与开发团队之间良好关系的建立,无法确保他们会在将来做决定时考虑你的意见。在实验新工具或者技术的时候,要限制公司在同一时间段内进行的实验的数量,因为同时进行多种实验很难精确地衡量每一个所产生的影响。

最后,成功的企业架构师只能是那些能够取得开发团队支持的人。如果你将他们当作下属,他们就会找到应付你的方式,将组织愿景和战略至于危险境地,此时你依然需要对结果负责,却几乎没有改变的能力。相反地,如果你将他们当作合作伙伴,那么他们就会帮你实现愿景,所有人都会取得成功。要拥抱变化,衡量变化,确保每一个人都理解变化的价值,同时始终都应该尽量引导团队实现组织架构的愿景。

寻找改变的机会

大的改变需要时间和机遇。一旦确定了将来的愿景,我们就会开始在企业里营造兴奋的氛围,并想立即看到结果。但有一点非常重要,那就是要时刻牢记对架构进行较大的调整需要循序渐进,需要合适的时机。要从已有的项目开始改变,引导新的实现向架构愿景的方向发展。要记住改变代码使之向预定方向发展的机会可能并不会按照期望的速度或者从期望的区域进行。要学会庆祝胜利,无论是多小的胜利,要对能作出积极改变的机会保持警惕。

也就是说,要优先处理组织已有项目中最糟糕的那部分。业务领导几乎不能理解改变技术组件的价值,也不清楚维持现状的成本。当需要纯粹的技术变化时,需要创造机会进行改变。根据改变将来对业务的价值、节约的成本以及降低的风险决定改变的速度。如果有必要,从业务部门寻找一个合作伙伴,创建一个既能增加业务价值,又能改变架构的项目。要寻找机会移除那些拖了预算和运营团队后退的老应用程序和硬件。

耐心和合理的改变速度有助于避免挫折。记住,只能改变正在进行的工作,因此预算应该尽可能地包含大部分项目。通过识别那些能够为业务创造新价值或者节约更多金钱的新项目而不是通过节约开发成本来创造机会。要记住,产生业务价值是最主要的目标,因此要避免那些有趣却没有价值的、纯粹的技术项目。当业务认识到遵循愿景所增加的产量和价值,他们做出改变的势头和步伐就会加快。此时,就有机会继续塑造并精炼愿景了。

构建学习社区

除了要对整个企业架构有一个愿景之外,企业架构师还需要清楚愿景的执行需要正确的技能和实践。每一个开发团队都需要学习并提高成功所需要的技能,在不同的团队之间共享最佳技能和实践有助于提高每一个人的能力,并建立共同的目标感。作为团队之间的桥梁,企业架构师最适合培养这种团队意识。

构建社区的方式有很多种,例如非常规的午餐和第三方培训。不要试图自己决定组织需要的内容,应该建立一个技术小组,让技术领导和那些热衷于技术的开发者参与进来帮你做出决定。团队通过定期的会议制定相关计划。确保为开发团队预留一定的时间让他们分享自己的经验——无论是成功的还是失败的。让所有人都有机会与整个 IT 组织分享知识是一个健康的组织构建社区意愿的开始。

尽量不要强制人们参加培训和学习活动。这些活动应该是开放的,大家可以根据自己的意愿选择。让人们参加不感兴趣的课程非但无法达到预期的效果,反而会挫败主讲人的激情。那些充满激情的人会乐于接受学习和成长的机会,那些不想提高的人则不会从这种机会中受益。

社区感和学习的机会会激励开发者,增强他们的归属感。通过引领社区的核心,你能够引导组织的开发团队向规划的愿景对齐,而允许其他人参与这种过程则有助于从内部确定并构建领导关系。此外,强烈的自豪感和社区感能够产生更好的质量和更多的协作。

结论

改变从来都不是容易、快速的。向新的实践转变需要时间和努力,但最终你会发现这是值得的。当团队能够一起协作创造价值,业务将 IT 看做是合作伙伴而不是负担的时候,所有的一切都是值得的。记住,一个精益企业的架构师能够让开发团队和业务部门建立合作关系,能够创建一个愿景并引导开发团队向该愿景努力。知识虽然不是很深入但是却很广泛,能帮助开发者提高自己,能通过明智的实验进行概念验证。最重要的是,懂得享受自己,能够学习新事物并创造价值,能让组织成为业界领先的创新者。

 

来源@36氪

文章链接:http://36kr.com/p/5042754.html

解剖Twitter:Twitter系统架构设计分析

没人带,自学慢,不在BAT怎么学产品?人人都是产品经理联合200+BAT资深产品经理带你学 点此查看详情

随着信息爆炸的加剧,微博客网站Twitter横空出世了。用横空出世这个词来形容Twitter的成长,并不夸张。从2006年5月Twitter上线,到2007年12月,一年半的时间里,Twitter用户数从0增长到6.6万。又过了一年,2008年12月,Twitter的用户数达到5百万。[1]

Twitter网站的成功,先决条件是能够同时给千万用户提供服务,而且提供服务的速度要快。[2,3,4]

有观点认为,Twitter的业务逻辑简单,所以竞争门槛低。前半句正确,但是后半句有商榷余地。Twitter的竞争力,离不开严谨的系统架构设计。

【1】万事开头易

Twitter的核心业务逻辑,在于Following和Be followed。[5]

进入Twitter个人主页,你会看到你following的那些作者,最近发表的微博客。所谓微博客,就是一则短信,Twitter规定,短信的长度不得超过140个字。短信不仅可以包含普通文字信息,也可以包含URL,指向某个网页,或者照片及视频等等。这就是following的过程。

当你写了一则短信并发表以后,你的followers会立刻在他们的个人主页中看到你写的最新短信。这就是be followed的过程。

实现这个业务流程似乎很容易。

1. 为每一个注册用户订制一个Be-followed的表,主要内容是每一个follower的ID。同时,也订制一个Following的表,主要内容是每一个following作者的ID。

2. 当用户打开自己的个人空间时,Twitter先查阅Following表,找到所有following的作者的ID。然后去数据库读取每一位作者最近写的短信。汇总后按时间顺序显示在用户的个人主页上。

3. 当用户写了一则短信时,Twitter先查阅Be-followed表,找到所有followers的IDs。然后逐个更新那些followers的主页。

如果有follower正在阅读他的Twitter个人主页,主页里暗含的JavaScript会自动每隔几十秒,访问一下Twitter服务器,检查正在看的这个个人主页是否有更新。如果有更新,立刻下载新的主页内容。这样follower就能读到最新发表的短信了。

从作者发表到读者获取,中间的延迟,取决于JavaScript更新的间隔,以及Twitter服务器更新每个follower的主页的时间。

从系统架构上来说,似乎传统的三段论(Three-tier architecture [6]),足够满足这个业务逻辑的需要。事实上,最初的Twitter系统架构,的确就是三段论。

Reference:

[1] Fixing Twitter. (http://www.bookfm.com/courseware/coursewaredetail.html?cid=100777)
[2] Twitter blows up at SXSW conference. (http://gawker.com/tech/next-big-thing/twitter-blows-up-at-sxsw-conference-243634.php)
[3] First Hand Accounts of Terrorist Attacks in India on Twitter and Flickr. (http://www.techcrunch.com/2008/11/26/first-hand-accounts-of-terrorist-attacks-in-india-on-twitter/)
[4] Social Media Takes Center Stage in Iran. (http://www.findingdulcinea.com/news/technology/2009/June/Twitter-on-Iran-a-Go-to-Source-or-Almost-Useless.html)
[5] Twitter的这些那些. (http://www.ccthere.com/article/2363334) (http://www.ccthere.com/article/2369092)
[6] Three tier architecture. http://en.wikipedia.org/wiki/Multitier_architecture

【2】三段论

网站的架构设计,传统的做法是三段论。所谓“传统的”,并不等同于“过时的”。大型网站的架构设计,强调实用。新潮的设计,固然吸引人,但是技术可能不成熟,风险高。所以,很多大型网站,走的是稳妥的传统的路子。

2006年5月Twitter刚上线的时候,为了简化网站的开发,他们使用了Ruby-On-Rails工具,而Ruby-On-Rails的设计思想,就是三段论。

1. 前段,即表述层(Presentation Tier) 用的工具是Apache Web Server,主要任务是解析HTTP协议,把来自不同用户的,不同类型的请求,分发给逻辑层。

2. 中段,即逻辑层 (Logic Tier)用的工具是Mongrel Rails Server,利用Rails现成的模块,降低开发的工作量。

3. 后段,即数据层 (Data Tier) 用的工具是MySQL 数据库。

先说后段,数据层。

Twitter 的服务,可以概括为两个核心,1. 用户,2. 短信。用户与用户之间的关系,是追与被追的关系,也就是Following和Be followed。对于一个用户来说,他只读自己“追”的那些人写的短信。而他自己写的短信,只有那些“追”自己的人才会读。抓住这两个核心,就不难理解 Twitter的其它功能是如何实现的[7]。

围绕这两个核心,就可以着手设计Data Schema,也就是存放在数据层(Data Tier)中的数据的组织方式。不妨设置三个表[8],

1. 用户表:用户ID,姓名,登录名和密码,状态(在线与否)。

2. 短信表:短信ID,作者ID,正文(定长,140字),时间戳。

3. 用户关系表,记录追与被追的关系:用户ID,他追的用户IDs (Following),追他的用户IDs (Be followed)。

再说中段,逻辑层。

当用户发表一条短信的时候,执行以下五个步骤,

1. 把该短信记录到“短信表” 中去。

2. 从“用户关系表”中取出追他的用户的IDs。

3. 有些追他的用户目前在线,另一些可能离线。在线与否的状态,可以在“用户表”中查到。过滤掉那些离线的用户的IDs。

4. 把那些追他的并且目前在线的用户的IDs,逐个推进一个队列(Queue)中去。

5. 从这个队列中,逐个取出那些追他的并且目前在线的用户的IDs,并且更新这些人的主页,也就是添加最新发表的这条短信。

以上这五个步骤,都由逻辑层(Logic Tier)负责。前三步容易解决,都是简单的数据库操作。最后两步,需要用到一个辅助工具,队列。队列的意义在于,分离了任务的产生与任务的执行。

队列的实现方式有多种,例如Apache Mina[9]就可以用来做队列。但是Twitter团队自己动手实现了一个队列,Kestrel [10,11]。Mina与Kestrel,各自有什么优缺点,似乎还没人做过详细比较。

不管是Kestrel还是Mina,看起来都很复杂。或许有人问,为什么不用简单的数据结构来实现队列,例如动态链表,甚至静态数组?如果逻辑层只在一台服务器上运行,那么对动态链表和静态数组这样的简单的数据结构,稍加改造,的确可以当作队列使用。Kestrel和Mina这些“重量级”的队列,意义在于支持联络多台机器的,分布式的队列。在本系列以后的篇幅中,将会重点介绍。

最后说说前段,表述层。

表述层的主要职能有两 个,1. HTTP协议处理器(HTTP Processor),包括拆解接收到的用户请求,以及封装需要发出的结果。2. 分发器(Dispatcher),把接收到的用户请求,分发给逻辑层的机器处理。如果逻辑层只有一台机器,那么分发器无意义。但是如果逻辑层由多台机器组成,什么样的请求,发给逻辑层里面哪一台机器,就大有讲究了。逻辑层里众多机器,可能各自专门负责特定的功能,而在同功能的机器之间,要分摊工作,使负载均衡。

访问Twitter网站的,不仅仅是浏览器,而且还有手机,还有像QQ那样的电脑桌面工具,另外还有各式各样的网站插件,以便把其它网站联系到Twitter.com上来[12]。因此,Twitter的访问者与Twitter网站之间的通讯协议,不一定是HTTP,也存在其它协议。

三段论的Twitter架构,主要是针对HTTP协议的终端。但是对于其它协议的终端,Twitter的架构没有明显地划分成三段,而是把表述层和逻辑层合二为一,在Twitter的文献中,这二合一经常被称为“API”。

综上所述,一个能够完成Twitter基本功能的,简单的架构如Figure 1 所示。或许大家会觉得疑惑,这么出名的网站,架构就这么简单?Yes and No,2006年5月Twitter刚上线的时候,Twitter架构与Figure 1差距不大,不一样的地方在于加了一些简单的缓存(Cache)。即便到了现在,Twitter的架构依然可以清晰地看到Figure 1 的轮廓。

Figure 1. The essential 3-tier of Twitter architecture
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/d22c_4051785892_e677ae9d33_o.png

Reference,

[7] Tweets中常用的工具 (http://www.ccthere.com/article/2383833)
[8] 构建基于PHP的微博客服务 (http://webservices.ctocio.com.cn/188/9092188.shtml)
[9] Apache Mina Homepage (http://mina.apache.org/)
[10] Kestrel Readme (http://github.com/robey/kestrel)
[11] A Working Guide to Kestrel. (http://github.com/robey/kestrel/blob/master/docs/guide.md)
[12] Alphabetical List of Twitter Services and Applications (http://en.wikipedia.org/wiki/List_of_Twitter_services_and_applications)

【3】Cache == Cash

Cache == Cash,缓存等于现金收入。虽然这话有点夸张,但是正确使用缓存,对于大型网站的建设,是至关重要的大事。网站在回应用户请求时的反应速度,是影响用户体验的一大因素。而影响速度的原因有很多,其中一个重要的原因在于硬盘的读写(Disk IO)。

Table 1 比较了内存(RAM),硬盘(Disk),以及新型的闪存(Flash),在读写方面的速度比较。硬盘的读写,速度比内存的慢了百万倍。所以,要提高网站的速度,一个重要措施是尽可能把数据缓存在内存里。当然,在硬盘里也必须保留一个拷贝,以此防范万一由于断电,内存里的数据丢失的情况发生。

Table 1. Storage media comparison of Disk, Flash and RAM [13]
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/9d42_4060534279_f575212c12_o.png

Twitter 工程师认为,一个用户体验良好的网站,当一个用户请求到达以后,应该在平均500ms以内完成回应。而Twitter的理想,是达到200ms- 300ms的反应速度[17]。因此在网站架构上,Twitter大规模地,多层次多方式地使用缓存。Twitter在缓存使用方面的实践,以及从这些实践中总结出来的经验教训,是Twitter网站架构的一大看点。

Figure 2. Twitter architecture with Cache
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/0ea9_4065827637_bb2ccc8e3f_o.png

哪里需要缓存?越是Disk IO频繁的地方,越需要缓存。

前面说到,Twitter业务的核心有两个,用户和短信(Tweet)。围绕这两个核心,数据库中存放着若干表,其中最重要的有三个,如下所示。这三个表的设置,是旁观者的猜测,不一定与Twitter的设置完全一致。但是万变不离其宗,相信即便有所不同,也不会本质区别。

1. 用户表:用户ID,姓名,登录名和密码,状态(在线与否)。
2. 短信表:短信ID,作者ID,正文(定长,140字),时间戳。
3. 用户关系表,记录追与被追的关系:用户ID,他追的用户IDs (Following),追他的用户IDs (Be followed)。

有没有必要把这几个核心的数据库表统统存放到缓存中去?Twitter的做法是把这些表拆解,把其中读写最频繁的列放进缓存。

1. Vector Cache and Row Cache

具体来说,Twitter工程师认为最重要的列是IDs。即新发表的短信的IDs,以及被频繁阅读的热门短信的IDs,相关作者的IDs,以及订阅这些作者的读者的IDs。把这些IDs存放进缓存 (Stores arrays of tweet pkeys [14])。在Twitter文献中,把存放这些IDs的缓存空间,称为Vector Cache [14]。

Twitter工程师认为,读取最频繁的内容是这些IDs,而短信的正文在其次。所以他们决定,在优先保证Vector Cache所需资源的前提下,其次重要的工作才是设立Row Cache,用于存放短信正文。

命中率(Hit Rate or Hit Ratio)是测量缓存效果的最重要指标。如果一个或者多个用户读取100条内容,其中99条内容存放在缓存中,那么缓存的命中率就是99%。命中率越高,说明缓存的贡献越大。

设立Vector Cache和Row Cache后,观测实际运行的结果,发现Vector Cache的命中率是99%,而Row Cache的命中率是95%,证实了Twitter工程师早先押注的,先IDs后正文的判断。

Vector Cache和Row Cache,使用的工具都是开源的MemCached [15]。

2. Fragment Cache and Page Cache

前文说到,访问Twitter网站的,不仅仅是浏览器,而且还有手机,还有像QQ那样的电脑桌面工具,另外还有各式各样的网站插件,以便把其它网站联系到Twitter.com上来[12]。接待这两类用户的,是以Apache Web Server为门户的Web通道,以及被称为“API”的通道。其中API通道受理的流量占总流量的80%-90% [16]。

所以,继Vector Cache和Row Cache以后,Twitter工程师们把进一步建筑缓存的工作,重点放在如何提高API通道的反应速度上。

读者页面的主体,显示的是一条又一条短信。不妨把整个页面分割成若干局部,每个局部对应一条短信。所谓Fragment,就是指页面的局部。除短信外,其它内容例如Twitter logo等等,也是Fragment。如果一个作者拥有众多读者,那么缓存这个作者写的短信的布局页面(Fragment),就可以提高网站整体的读取效率。这就是Fragment Cache的使命。

对于一些人气很旺的作者,读者们不仅会读他写的短信,而且会访问他的主页,所以,也有必要缓存这些人气作者的个人主页。这就是Page Cache的使命。

Fragment Cache和Page Cache,使用的工具也是MemCached。

观测实际运行的结果,Fragment Cache的命中率高达95%,而Page Cache的命中率只有40%。虽然Page Cache的命中率低,但是它的内容是整个个人主页,所以占用的空间却不小。为了防止Page Cache争夺Fragment Cache的空间,在物理部署时,Twitter工程师们把Page Cache分离到不同的机器上去。

3. HTTP Accelerator

解决了API通道的缓存问题,接下去Twitter工程师们着手处理Web通道的缓存问题。经过分析,他们认为Web通道的压力,主要来自于搜索。尤其是面临突发事件时,读者们会搜索相关短信,而不理会这些短信的作者,是不是自己“追”的那些作者。

要降低搜索的压力,不妨把搜索关键词,及其对应的搜索结果,缓存起来。Twitter工程师们使用的缓存工具,是开源项目Varnish [18]。

比较有趣的事情是,通常把Varnish部署在Web Server之外,面向Internet的位置。这样,当用户访问网站时,实际上先访问Varnish,读取所需内容。只有在Varnish没有缓存相应内容时,用户请求才被转发到Web Server上去。而Twitter的部署,却是把Varnish放在Apache Web Server内侧[19]。原因是Twitter的工程师们觉得Varnish的操作比较复杂,为了降低Varnish崩溃造成整个网站瘫痪的可能性,他们便采取了这种古怪而且保守的部署方式。

Apache Web Server的主要任务,是解析HTTP,以及分发任务。不同的Mongrel Rails Server负责不同的任务,但是绝大多数Mongrel Rails Server,都要与Vector Cache和Row Cache联系,读取数据。Rails Server如何与MemCached联系呢?Twitter工程师们自行开发了一个Rails插件(Gem),称为CacheMoney。

虽然Twitter没有公开Varnish的命中率是多少,但是[17]声称,使用了Varnish以后,导致整个Twitter.com网站的负载下降了50%,参见Figure 3.

Figure 3. Cache decreases Twitter.com load by 50% [17]
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/0b3a_4061273900_2d91c94374_o.png

Reference,

[12] Alphabetical List of Twitter Services and Applications.
(http://en.wikipedia.org/wiki/List_of_Twitter_services_and_applications)
[13] How flash changes the DBMS world.
(http://hansolav.net/blog/content/binary/HowFlashMemory.pdf)
[14] Improving running component of Twitter.
(http://qconlondon.com/london-2009/file?path=/qcon-london-2009/slides/
EvanWeaver_ImprovingRunningComponentsAtTwitter.pdf)
[15] A high-performance, general-purposed, distributed memory object caching system.
(http://www.danga.com/memcached/)
[16] Updating Twitter without service disruptions.
(http://gojko.net/2009/03/16/qcon-london-2009-upgrading-twitter-without-service-disruptions/)
[17] Fixing Twitter. (http://assets.en.oreilly.com/1/event/29/
Fixing_Twitter_Improving_the_Performance_and_Scalability_of_the_World_s_
Most_Popular_Micro-blogging_Site_Presentation%20Presentation.pdf)
[18] Varnish, a high-performance HTTP accelerator.
(http://varnish.projects.linpro.no/)
[19] How to use Varnish in Twitter.com?
(http://projects.linpro.no/pipermail/varnish-dev/2009-February/000968.html)
[20] CacheMoney Gem, an open-source write-through caching library.
(http://github.com/nkallen/cache-money)

【4】抗洪需要隔离

如果说如何巧用Cache是Twitter的一大看点,那么另一大看点是它的消息队列(Message Queue)。为什么要使用消息队列?[14]的解释是“隔离用户请求与相关操作,以便烫平流量高峰 (Move operations out of the synchronous request cycle, amortize load over time)”。

为了理解这段话的意思,不妨来看一个实例。2009年1月20日星期二,美国总统Barack Obama就职并发表演说。作为美国历史上第一位黑人总统,Obama的就职典礼引起强烈反响,导致Twitter流量猛增,如Figure 4 所示。

Figure 4. Twitter burst during the inauguration of Barack Obama, 1/20/2009, Tuesday
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/d594_4071879010_19fb519124_o.png

其中洪峰时刻,Twitter网站每秒钟收到350条新短信,这个流量洪峰维持了大约5分钟。根据统计,平均每个Twitter用户被120人“追”,这就 是说,这350条短信,平均每条都要发送120次 [16]。这意味着,在这5分钟的洪峰时刻,Twitter网站每秒钟需要发送350 x 120 = 42,000条短信。

面对洪峰,如何才能保证网站不崩溃?办法是迅速接纳,但是推迟服务。打个比方,在晚餐高峰时段,餐馆常常客满。对于新来的顾客,餐馆服务员不是拒之门外,而是让这些顾客在休息厅等待。这就是[14] 所说的 “隔离用户请求与相关操作,以便烫平流量高峰”。

如何实施隔离呢?当一位用户访问Twitter网站时,接待他的是Apache Web Server。Apache做的事情非常简单,它把用户的请求解析以后,转发给Mongrel Rails Sever,由Mongrel负责实际的处理。而Apache腾出手来,迎接下一位用户。这样就避免了在洪峰期间,用户连接不上Twitter网站的尴尬局面。

虽然Apache的工作简单,但是并不意味着Apache可以接待无限多的用户。原因是Apache解析完用户请求,并且转发给 Mongrel Server以后,负责解析这个用户请求的进程(process),并没有立刻释放,而是进入空循环,等待Mongrel Server返回结果。这样,Apache能够同时接待的用户数量,或者更准确地说,Apache能够容纳的并发的连接数量(concurrent connections),实际上受制于Apache能够容纳的进程数量。Apache系统内部的进程机制参见Figure 5,其中每个Worker代表一个进程。

Apache能够容纳多少个并发连接呢?[22]的实验结果是4,000个,参见Figure 6。如何才能提高Apache的并发用户容量呢?一种思路是不让连接受制于进程。不妨把连接作为一个数据结构,存放到内存中去,释放进程,直到 Mongrel Server返回结果时,再把这个数据结构重新加载到进程上去。

事实上Yaws Web Server[24],就是这么做的[23]。所以,Yaws能够容纳80,000以上的并发连接,这并不奇怪。但是为什么Twitter用 Apache,而不用Yaws呢?或许是因为Yaws是用Erlang语言写的,而Twitter工程师对这门新语言不熟悉 (But you need in house Erlang experience [17])。

Figure 5. Apache web server system architecture [21]
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/3b81_4071355801_db6c8cd6c0_o.png

Figure 6. Apache vs. Yaws.
The horizonal axis shows the parallel requests,
the vertical one shows the throughput (KBytes/second).
The red curve is Yaws, running on NFS.
The blue one is Apache, running on NFS,
while the green one is also Apache but on a local file system.
Apache dies at about 4,000 parallel sessions,
while Yaws is still functioning at over 80,000 parallel connections. [22]
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/8fa1_4072077210_3c3a507a8a_o.jpg

Reference,

[14] Improving running component of Twitter.
(http://qconlondon.com/london-2009/file?path=/qcon-london-
2009/slides/EvanWeaver_ImprovingRunningComponentsAtTwitter.pdf)
[16] Updating Twitter without service disruptions.
(http://gojko.net/2009/03/16/qcon-london-2009-upgrading-
twitter-without-service-disruptions/)
[17] Fixing Twitter. (http://assets.en.oreilly.com/1/event/29/Fixing_Twitter_
Improving_the_Performance_and_Scalability_of_the_World_s_Most_Popular_
Micro-blogging_Site_Presentation%20Presentation.pdf)
[21] Apache system architecture.
(http://www.fmc-modeling.org/download/publications/
groene_et_al_2002-architecture_recovery_of_apache.pdf)
[22] Apache vs Yaws. (http://www.sics.se/~joe/apachevsyaws.html)
[23] 质疑Apache和Yaws的性能比较. (http://www.javaeye.com/topic/107476)
[24] Yaws Web Server. (http://yaws.hyber.org/)
[25] Erlang Programming Language. (http://www.erlang.org/)

【5】数据流与控制流

通过让Apache进程空循环的办法,迅速接纳用户的访问,推迟服务,说白了是个缓兵之计,目的是让用户不至于收到“HTTP 503” 错误提示,“503错误” 是指 “服务不可用(Service Unavailable)”,也就是网站拒绝访问。

大禹治水,重在疏导。真正的抗洪能力,体现在蓄洪和泄洪两个方面。蓄洪容易理解,就是建水库,要么建一个超大的水库,要么造众多小水库。泄洪包括两个方面,1. 引流,2. 渠道。

对于Twitter系统来说,庞大的服务器集群,尤其是以MemCached为主的众多的缓存,体现了蓄洪的容量。引流的手段是Kestrel消息队列,用于传递控制指令。渠道是机器与机器之间的数据传输通道,尤其是通往MemCached的数据通道。渠道的优劣,在于是否通畅。

Twitter的设计,与大禹的做法,形相远,实相近。Twitter系统的抗洪措施,体现在有效地控制数据流,保证在洪峰到达时,能够及时把数据疏散到多个机器上去,从而避免压力过度集中,造成整个系统的瘫痪。

2009 年6月,Purewire公司通过爬Twitter网站,跟踪Twitter用户之间“追”与“被追”的关系,估算出Twitter用户总量在 7,000,000左右 [26]。在这7百万用户中,不包括那些既不追别人,也不被别人追的孤立用户。也不包括孤岛人群,孤岛内的用户只相互追与被追,不与外界联系。如果加上这 些孤立用户和孤岛用户群,目前Twitter的用户总数,或许不会超过1千万。

截止2009年3月,中国移动用户数已达 4.7亿户[27]。如果中国移动的飞信[28] 和139说客[29] 也想往Twitter方向发展,那么飞信和139的抗洪能力应该设计到多少呢?简单讲,需要把Twitter系统的现有规模,至少放大47倍。所以,有人 这样评论移动互联网产业,“在中国能做到的事情,在美国一定能做到。反之,不成立”。

但是无论如何,他山之石可以攻玉。这就是我们研究Twitter的系统架构,尤其是它的抗洪机制的目的。

Figure 7. Twitter internal flows
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/fe8f_4095392354_66bd4bcc30_o.png
下面举个简单的例子,剖析一下Twitter网站内部的流程,借此考察Twitter系统有哪些机制,去实现抗洪的三要素,“水库”,“引流”和“渠道”。
假设有两个作者,通过浏览器,在Twitter网站上发表短信。有一个读者,也通过浏览器,访问网站并阅读他们写的短信。

1. 作者的浏览器与网站建立连接,Apache Web Server分配一个进程(Worker Process)。作者登录,Twitter查找作者的ID,并作为Cookie,记忆在HTTP邮包的头属性里。

2. 浏览器上传作者新写的短信(Tweet),Apache收到短信后,把短信连同作者ID,转发给Mongrel Rails Server。然后Apache进程进入空循环,等待Mongrel的回复,以便更新作者主页,把新写的短信添加上去。

3. Mongrel收到短信后,给短信分配一个ID,然后把短信ID与作者ID,缓存到Vector MemCached服务器上去。

同时,Mongrel让Vector MemCached查找,有哪些读者“追”这位作者。如果Vector MemCached没有缓存这些信息,Vector MemCached自动去MySQL数据库查找,得到结果后,缓存起来,以备日后所需。然后,把读者IDs回复给Mongrel。

接着,Mongrel把短信ID与短信正文,缓存到Row MemCached服务器上去。

4. Mongrel通知Kestrel消息队列服务器,为每个作者及读者开设一个队列,队列的名称中隐含用户ID。如果Kestrel服务器中已经存在这些队列,那就延用以往的队列。

对应于每个短信,Mongrel已经从Vector MemCached那里知道,有哪些读者追这条短信的作者。Mongrel把这条短信的ID,逐个放进每位读者的队列,以及作者本人的队列。

5. 同一台Mongrel Server,或者另一台Mongrel Server,在处理某个Kestrel队列中的消息前,从这个队列的名称中解析出相应的用户ID,这个用户,既可能是读者,也可能是作者。

然后Mongrel从Kestrel队列中,逐个提取消息,解析消息中包含的短信ID。并从Row MemCached缓存器中,查找对应于这个短信ID的短信正文。

这时,Mongrel既得到了用户的ID,也得到了短信正文。接下去Mongrel就着手更新用户的主页,添加上这条短信的正文。

6. Mongrel把更新后的作者的主页,传递给正在空循环的Apache的进程。该进程把作者主页主动传送(push)给作者的浏览器。

如果读者的浏览器事先已经登录Twitter网站,建立连接,那么Apache给该读者也分配了一个进程,该进程也处于空循环状态。Mongrel把更新后的读者的主页,传递给相应进程,该进程把读者主页主动传递给读者的浏览器。

咋一看,流程似乎不复杂。“水库”,“引流”和“渠道”,这抗洪三要素体现在哪里呢?盛名之下的Twitter,妙处何在?值得细究的看点很多。

Reference,

[26] Twitter user statistics by Purewire, June 2009.
(http://www.nickburcher.com/2009/06/twitter-user-statistics-purewire-report.html)
[27] 截止2009年3月,中国移动用户数已达4.7亿户.
(http://it.sohu.com/20090326/n263018002.shtml)
[28] 中国移动飞信网. (http://www.fetion.com.cn/)
[29] 中国移动139说客网. (http://www.139.com/)

【6】流量洪峰与云计算

上一篇历数了一则短信从发表到被阅读,Twitter业务逻辑所经历的6个步骤。表面上看似乎很乏味,但是细细咀嚼,把每个步骤展开来说,都有一段故事。

美国年度橄榄球决赛,绰号超级碗(Super Bowl)。Super Bowl在美国的收视率,相当于中国的央视春节晚会。2008年2月3日,星期天,该年度Super Bowl如期举行。纽约巨人队(Giants),对阵波士顿爱国者队(Patriots)。这是两支实力相当的球队,决赛结果难以预料。比赛吸引了近一亿美国人观看电视实况转播。

对于Twitter来说,可以预料的是,比赛进行过程中,Twitter流量必然大涨。比赛越激烈,流量越高涨。Twitter无法预料的是,流量究竟会涨到多少,尤其是洪峰时段,流量会达到多少。

根据[31]的统计,在Super Bowl比赛进行中,每分钟的流量与当日平均流量相比,平均高出40%。在比赛最激烈时,更高达150%以上。与一周前,2008年1月27日,一个平静的星期天的同一时段相比,流量的波动从平均10%,上涨到40%,最高波动从35%,上涨到150%以上。

Figure 8. Twitter traffic during Super Bowl, Sunday, Feb 3, 2008 [31]. The blue line represents the percentage of updates per minute during the Super Bowl normalized to the average number of updates per minute during the rest of the day, with spikes annotated to show what people were twittering about. The green line represents the traffic of a “regular” Sunday, Jan 27, 2008.
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/d4fa_4085122087_970072e518_o.png

由此可见,Twitter流量的波动十分可观。对于Twitter公司来说,如果预先购置足够的设备,以承受流量的变化,尤其是重大事件导致的洪峰流量,那么这些设备在大部分时间处于闲置状态,非常不经济。但是如果缺乏足够的设备,那么面对重大事件,Twitter系统有可能崩溃,造成的后果是用户流失。

怎么办?办法是变买为租。Twitter公司自己购置的设备,其规模以应付无重大事件时的流量压力为限。同时租赁云计算平台公司的设备,以应付重大事件来临时的洪峰流量。租赁云计算的好处是,计算资源实时分配,需求高的时候,自动分配更多计算资源。

Twitter公司在2008年以前,一直租赁Joyent公司的云计算平台。在2008年2月3日的Super Bowl即将来临之际,Joyent答应Twitter,在比赛期间免费提供额外的计算资源,以应付洪峰流量[32]。但是诡异的是,离大赛只剩下不到4天,Twitter公司突然于1月30日晚10时,停止使用Joyent的云计算平台,转而投奔Netcraft [33,34]。

Twitter弃Joyent,投Netcraft,其背后的原因是商务纠葛,还是担心Joyent的服务不可靠,至今仍然是个谜。

变买为租,应对洪峰,这是一个不错的思路。但是租来的计算资源怎么用,又是一个大问题。查看一下[35],不难发现Twitter把租赁来的计算资源,大部分用于增加Apache Web Server,而Apache是Twitter整个系统的最前沿的环节。

为什么Twitter很少把租赁来的计算资源,分配给Mongrel Rails Server,MemCached Servers,Varnish HTTP Accelerators等等其它环节?在回答这个问题以前,我们先复习一下前一章“数据流与控制流”的末尾,Twitter从写到读的6个步骤。

这6个步骤的前2步说到,每个访问Twitter网站的浏览器,都与网站保持长连接。目的是一旦有人发表新的短信,Twitter网站在500ms以内,把新短信push给他的读者。问题是在没有更新的时候,每个长连接占用一个Apache的进程,而这个进程处于空循环。所以,绝大多数Apache进程,在绝大多数时间里,处于空循环,因此占用了大量资源。

事实上,通过Apache Web Servers的流量,虽然只占Twitter总流量的10%-20%,但是Apache却占用了Twitter整个服务器集群的50%的资源[16]。所以,从旁观者角度来看,Twitter将来势必罢黜Apache。但是目前,当Twitter分配计算资源时,迫不得已,只能优先保证Apache的需求。

迫不得已只是一方面的原因,另一方面,也表明Twitter的工程师们,对其系统中的其它环节,太有信心了。

在第四章“抗洪需要隔离”中,我们曾经打过一个比方,“在晚餐高峰时段,餐馆常常客满。对于新来的顾客,餐馆服务员不是拒之门外,而是让这些顾客在休息厅等待”。对于Twitter系统来说,Apache充当的角色就是休息厅。只要休息厅足够大,就能暂时稳住用户,换句行话讲,就是不让用户收到HTTP-503的错误提示。

稳住用户以后,接下去的工作是高效率地提供服务。高效率的服务,体现在Twitter业务流程6个步骤中的后4步。为什么Twitter对这4步这么有信心?

Reference,

[16] Updating Twitter without service disruptions.
(http://gojko.net/2009/03/16/qcon-london-2009-upgrading-twitter-without-service-disruptions/)
[30] Giants and Patriots draws 97.5 million US audience to the Super Bowl. (http://www.reuters.com/article/topNews/idUSN0420266320080204)
[31] Twitter traffic during Super Bowl 2008.
(http://blog.twitter.com/2008/02/highlights-from-superbowl-sunday.html)
[32] Joyent provides Twitter free extra capacity during the Super Bowl 2008.
(http://blog.twitter.com/2008/01/happy-happy-joyent.html)
[33] Twitter stopped using Joyent’s cloud at 10PM, Jan 30, 2008. (http://www.joyent.com/joyeurblog/2008/01/31/twitter-and-joyent-update/)
[34] The hasty divorce for Twitter and Joyent.
(http://www.datacenterknowledge.com/archives/2008/01/31/hasty-divorce-for-twitter-joyent/)
[35] The usage of Netcraft by Twitter.
(http://toolbar.netcraft.com/site_report?url=http://twitter.com)

【7】作为一种进步的不彻底

不彻底的工作方式,对于架构设计是一种进步。

当一个来自浏览器的用户请求到达Twitter后台系统的时候,第一个迎接它的,是Apache Web Server。第二个出场的,是Mongrel Rails Server。Mongrel既负责处理上传的请求,也负责处理下载的请求。Mongrel处理上传和下载的业务逻辑非常简洁,但是简洁的表象之下,却蕴含着反常规的设计。这种反常规的设计,当然不是疏忽的结果,事实上,这正是Twitter架构中,最值得注意的亮点。

Figure 9. Twitter internal flows
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/fe8f_4095392354_66bd4bcc30_o.png
所谓上传,是指用户写了一个新短信,上传给Twitter以便发表。而下载,是指Twitter更新读者的主页,添加最新发表的短信。Twitter下载的方式,不是读者主动发出请求的pull的方式,而是Twitter服务器主动把新内容push给读者的方式。先看上传,Mongrel处理上传的逻辑很简洁,分两步。

1. 当Mongrel收到新短信后,分配一个新的短信ID。然后把新短信的ID,连同作者ID,缓存进Vector MemCached服务器。接着,把短信ID以及正文,缓存进Row MemCached服务器。这两个缓存的内容,由Vector MemCached与Row MemCached在适当的时候,自动存放进MySQL数据库中去。

2. Mongrel在Kestrel消息队列服务器中,寻找每一个读者及作者的消息队列,如果没有,就创建新的队列。接着,Mongrel把新短信的ID,逐个放进“追”这位作者的所有在线读者的队列,以及作者本人的队列。

品味一下这两个步骤,感觉是Mongrel的工作不彻底。一,把短信及其相关IDs,缓存进Vector MemCached和Row Cached就万事大吉,而不直接负责把这些内容存入MySQL数据库。二,把短信ID扔进Kestrel消息队列,就宣告上传任务结束。Mongrel 没有用任何方式去通知作者,他的短信已经被上传。也不管读者是否能读到新发表的短信。

为什么Twitter采取了这种反常规的不彻底的工作方式?回答这个问题以前,不妨先看一看Mongrel处理下载的逻辑。把上传与下载两段逻辑联系起来,对比一下,有助于理解。Mongrel下载的逻辑也很简单,也分两步。

1. 分别从作者和读者的Kestrel消息队列中,获得新短信的ID。

2. 从Row MemCached缓存器那里获得短信正文。以及从Page MemCached那里获得读者以及作者的主页,更新这些主页,也就是添加上新的短信的正文。然后通过Apache,push给读者和作者。

对照Mongrel处理上传和下载的两段逻辑,不难发现每段逻辑都“不彻底”,合在一起才形成一个完整的流程。所谓不彻底的工作方式,反映了 Twitter架构设计的两个“分”的理念。一,把一个完整的业务流程,分割成几段相对独立的工作,每一个工作由同一台机器中不同的进程负责,甚至由不同的机器负责。二,把多个机器之间的协作,细化为数据与控制指令的传递,强调数据流与控制流的分离。

分割业务流程的做法,并不是Twitter的首创。事实上,三段论的架构,宗旨也是分割流程。Web Server负责HTTP的解析,Application Server负责业务逻辑,Database负责数据存储。遵从这一宗旨,Application Server的业务逻辑也可以进一步分割。

1996年,发明TCL语言的前伯克利大学教授John Ousterhout,在Usenix大会上做了一个主题演讲,题目是“为什么在多数情况下,多线程是一个糟糕的设计[36]”。2003年,同为伯克利大学教授的Eric Brewer及其学生们,发表了一篇题为“为什么对于高并发服务器来说,事件驱动是一个糟糕的设计[37]”。这两个伯克利大学的同事,同室操戈,他们在争论什么?

所谓多线程,简单讲就是由一根线程,从头到尾地负责一个完整的业务流程。打个比方,就像修车行的师傅每个人负责修理一辆车。而所谓事件驱动,指的是把一个完整的业务流程,分割成几个独立工作,每个工作由一个或者几个线程负责。打个比方,就像汽车制造厂里的流水线,有多个工位组成,每个工位由一位或者几位工人负责。

很显然,Twitter的做法,属于事件驱动一派。事件驱动的好处在于动态调用资源。当某一个工作的负担繁重,成为整个流程中的瓶颈的时候,事件驱动的架构可以很方便地调集更多资源,来化解压力。对于单个机器而言,多线程和事件驱动的两类设计,在性能方面的差异,并不是非常明显。但是对于分布式系统而言,事件驱动的优势发挥得更为淋漓尽致。

Twitter把业务流程做了两次分割。一,分离了Mongrel与MySQL数据库,Mongrel不直接插手MySQL数据库的操作,而是委托MemCached全权负责。二,分离了上传和下载两段逻辑,两段逻辑之间通过Kestrel队列来传递控制指令。

在John Ousterhout和Eric Brewer两位教授的争论中,并没有明确提出数据流与控制流分离的问题。所谓事件,既包括控制信号,也包括数据本身。考虑到通常数据的尺寸大,传输成本高,而控制信号的尺寸小,传输简便。把数据流与控制流分离,可以进一步提高系统效率。

在Twitter系统中,Kestrel消息队列专门用来传输控制信号,所谓控制信号,实际上就是IDs。而数据是短信正文,存放在Row MemCached中。谁去处理这则短信正文,由Kestrel去通知。

Twitter完成整个业务流程的平均时间是500ms,甚至能够提高到200-300ms,说明在Twitter分布式系统中,事件驱动的设计是成功。

Kestrel消息队列,是Twitter自行开发的。消息队列的开源实现很多,Twitter为什么不用现成的免费工具,而去费神自己研发呢?

Reference,

[36] Why threads are a bad idea (for most purposes), 1996.
(http://www.stanford.edu/class/cs240/readings/threads-bad-usenix96.pdf)
[37] Why events are a bad idea (for high-concurrency servers), 2003.
(http://www.cs.berkeley.edu/~brewer/papers/threads-hotos-2003.pdf)

【8】 得过不且过

北京西直门立交桥的设计,经常遭人诟病。客观上讲,对于一座立交桥而言,能够四通八达,就算得上基本完成任务了。大家诟病的原因,主要是因为行进路线太复杂。

当然,站在设计者角度讲,他们需要综合考虑来自各方面的制约。但是考虑到世界上立交桥比比皆是,各有各的难处,然而像西直门立交桥这样让人迷惑的,还真是少见。所以,对于西直门立交桥的设计者而言,困难是客观存在的,但是改进的空间总还是有的。

Figure 10. 北京西直门立交桥行进路线
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/ef82_4113112287_86cfb1cffd_o.png

大型网站的架构设计也一样,沿用传统的设计,省心又省力,但是代价是网站的性能。网站的性能不好,用户的体验也不好。Twitter这样的大型网站之所以能够一飞冲天,不仅功能的设计迎合了时代的需要,同时,技术上精益求精也是成功的必要保障。

例如,从Mongrel到MemCached之间,需要一个数据传输通道。或者严格地说,需要一个client library communicating to the memcached server。Twitter的工程师们,先用Ruby实现了一个通道。后来又用C实现了一个更快的通道。随后,不断地改进细节,不断地提升数据传输的效率。这一系列的改进,使Twitter的运行速度,从原先不设缓存时,每秒钟处理3.23个请求,到现在每秒处理139.03个请求,参见Figure 11。这个数据通道,现在定名为libmemcached,是开源项目 [38]。

Figure 11. Evolving from a Ruby memcached client to a C client with optimised hashing. These changes increases Twitter performance from 3.23 requests per second without caching, to 139.03 requests per second nowadays [14].
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/7666_4115077218_55c7250d43_o.png

又例如,Twitter系统中用消息队列来传递控制信号。这些控制信号,从插入队列,到被删除,生命周期很短。短暂的生命周期,意味着消息队列的垃圾回收(Garbage Collection)的效率,会严重影响整个系统的效率。因此,改进垃圾回收的机制,不断提高效率,成为不可避免的问题。Twitter使用的消息队列,原先不是Kestrel,而是用Ruby编写的一个简单的队列工具。但是如果继续沿用Ruby这种语言,性能优化的空间不大。Ruby的优点是集成了很多功能,从而大大减少了开发过程中编写程序的工作量。但是优点也同时是缺点,集成的功能太多,拖累也就多,牵一发而动全身,造成优化困难。

Twitter工程师戏言,”Ruby抗拒优化”,(“Ruby is optimization resistant”, by Evan Weaver [14])。几经尝试以后,Twitter的工程师们最终放弃了Ruby语言,改用Scala语言,自行实现了一个队列,命名为Kestrel [39]。

改换语言的主要动机是,Scala运行在JVM之上,因此优化Garbage Collection性能的手段丰富。Figure 12. 显示了使用Kestrel以后,垃圾回收的滞后,在平时只有2ms,最高不超过4ms。高峰时段,平均滞后5ms,最高不超过35ms。

Figure 12. The latency of Twitter Kestrel garbage collection [14].
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/2f0a_4115072726_c611955bb2_o.png

RubyOnRails逐渐淡出Twitter,看来这是大势所趋。最后一步,也是最高潮的一步,可能是替换Mongrel。事实上,Twitter所谓“API Server”,很可能是他们替换Mongrel的前奏。

Twitter的Evan Weaver说,“API Server”的运行效率,比Apache+Mongrel组合的速度快4倍。所谓Apache+Mongrel组合,是RubyOnRails的一种实现方式。Apache+Mongrel组合,每秒能够处理139个请求,参见Figure 11,而“API Server” 每秒钟能够处理大约550个请求 [16]。换句话说,使用Apache+Mongrel组合,优点是降低了工程师们写程序的负担,但是代价是系统性能降低了4倍,换句话说,用户平均等待的时间延长了4倍。

活着通常不难,活得精彩永远很难。得过不且过,这是一种精神。

Reference,
[14] Improving running component of Twitter.
(http://qconlondon.com/london-2009/file?path=/qcon-london-2009/slides/EvanWeaver_ImprovingRunningComponentsAtTwitter.pdf)
[16] Updating Twitter without service disruptions.
(http://gojko.net/2009/03/16/qcon-london-2009-upgrading-twitter-without-service-disruptions/)
[38] Open source project, libmemcached, by Twitter.
(http://tangent.org/552/libmemcached.html)
[39] Open source project, Kestrel Messaging Queue, by Twitter.
(http://github.com/robey/kestrel)

【9】结语

这个系列讨论了Twitter架构设计,尤其是cache的应用,数据流与控制流的组织等等独特之处。把它们与抗洪抢险中,蓄洪,引流,渠道三种手段相对比,便于加深理解。同时参考实际运行的结果,验证这样的设计是否能够应付实际运行中遇到的压力。

解剖一个现实网站的架构,有一些难度。主要体现在相关资料散落各处,而且各个资料的视点不同,覆盖面也不全。更严重的问题是,这些资料不是学术论文,质量良莠不齐,而且一些文章或多或少地存在缺失,甚至错误。

单纯把这些资料罗列在一起,并不能满足全景式的解剖的需要。整理这些资料的过程,很像是侦探办案。福尔摩斯探案的方法,是证据加推理。

1. 如果观察到证据O1,而造成O1出现的原因,有可能是R1,也有可能是R2或者R3。究竟哪一个原因,才是真正的原因,需要进一步收集更多的证据,例如O2,O3。如果造成O2 出现的可能的原因是R2和R4,造成O3 出现的可能原因是R3和R5。把所有证据O1 O2 O3,综合起来考虑,可能性最大的原因必然是(R1,R2,R3), (R2,R4), (R3,R5) 的交集,也就是R2。这是反绎推理的过程。

2. 如果反绎推理仍然不能确定什么是最可能的原因,那么假定R2是真实的原因,采用演绎推理,R2必然导致O4证据的出现。接下去要做的事情是,确认O4是否真的出现,或者寻找O4肯定不会出现的证据。以此循环。

解剖网络架构的方法,与探案很相似。只读一篇资料是不够的,需要多多收集资料,交叉印证。不仅交叉印证,而且引申印证,如果某一环节A是这样设计的,那么关联环节B必然相应地那样设计。如果一时难以确定A到底是如何设计的,不妨先确定B是如何设计的。反推回来,就知道A应该如何设计了。

解剖网站架构,不仅有益,而且有趣。

Figure 13. Sherlock Holmes,福尔摩斯探案
Courtesy http://alibuybuy-img1011.stor.sinaapp.com/2010/11/be50_c2053.jpg

【全文完】

本文来自:http://www.tektalk.org

原文链接:

  1. 解剖Twitter 【1】万事开头易
  2. 解剖Twitter 【2】三段论
  3. 解剖Twitter 【3】Cache == Cash
  4. 解剖Twitter 【4】抗洪需要隔离
  5. 解剖Twitter 【5】数据流与控制流
  6. 解剖Twitter【6】流量洪峰与云计算
  7. 解剖Twitter 【7】 作为一种进步的不彻底
  8. 解剖Twitter 【8】 得过不且过
  9. 解剖Twitter 【9】结语

数据仓库的基本架构

没人带,自学慢,不在BAT怎么学产品?人人都是产品经理联合200+BAT资深产品经理带你学 点此查看详情

数据仓库的目的是构建面向分析的集成化数据环境,为企业提供决策支持(Decision-Support)。其实数据仓库本身并不“生产”任何数据,同时自身也不需要“消费”任何的数据,数据来源于外部,并且开放给外部应用,这也是为什么叫“仓库”,而不叫“工厂”的原因。因此数据仓库的基本架构主要包含的是数据流入流出的过程,可以分为三层——源数据数据仓库数据应用

data-warehouse-frame

从图中可以看出数据仓库的数据来源于不同的源数据,并提供多样的数据应用,数据自上而下流入数据仓库后向上层开放应用,而数据仓库只是中间集成化数据管理的一个平台。

数据仓库从各数据源获取数据及在数据仓库内的数据转换和流动都可以认为是ETL(抽取Extra, 转化Transfer, 装载Load)的过程,ETL是数据仓库的流水线,也可以认为是数据仓库的血液,它维系着数据仓库中数据的新陈代谢,而数据仓库日常的管理和维护工作的大部分精力就是保持ETL的正常和稳定。

下面主要简单介绍下数据仓库架构中的各个模块,当然这里所介绍的数据仓库主要是指网站数据仓库。

数据仓库的数据来源

其实之前的一篇文章已经介绍过数据仓库各种源数据的类型——数据仓库的源数据类型,所以这里不再详细介绍。

对于网站数据仓库而言,点击流日志是一块主要的数据来源,它是网站分析的基础数据;当然网站的数据库数据也并不可少,其记录这网站运营的数据及各种用户操作的结果,对于分析网站Outcome这类数据更加精准;其他是网站内外部可能产生的文档及其它各类对于公司决策有用的数据。

数据仓库的数据存储

dw-data-storage
源数据通过ETL的日常任务调度导出,并经过转换后以特性的形式存入数据仓库。其实这个过程一直有很大的争议,就是到底数据仓库需不需要储存细节数据,一方的观点是数据仓库面向分析,所以只要存储特定需求的多维分析模型;另一方的观点是数据仓库先要建立和维护细节数据,再根据需求聚合和处理细节数据生成特定的分析模型。我比较偏向后面一个观点:数据仓库并不需要储存所有的原始数据,但数据仓库需要储存细节数据,并且导入的数据必须经过整理和转换使其面向主题。简单地解释下:
(1).为什么不需要所有原始数据?数据仓库面向分析处理,但是某些源数据对于分析而言没有价值或者其可能产生的价值远低于储存这些数据所需要的数据仓库的实现和性能上的成本。比如我们知道用户的省份、城市足够,至于用户究竟住哪里可能只是物流商关心的事,或者用户在博客的评论内容可能只是文本挖掘会有需要,但将这些冗长的评论文本存在数据仓库就得不偿失;

(2).为什么要存细节数据?细节数据是必需的,数据仓库的分析需求会时刻变化,而有了细节数据就可以做到以不变应万变,但如果我们只存储根据某些需求搭建起来的数据模型,那么显然对于频繁变动的需求会手足无措;

(3).为什么要面向主题?面向主题是数据仓库的第一特性,主要是指合理地组织数据以方面实现分析。对于源数据而言,其数据组织形式是多样的,像点击流的数据格式是未经优化的,前台数据库的数据是基于OLTP操作组织优化的,这些可能都不适合分析,而整理成面向主题的组织形式才是真正地利于分析的,比如将点击流日志整理成页面(Page)、访问(Visit或Session)、用户(Visitor)三个主题,这样可以明显提升分析的效率。

数据仓库基于维护细节数据的基础上在对数据进行处理,使其真正地能够应用于分析。主要包括三个方面:

数据的聚合

这里的聚合数据指的是基于特定需求的简单聚合(基于多维数据的聚合体现在多维数据模型中),简单聚合可以是网站的总Pageviews、Visits、Unique Visitors等汇总数据,也可以是Avg. time on page、Avg. time on site等平均数据,这些数据可以直接地展示于报表上。

多维数据模型

多维数据模型提供了多角度多层次的分析应用,比如基于时间维、地域维等构建的销售星形模型、雪花模型,可以实现在各时间维度和地域维度的交叉查询,以及基于时间维和地域维的细分。所以多维数据模型的应用一般都是基于联机分析处理(Online Analytical Process, OLAP)的,而面向特定需求群体的数据集市也会基于多维数据模型进行构建。

业务模型

这里的业务模型指的是基于某些数据分析和决策支持而建立起来的数据模型,比如我之前介绍过的用户评价模型、关联推荐模型、RFM分析模型等,或者是决策支持的线性规划模型、库存模型等;同时,数据挖掘中前期数据的处理也可以在这里完成。

数据仓库的数据应用

dw-data-application
之前的一篇文章——数据仓库的价值中介绍过数据仓库的四大特性上的价值体现,但数据仓库的价值远不止这样,而且其价值真正的体现是在数据仓库的数据应用上。图中罗列的几种应用并未包含所有,其实一切基于数据相关的扩展性应用都可以基于数据仓库来实现。

报表展示

报表几乎是每个数据仓库的必不可少的一类数据应用,将聚合数据和多维分析数据展示到报表,提供了最为简单和直观的数据。

即席查询

理论上数据仓库的所有数据(包括细节数据、聚合数据、多维数据和分析数据)都应该开放即席查询,即席查询提供了足够灵活的数据获取方式,用户可以根据自己的需要查询获取数据,并提供导出到Excel等外部文件的功能。

数据分析

数据分析大部分可以基于构建的业务模型展开,当然也可以使用聚合的数据进行趋势分析、比较分析、相关分析等,而多维数据模型提供了多维分析的数据基础;同时从细节数据中获取一些样本数据进行特定的分析也是较为常见的一种途径。

数据挖掘

数据挖掘用一些高级的算法可以让数据展现出各种令人惊讶的结果。数据挖掘可以基于数据仓库中已经构建起来的业务模型展开,但大多数时候数据挖掘会直接从细节数据上入手,而数据仓库为挖掘工具诸如SAS、SPSS等提供数据接口。

元数据管理

元数据(Meta Date),其实应该叫做解释性数据,即数据的数据。主要记录数据仓库中模型的定义、各层级间的映射关系、监控数据仓库的数据状态及ETL的任务运行状态。一般会通过元数据资料库(Metadata Repository)来统一地存储和管理元数据,其主要目的是使数据仓库的设计、部署、操作和管理能达成协同和一致。

最后做个Ending,数据仓库本身既不生产数据也不消费数据,只是作为一个中间平台集成化地存储数据;数据仓库实现的难度在于整体架构的构建及ETL的设计,这也是日常管理维护中的重头;而数据仓库的真正价值体现在于基于其的数据应用上,如果没有有效的数据应用也就失去了构建数据仓库的意义。
VIA:网络