当当网的内部框架开源策略案例分享
author:一佰互联 2019-04-18   click:245

打造内部应用框架
当当技术部现在是按照产品线划分的,一个产品线的产品、开发、测试都在一个部门,但像项目管理、运维、架构这些技术体系中公用的部分是独立的部门。架构部里主要分成三部分,一个是架构与规范,一个是性能测试,一个是基础应用系统研发。

我们花了比较多的精力在技术架构上,去年我们在Dubbo上做了二次开发,做了DubboX并且对外开源,业界反馈还不错,包括很多来面试的人都知道。

我们的技术体系、核心业务系统明确的方向是Java,去年年底,我们开始做一个基于Java的应用开发框架,DDFrame,用它去对接一些核心 组件,包括SOA、作业调度、缓存、消息队列、数据库、配置中心等,现在已经发布了2.0版本。虽然受限于资源,进度比较缓慢,但我们一直在做这件事,未 来也会慢慢完善这个框架,使其成为技术体系的核心。

开源Dubbox,扩展Dubbo服务框架支持REST风格远程调用
当当网近日开源了Dubbox项目,可为Dubbo服务框架提供多项扩展功能,包括REST风格远程调用、Kryo/FST序列化等等。

当当网架构部和技术委员会架构师沈理向InfoQ中文站介绍了Dubbox项目,开发背景和主要特点描述如下:

Dubbo是一个被国内很多互联网公司广泛使用的开源分布式服务框架,即使从国际视野来看应该也是一个非常全面的SOA基础框架。作为一个重要的技术研究课题,在当当网我们根据自身的需求,为Dubbo实现了一些新的功能,并将其命名为Dubbox(即Dubbo eXtensions)。

主要的新功能包括:

支持REST风格远程调用(HTTP + JSON/XML):基于非常成熟的JBoss RestEasy框架,在dubbo中实现了REST风格(HTTP + JSON/XML)的远程调用,以显著简化企业内部的跨语言交互,同时显著简化企业对外的Open API、无线API甚至AJAX服务端等等的开发。事实上,这个REST调用也使得Dubbo可以对当今特别流行的“微服务”架构提供基础性支持。 另外,REST调用也达到了比较高的性能,在基准测试下,HTTP + JSON与Dubbo 2.x默认的RPC协议(即TCP + Hessian2二进制序列化)之间只有1.5倍左右的差距,详见下文的基准测试报告。

支持基于Kryo和FST的Java高效序列化实现:基于当今比较知名的Kryo和FST高性能序列化库,为Dubbo 默认的RPC协议添加新的序列化实现,并优化调整了其序列化体系,比较显著的提高了Dubbo RPC的性能,详见下图。

支持基于嵌入式Tomcat的HTTP remoting体系:基于嵌入式tomcat实现dubbo的HTTP remoting体系(即dubbo-remoting-http),用以逐步取代Dubbo中旧版本的嵌入式Jetty,可以显著的提高REST等的远程调用性能,并将Servlet API的支持从2.5升级到3.1。(注:除了REST,dubbo中的WebServices、Hessian、HTTP Invoker等协议都基于这个HTTP remoting体系)。

升级Spring:将dubbo中Spring由2.x升级到目前最常用的3.x版本,减少项目中版本冲突带来的麻烦。

升级ZooKeeper客户端:将dubbo中的zookeeper客户端升级到最新的版本,以修正老版本中包含的bug。

上面很多功能已在当当网内部稳定的使用,现在开源出来,供大家参考和指正。也希望感兴趣的朋友也来为Dubbo贡献更多的改进。

注:dubbox和dubbo 2.x是兼容的,没有改变dubbo的任何已有的功能和配置方式(除了升级了Spring之类的版本)。另外,dubbox也严格遵循了Apache 2.0许可证的要求。


分布式作业调度框架elastic-job的开源
elastic-job原本是当当java应用框架ddframe的一部分,本名dd-job。ddframe包括编码规范,开发框架,技术规范,监控以及分布式组件。ddframe规划分为4个演进阶段,目前处于第2阶段。3、4阶段涉及的技术组件不代表当当没有使用,只是ddframe还未统一规划。

ddframe由各种模块组成,均已dd-开头,如dd-container、dd-soa、dd-rdb、dd-job等。当当希望将ddframe的各个模块与公司环境解耦并开源以反馈社区。之前开源的Dubbo扩展版本DubboX即是dd-soa的核心模块。而本次介绍的elastic-job则是dd-job的开源部分,其中监控(但开源了监控方法)和ddframe核心接入等部分并未开源。

elastic-job主要的设计理念是无中心化的分布式定时调度框架,思路来源于Quartz的基于数据库的高可用方案。但数据库毕竟没有分布式协调功能,所以在高可用方案的基础上增加了弹性扩容和数据分片的思路,以便于更大限度的利用分布式服务器的资源。

团队目前由3个部分组成,第一部分是开发团队,由架构部的架构师曹昊、高洪涛和我组成,主要负责设计和编码;第二部分是来自于各个研发团队的应用架构师、开发工程师和架构部总监史海峰,他们负责推广落地,整理需求并贡献当当已经存在的最佳实践代码;第三部分由架构部性能测试团队组成,负责框架的性能和稳定性测试。

elastic-job的主要分为注册中心、数据分片、分布式协调,定时任务处理和多作业模式等模块。

注册中心模块目前直接使用Zookeeper,用于记录作业的配置,服务器信息以及作业运行状态。Zookeeper虽然很成熟,但原理复杂,使用较难,在海量数据支持的情况下也会有性能和网络问题。目前elastic-job已经抽象出注册中心的接口,下一步将会考虑支持多注册中心,如etcd,或由用户自行实现注册中心。无临时节点和监听机制的注册中心需要自行实现定时心跳监测等功能。

数据分片是elastic-job中实现分布式的重要概念,将真实数据和逻辑分片对应,用于解耦作业框架和数据的关系。作业框架只负责将分片合理的分配给相关的作业服务器,而作业服务器需要根据所分配的分片匹配数据进行处理。服务器分片目前都存储在注册中心中,各个服务器根据自己的IP地址拉取分片。

分布式协调模块用于处理作业服务器的动态扩容缩容。一旦集群中有服务器发生变化,分布式协调将自动监测并将变化结果通知给各个仍存活的作业服务器。协调时将会涉及主节点选举,重分片等操作。目前使用的Zookeeper的临时节点和监听器实现主动检查和通知功能。

定时任务处理根据cron表达式定时触发任务,目前有防止任务同时触发,错过任务重出发等功能。主要还是使用Quartz本身的定时调度功能,为了便于控制,每个任务都使用独立的线程池。

多作业模式将定时任务分为多种流程,有不经任何修饰的简单任务;有用于处理数据的fetchData/processData的数据流任务;以后还将增加消息流任务,文件任务,工作流任务等。用户能以插件的形式扩展并贡献代码。

作业即定时任务。一般来说,系统可使用消息传递代替部分使用作业的场景。两者确有相似之处。可互相替换的场景,如队列表。将待处理的数据放入队列表,然后使用频率极短的定时任务拉取队列表的数据并处理。这种情况使用消息中间件的推送模式可更好的处理实时性数据。而且基于数据库的消息存储吞吐量远远小于基于文件的顺序追加消息存储。

但在某些场景下则不能互换:

时间驱动 OR 事件驱动:内部系统一般可以通过事件来驱动,但涉及到外部系统,则只能使用时间驱动。如:抓取外部系统价格。每小时抓取,由于是外部系统,不能像内部系统一样发送事件触发事件。
批量处理 OR 逐条处理:批量处理堆积的数据更加高效,在不需要实时性的情况下比消息中间件更有优势。而且有的业务逻辑只能批量处理,如:电商公司与快递公司结算,一个月结算一次,并且根据送货的数量有提成。比如,当月送货超过1000则额外给快递公司多1%的快递费。
非实时性 OR 实时性:虽然消息中间件可以做到实时处理数据,但有的情况并不需要。如:VIP用户降级,如果超过1年无购买行为,则自动降级。这类需求没有强烈的时间要求,不需要按照时间精确的降级VIP用户。
系统内部 OR 系统解耦。作业一般封装在系统内部,而消息中间件可用于系统间解耦。

elastic-job的主要功能
主要功能
分布式:重写Quartz基于数据库的分布式功能,改用Zookeeper实现注册中心。
并行调度:采用任务分片方式实现。将一个任务拆分为n个独立的任务项,由分布式的服务器并行执行各自分配到的分片项。
弹性扩容缩容:将任务拆分为n个任务项后,各个服务器分别执行各自分配到的任务项。一旦有新的服务器加入集群,或现有服务器下线,elastic-job将在保留本次任务执行不变的情况下,下次任务开始前触发任务重分片。
集中管理:采用基于Zookeeper的注册中心,集中管理和协调分布式作业的状态,分配和监听。外部系统可直接根据Zookeeper的数据管理和监控elastic-job。
定制化流程型任务:作业可分为简单和数据流处理两种模式,数据流又分为高吞吐处理模式和顺序性处理模式,其中高吞吐处理模式可以开启足够多的线程快速的处理数据,而顺序性处理模式将每个分片项分配到一个独立线程,用于保证同一分片的顺序性,这点类似于kafka的分区顺序性。
其他功能
失效转移:弹性扩容缩容在下次作业运行前重分片,但本次作业执行的过程中,下线的服务器所分配的作业将不会重新被分配。失效转移功能可以在本次作业运行中用空闲服务器抓取孤儿作业分片执行。同样失效转移功能也会牺牲部分性能。
Spring命名空间支持:elastic-job可以不依赖于spring直接运行,但是也提供了自定义的命名空间方便与spring集成。
运维平台:提供web控制台用于管理作业。
非功能需求
稳定性:在服务器无波动的情况下,并不会重新分片;即使服务器有波动,下次分片的结果也会根据服务器IP和作业名称哈希值算出稳定的分片顺序,尽量不做大的变动。
高性能:同一服务器的批量数据处理采用自动切割并多线程并行处理。
灵活性:所有在功能和性能之间的权衡,都可通过配置开启/关闭。如:elastic-job会将作业运行状态的必要信息更新到注册中心。如果作业执行频度很高,会造成大量Zookeeper写操作,而分布式Zookeeper同步数据可能引起网络风暴。因此为了考虑性能问题,可以牺牲一些功能,而换取性能的提升。
幂等性:elastic-job可牺牲部分性能用以保证同一分片项不会同时在两个服务器上运行。
容错性:作业服务器和Zookeeper断开连接则立即停止作业运行,用于防止分片已经重新分配,而脑裂的服务器仍在继续执行,导致重复执行。
Elastic-job在当当内部也是刚刚进行推广,目前支付系统、订单系统、发票系统、促销系统都有使用,快递系统和仓储系统也即将使用。开源后也得知不少公司有使用计划。