当当弹性化中间件及云化之路(据说读完可以少踩坑)

Markdown

6月24日,双态运维·乌镇峰会-数人云专题研讨会上,张亮老师从业务、中间件、云化的方向出发,分享了当当网实践落地容器的经验。数人云掐指一算,稀缺好文,宜收藏和分享!

今天主分享的内容分为三部分——

第一部分:当当业务体系简介。受限于时间,只做简单概述。

第二部分:介绍当当的弹性化中间件。现阶段,无状态容器已趋于成熟,需要进一步开发和优化的是运行在容器里面的部分,即中间件。

第三部分:当当的云化之路。

业务特征

Markdown

首先,介绍下当当的业务特征,这也是互联网行业的共有特征:

海量用户 当当面向的是完全开放的、整个互联网的亿级用户量。

品类繁多 当当是卖书起家的,书的品类是很多的。除了书,当当还有百货、服装等。与垂直电商不同,水平电商由于品类繁多,因此数据架构也会有很大区别。

7X24 互联网公司基本都是如此要求,尽量缩短宕机时间。

流量突增 如“双11”或“书香节”,其流量是平时的几倍到几十倍不等。

业务复杂 下图是业务框架图,分为三部分,中间部分是当当主业务流程,从用户查看选择商品的卖场模块,到购物车、结算的交易模块,最后发至订单工作流系统;然后通过仓储系统生产订单,并交由物流系统配送货品。上面部分是用户相关的系统,如搜索、推荐、促销、会员等。下面部分是为当当运营人员与合作伙伴提供的系统,如商品、价格、库存等。

Markdown

这仅是缩略版的系统架构图,当当实际系统要复杂的多、用到的语言也各异,大部分如:前端PHP、后端Java、搜索系统用C++,推荐系统用Go和Python。因此它是一个由异构语言组成的复杂系统集合。

业务特征·互联网架构核心问题

Markdown

互联网核心问题和企业级开发不一样的地方在于规模。互联网公司的规模由以下几点组成的——

海量数据:包括但不限于用户数据、商品数据、交易数据、订单数据、用户行为数据等。由于数据量巨大,因此不能仅使用单一的数据存储结构,也不能以单数据节点的方式存储所有数据。

响应迟缓:大量的用户访问,使得系统承载了更多流量,处理过多的请求会导致系统响应变的迟缓。

系统繁多:通过上一张图可以得知,系统是由非常多的子系统组成的,每个子系统各司其职。合理的系统拆分可以灵活的分离冷热数据、扩容重点系统等。

开发困难:各个系统都是由异构语言的不同技术栈构成,开发成本较高。

稳定性差:由于系统间交互增多,系统之间会形成一个复杂的交互网。任一系统出现问题,都可能导致部分不可用,进而增加整体不可控的风险。系统拆分的越多,出问题的可能性就越大,系统稳定性就会越差。

伸缩性差:电商公司不可能常备“双11”的机器体量。因此遇到大促等需要承载突发流量的场景,系统需要可以弹性伸缩。在流量增加的时候扩张容量,用于承接更多的购买需求;在流量下降的时候收缩容量,节省成本。

中间件·解决方案=中间件+云平台

Markdown

解决方案是中间件+云平台。

中间件解决的主要问题是服务化、弹性化和异步化。

  • 服务化:众多系统间应该提供一致的交互和治理方式。
  • 弹性化:让系统具备根据实际需要灵活的扩缩容的能力。
  • 异步化:将同步调用链梳理为同步落盘 + 异步处理的方式以提升吞吐量。

云平台解决的主要问题是部署自动化和监控一体化。

中间件缺乏对运行环境搭建,App部署分发等能力,也难以提供统一的监控一体化系统,因此云平台在这方面是对中间件的有益补充。

中间件·基础3件套

Markdown

这里介绍最主要的三个中间件:服务中间件作业中间件数据中间件。中间件远远不止这三种,限于时间,无法涵盖全部的中间件:如消息中间件、缓存中间件、NoSQL以及离线大数据等因时间关系不在分享范围之内。

中间件·服务中间件

Markdown

服务中间件有很多优秀的开源产品,从早期的Finagle, Dubbo,到近期出现的Motan,Spring Cloud都是个中翘楚。服务中间件的核心功能主要包括:

远程调用:分为长连接调用和短连接调用两种方式。长链接采用Socket + 二进制序列化的方式居多,短连接以HTTP RESTFul + JSON的方式为主。无论采用何种调用方式,都应在服务中间件中封装为统一接口。

服务发现:自动感知上线和下线的应用,并分配和截断相应的请求。标准实现方案是通过一个注册中心管理和协调分布式应用,常见的注册中心有Zookeeper,etcd和Eureka。

负载均衡:合理的将流量分配给权重不尽相同的分布式节点。

服务治理:包括服务调动链梳理、服务降级、服务版本控制等功能。

限流:将过载的流量挡在后端系统之外,让部分不过载的请求可以继续提供服务。保证系统不会因为突增的流量而被完全冲垮,而是任何情况下都能提供对核心用户的平稳服务能力。

监控报警:提供将内部指标通过API对接到外部系统的能力,内部指标一般有SLA、服务状态、节点承载量等。

Markdown

上图是当当采用的服务框架——DubboX。

该项目Fork了阿里开源的Dubbo,并内置了Web服务器且增加REST协议,用于支持异构语言间的调用。每个基于DubboX的应用均可通过内置的Web服务器提供服务,每个Web服务器通过Nginx实现负载均衡。并且在Nginx通过OpenResty实现了基于漏桶和令牌桶两种算法的限流插件。请求调用信息通过Agent的本地汇总之后,定期发送至一个Kafka的消息队列,并通过Storm的二次汇总计算出SLA,存储至数据库中。最终由监控系统定期抓取报警数据。

使用DubboX部署的程序,在逻辑上分为两部分。上面部分是服务的提供者;下面部分是服务的消费者。服务消费者在REST协议的场景,是直接通过Nginx代理访问服务提供者的。如果采用Java同构应用,可以使用Dubbo原生的长链接+Dubbo协议,并通过注册中心服务发现,以及客户端负载均衡。

下图是当当的SLA监控系统以及限流系统的Dashboard。

Markdown Markdown

中间件·作业中间件

当当系统中很多业务是由作业实现的,如拉单作业、价格同步作业等。因为公司系统较多,很难通过唯一的方案实现,因此也有部分系统通过消息中间件完成。作业中间件的核心功能主要包括:

  • 定时调度:根据cron表达式的时间调度应用。

  • 任务分片:将一个大任务拆分成为多个任务片段,分布运行。此功能后文会重点介绍。

  • 弹性扩容:与任务分片息息相关,一并在后文中介绍。

  • 作业治理:管控作业生命周期,如:执行、禁用、启用以及更复杂的行为,如:失效转移、错过任务重触发等。

  • 监控报警:提供将作业运行状态、历史统计和查询对接至外部系统的能力。

Markdown

当当采用的作业中间件是自研的Elastic-Job,它可以将一个完整的作业拆分为多个相互独立的任务。一个完整的作业运行时间可能较长,但很难通过增加机器实例提升运行效率。通过Elastic-Job将作业拆分为多个任务,可以并行的在分布式的环境中运行,提升其处理速度。用户可以实现自己的分片策略,将任务分配至合适的节点运行。

通过上图举例,作业分为4片,由两台服务器执行,每台执行两片。当增加一台服务器时,如下图所示,分片项被稀释为服务器1和3各执行一片,服务器2执行两片,那么服务器1和3由于执行的分片项减少,从而提升性能。

Markdown Markdown

而一旦有服务器宕机,如下图所示,仅剩余一台服务器可以提供服务,那么所有的分片都将指向该服务器。集群的整体处理能力会下降,但作业的完整性不会受到影响,从而提供高可用的能力。

Markdown

因此,随着运行实例的增加和减少,Elastic-Job可以动态的调整分片来提升性能和保证可用性。

Markdown

这是Elastic-Job的部署架构图。

中间件·数据库中间件

Markdown

接下来介绍的是数据库中间件。关系型数据库在大数据量的情况下,单库单表难以支撑。解决方案是将单一的数据库拆分为分布式数据库,而让开发和运维人员像访问一个数据库一样访问分布式数据库,屏蔽其复杂度,是数据库中间件的基本功能。数据库中间件的核心功能主要包括:

分库分表:通过打散数据的方式有效的减少每个表的数据量,减少索引的深度以提升查询性能。并且拆分数据库来有效的疏导请求流量。

读写分离:进一步提升数据库的性能可以采用读写分离。读库仅负责响应查询,写库相应更新,通过异步数据同步的方式保持读写库的数据一致性。这种方式可以有效的减低行锁,进一步提升效率。

内联事务:数据库一旦打散,就必须使用分布式事务而导致性能急剧下降。因此如何合理的分库分表,尽量将操作保证落在同一个库,而使用内联事务,是更好的选择。

柔性事务:在内联事务不适用的跨库场景,牺牲强一致性来换取性能的提升,然后采用异步补偿的机制来达到最终一致性,是柔性事务的核心理念。

SQL审核:先期过滤出不符合OLTP的非法SQL。如:DELETE语句没有WHERE等。

Markdown

当当采用自研的Sharding-JDBC作为数据库中间层。它直接修改JDBC协议,因此可以兼容各种数据库以及ORM框架,应用工程师几乎没有学习成本,和使用原生JDBC没有区别。应用工程师仅需要配置分片规则,用于告诉Sharding-JDBC哪一个分片键的数据应该路由至哪个库的哪个表,即可。

Sharding-JDBC的内部结构包括:JDBC规范重写、SQL解析、 SQL改写、SQL路由、SQL执行以及结果集归并。

Markdown Markdown

上面两个图是Sharding-JDBC的性能测试报告,在单库时,由于SQL解析带来的损耗,Sharding-JDBC比原生JDBC慢了0.02%。而将数据拆分至双库,Sharding-JDBC比原生JDBC的性能提升了94%,因此,拆分的数据库实例越多,其对性能的提升也越显著。

Markdown

Sharding-JDBC定位是面向在线业务的框架。因此OLTP涉及到的SQL基本都兼容。

Markdown

分布式的事务处理方案有三种:XA,弱XA和柔性事务。

XA即分布式事务,他对业务代码完全没有侵入性,而且也可以保证分布式场景下事务的强一致性,但其性能低下,在互联网的场景下非常不适用。

弱XA是分库分表数据库的中间层所提出来的概念,简单说就是单片事务。它仅能控制单节点事务的一致性,对于分布式的事务完全没有控制能力。他不会对性能带来任何影响,但没有一致性的能力,在分布式的场景下极易造成数据不一致。

柔性事务是对弱XA的补充。它使用异步补偿的方式,将短期内不一致的数据修复,达到最终一致性。它用牺牲强一致性的方式提升了性能,因此内联事务 + 柔性事务成为了最受青睐的互联网事务处理方案。

Markdown

柔性事务有两种比较成熟实现方案,最大努力送达型和TCC(Try Confirm Cancel)。

最大努力送达型可以保证事务最终成功,业务入侵较小,仅需要业务方实现幂等性即可。它要求事务最终一定会成功,无法回滚,因此会反复尝试,直至最终成功。

TCC类似原生事务,事务可以提交,也可以回滚。但事务的提交和回滚操作均需要业务工程师去实现,因此对业务入侵极大,TCC同样需要业务代码实现幂等性。

Markdown 上图是最大努力送达型的流程图。它在SQL执行前记录事务日志,在SQL执行成功后清理相应事务日志。一旦SQL执行失败,事务管理器就会通过同步和异步执行的方式从日志库中获取当前的SQL反复尝试,直至到达最大重试次数为止。超过最大重试次数的数据需要人工介入处理。

云化

Markdown

  • 首先,应用运行时环境是通过容器来实现的,不同编程语言编写的应用需要不同的运行时环境,将环境、应用及相关依赖一起打包发布是容器的主要作用,在当当采用的容器解决方案是Docker。另外,容器还可以用于隔离运行环境,让运行在同一宿主机的不同镜像可以安全和独立的使用既有资源。

  • 其次,仅通过容器无法做到应用治理。中间件是应用弹性化的关键,它分别针对服务、作业以及数据库访问进行有效的增强。服务和作业中间件是两种截然不同的应用类型,而数据中间件则是它们的有力支撑。无状态的服务更易弹性扩展;而有状态的作业则通过分片项隔离与数据的依赖;数据库中间件则用于简化分布式数据库的访问以及事务处理。

  • 最后,一个可快速运行且高度弹性化方案的最后一块拼图,即是需要一个平台去合理分配资源以及自动分发应用。目前比较流行的是Kubernetes和Mesos两种方案。在使用的复杂度上Kubernetes要更加简洁和一站式,但Mesos所采用两级调度概念,通过其Framework定制化非常简单,因此当当选择Mesos作为资源调度系统。

Markdown

分布式调度系统当前有三种架构:单体式,两阶段以及状态共享。

单体式调度较为简单,调度逻辑可以在没有任何并发的情况下使用全部资源。但由于互联网业务需求多、变化快,因此这种架构虽然性能高,但不利于业务的快速变化。Hadoop V1即采用此种架构。

两阶段调度将资源通过Offer的形式分发给注册在调度系统中的各个Framework,由Framework负责处理接收到的Offer,调度底层系统仅用于资源的收集和治理。因此此种架构更易于通过Framework定制化扩展业务需求。Mesos即是两阶段调度的典型代表。

状态共享调度功能看似最为强大,但实现起来却更加复杂。它的每一个Framework都可以看到资源逻辑视图的全集,采用乐观锁的方式使用资源,每次资源使用时,需要根据资源的占用状态进行类似于事务方式的提交和回滚。它的优点是可以更加有效的利用资源,缺点是实现难度高。除了Google闭源的Omega系统论文,开源产品中目前很少见成熟的实现方案。

Markdown

上图是当当云平台的架构图。中间件API与业务应用一起打包至Docker镜像或tar文件,并放入应用仓库。服务型应用当当采用Marathon管理,由DubboX治理服务,并与应用一同放入Docker镜像;作业型应用当当使用自研的Mesos Framework Elastic-Job-Cloud代替Marathon进行瞬时和常驻作业的治理。

在Elastic-Job-Cloud中采用了自定义Executor和Mesos原生容器,它能够追加资源。利用此功能,可以有效的聚合作业,进一步简化开发并节省资源。Elastic-Job-Cloud调度的应用使用tar包存入应用仓库。虽然Marathon与Elastic-Job-Cloud所管理的容器不同,但Mesos都会采用同样的接口将其分发至相应Mesos Agent执行。

Markdown

上图是更加全面的整体云化架构图。可以从运维、构建、日志和监控4个方面说明。

运维通过自研的控制台,向Mesos Framework发送信令和读取数据来达到控制业务应用上下线、暂停执行等功能,并可查看运行时状态。

构建是通过当当自研的盘古系统,控制灰度发布,一键回滚等功能,并由盘古系统将待部署上线的应用镜像推送至Nexus或Docker仓库。由Mesos Agent自动从Nexus获取tar文件、或由Docker仓库获取应用镜像并执行。

日志采用的标准的ELK的方式,不过获取日志并未使用Logstash,而是采用性能更佳的Filebeat代替。

监控是由多个维度组成。首先使用Mesos Exporter暴露Mesos的状态数据,然后由时序数据库Prometheus定期抓取,并由Grafana展现结果。Prometheus也通过Alertmanager向当当自研的雷达系统发送报警数据,由雷达系统负责最终的报警。其次,雷达系统还负责抓取elasticsearch的报警日志以及运行历史记录、SLA等信息一并报警。

Markdown

刚才介绍的DubboX、Elastic-Job以及Sharding-JDBC都已开源。

Elastic-Job经不完全统计,有30家以上的公司在使用。Elastic-Job目前是Mesosphere官方认可的Framework,在Apache Mesos的官方文档中可以查到,目前已经进行到对接DC/OS的最终阶段。

Sharding-JDBC仅仅开源1年多,即获取了2016年最受欢迎的国产开源第17名。

三个开源项目均采用Apache协议,有兴趣的同学请自由使用,欢迎提供宝贵意见。