前言
为什么要写这本书
大概但凡写文章,都应该是在夜深人静的时候吧。
回顾来沪四年,思绪一下子回到了到沪的第一天。那是我第一次来上海。虽然没有小马哥勇闯上海滩的豪情万丈,但似乎也并未十分惆怅。拖着行李箱去新公司报到,当时住的地方尚未找好,新公司的名字竟也还未确定。报到的新公司,真的是一家“新”公司,刚刚成立不久,办公室在一个略显陈旧的大楼内,后来才知道那个办公室内还有另外一个公司的人员在那儿办公。当时辉哥接待了我,交给我一个Mac电脑,给我讲了一下公司的业务和系统现状。那是我第一次使用Mac电脑,操作还十分生疏。而现在,我正用着这台电脑,写着这篇自序(前言)。后来海阳到了,他给我展示和讲解了公司当时的产品。再后来,他还教了我怎样用Mac电脑。下午的时候我提前下班,拖着行李箱找住的地方去了。那天,当忙完一切躺到床上时,我意识到一切真的都是新的开始了。新的城市、新的公司、新的同事、新的业务、新的领域和新的工作内容。
之后就是正式的工作了。
因为是做Java开发的,所以花一天半时间学了Django,之后接手了Python后台开发。
因为是做Java开发的,所以花一天时间装了Android Studio,开始了SDK与后台的适配。
因为是做Java开发的,所以Java后台服务更是当仁不让了。
因为是做Java开发的,所以也要负责业务核心算法的开发。
因为是做Java开发的,所以DevOps也得负责推进吧。
因为是做Java开发的,所以……
那应该是我最专心地做开发的一段时间,甚至在一年之后的公司年会上,我还因此获得了一个“最佳沉默奖”。其实并非我不说话,而是每次市场部的同事看到我时,我都是在座位上写程序,好像我从来没离开过座位一样。是的,我一直在开发着,但是并不感到忙碌和紧迫,因为有充足的时间去思考问题、验证猜想并最终解决问题。
从2015年开始,公司因为业务需要,开始涉及实时流计算领域。当时流计算技术远非像现在这样普及,Flink还没有在国内流行起来,Spark Streaming处于“半吊子”状态,Storm则还是简单的TopologyBuilder。不过最主要的问题还是,虽然当时公司意识到要使用实时流计算技术,但没有人真正理解实时流计算系统到底该怎么用,应该怎样将流计算真正贴切地运用到我们的业务需求中。
在这种情况下,作为后台开发的我开始了自己的思考和探索。其实解决问题的思路非常简单,用最自然、最贴切、最实际、最节省资源的方式去解决真实的业务问题。最开始,我们选择Akka来作为流计算框架,但是因为对Akka的特性理解不到位,后来开发出的程序出现各种问题。例如,在Actor中光明正大地休眠(sleep)、对反向压力不管不顾,结果程序总是时不时宕掉、错误使用Akka Cluster导致集群脑裂等。
在程序开发过程中,我是一个谨小慎微的人,对于任何不确定性的因素,只要想到了就一定会尽力去避免,即使当时其他人不甚理解。所以从一开始,我就在自己负责的模块中,对Akka添加了反向压力的支持,就是为了避免执行步调不一致时导致的OOM。从后来的结果看,当时的做法是非常正确的。虽然在Akka中添加了反向压力的支持,但回看起来,实现得过于复杂。虽然保证了反向压力带来的程序稳定性,可是在50行的代码中,只有1行代码是涉及业务处理的。这种解决方案显然非常不明智。
当时,我还在极力尝试尽可能提高程序的性能,希望充分“榨干”机器的CPU和I/O资源,以尽可能降低硬件成本。经过一段时间的调研和思考,我逐渐发现,NIO和异步才是彻底“榨干”CPU与I/O资源的关键所在。虽然纤程(或协程)也是一种充分利用资源的完美手段,但可惜当时JVM领域尚无一个公开好用的完美纤程实现方案。
那时候,我开始隐隐约约地意识到,似乎“流”是一种非常好的编程模式。
首先,“流”与“异步”不谋而合。“流”的各个节点通过队列传递消息,不同节点的执行正好就是完全异步的。另外,由于队列隔离,不同节点的执行完全不用考虑并发安全的问题。
其次,如果“流”的执行节点间使用的是阻塞队列,那么整个流的各个执行环节就天然地带有了反向压力能力,不需要像之前在Akka中那样非常复杂的实现。
再次,“流”能够非常自然地描述业务执行的流程。不管是大到整个产品线的各个服务模块,还是小到每个服务模块中的具体实现步骤,就像“分形”一样,“流”能够做任意细粒度的划分。这是一种非常普遍的描述事情发生过程的模式。
最后,通过Kafka这种消息中间件的隔离,我可以非常清晰地定义好自己负责开发模块的责任边界,与其他同事的程序隔离开来,避免纠缠不清。当然,这是一种自私的想法,但是从设计模式高内聚、低耦合的角度来看,这又何尝不是一种非常不错的实践呢?更何况Kafka这种好用到“爆”的消息队列,真的是让人爱不释手!
于是,说干就干,我花了一个周末的时间,编写了第一个版本的流计算框架。之后又经过几次大大小小的调整和改进,最终,这个流计算框架进入了公司所有产品的主要业务模块中。再后来,我又在这个流计算框架上开发了一个特征引擎,支持DSL和脚本,可以非常灵活、方便、快速地在流数据上即写即算地实现各种特征计算。所有特征计算都是并发处理,并且自动解析特征依赖与优化执行过程,可以说还是有一丁点儿小小的惊艳了。
4年的时光转瞬即逝。其实这4年还有太多太多的事情,从后台到前端、从开发到运维、从数据到系统、从服务器到嵌入式,编写程序、负责项目、担任架构师,一路走来,我有太多的收获,也有太多的感触。所以,我希望赶在而立之年前,对自己的这些收获和感触做一个总结,一方面是给自己人生阶段的交代,另一方面希望这些经验能够给后来的开发者带来些许帮助。
读者对象
本书主要适合于以下读者:
·Java软件开发人员;
·实时计算工程师和架构师;
·分布式系统工程师和架构师。
本书特色
本书总结了实时流计算系统的通用架构模式。通过从无到有构建一个流计算编程框架,让读者了解流计算应用计算的任务类型,学会解决计算过程遇到的各种问题和难点。本书希望让读者领会Java程序开发中“流”这种编程方式的优势和乐趣所在。另外,通过将单节点流计算应用扩展为分布式集群,让读者理解分布式系统的架构模式,并能准确看待开源社区中各种眼花缭乱的流计算框架,看透这些流计算框架的本质,避免选择恐惧症。本书还探讨了实时流计算能够与不能够解决的问题,让读者对流计算系统的能力了然于胸,不至于钻牛角尖。总而言之,读者在阅读本书后,能够对实时流计算系统有清晰的认识和理解,在架构设计、系统实现和具体应用方面都能做到心有丘壑,最终做出优秀的实时流计算应用产品。
如何阅读本书
首先需要澄清的是,本书的“非目标”是什么:
·各种流计算框架实战,诸如教读者如何使用Storm、Spark、Flink等流计算框架。笔者相信,针对每一种具体的流计算框架已经有许多优秀的书籍了。如果笔者再讲,就是不自量力、班门弄斧、狗尾续貂了。
澄清了本书的“非目标”,就可以定义本书的“目标”了:
·总结实时流计算系统的通用架构模式。所谓架构模式,是一种“形而上”的东西,也就是所谓的“道”。实时流计算系统体现出的软件设计之“道”,是笔者试图阐述的东西。
·从无到有构建一个“麻雀虽小,五脏俱全”的单节点实时流计算框架。通过这个造轮子的过程,我们会深入理解流计算系统中最本质、最困难、最容易混淆的概念。之后通过在多种开源流计算框架中多次验证这些概念,实现“道”向“形而
·下”的具象,让我们以后面对各种流计算框架时,都能够做到胸有成竹。
·通过将单节点的实时流计算框架扩展为分布式实时流计算框架,让读者理解多种不同的分布式系统构建模式。
·通过“流”这种异步编程模式,让读者理解并掌握编写高性能程序的编程之道,领略Java高并发编程的乐趣。
·不仅探讨实时流计算能够解决的问题,而且要明白当实在做不到“实时”时该如何进行架构设计。
·尽可能全面覆盖一个完整的实时流计算系统,包括许多周边系统,如存储系统、服务治理和配置管理等。如果这些“绿叶”点缀得不好,有时也会给实时流计算系统带来不利影响。
整体而言,本书的内容按照“总分”的结构组织。全书分为11章。
第1章介绍实时流计算技术的产生背景、使用场景和通用架构。
第2章通过实时流计算数据的采集,详细分析Java平台NIO和异步编程的基础,并初步讨论了“异步”和“流”这两种编程模式之间的关系。
第3~5章通过从零开始构造分布式实时流计算应用,详细剖析了实时流计算系统的计算任务类型、核心概念和技术关键点。
第6章通过多种开源流计算框架,验证第3~5章所讨论的实时流计算系统核心概念和技术关键点。
第7章讨论当实在做不到“实时”时,我们应该做出的备选方案。
第8~10章讨论构建完整实时流计算系统时必要的周边辅助系统。
第11章详细讨论两个实时流计算应用的具体案例。
另外,本书包含许多示例代码,但由于篇幅有限,这些代码不能完全展现在书中。读者可从GitHub仓库(https://github.com/alain898/real_time_stream_computing_book_source_code)获取本书完整的配套源代码。
勘误和支持
由于笔者水平有限,编写时间仓促,书中难免有一些错误或者不准确的地方,恳请读者批评指正。大家可以通过电子邮箱347041583@qq.com联系笔者。期待得到大家的真挚反馈,在技术之路上互勉共进。
致谢
首先要感谢来沪四年遇到的许多同事,辉哥、海阳、克克、波叔、Lex、沛沛、亘哥、佳哥、牙膏、Kevin、军军、建军、叶晗、Mike、国聪、俊华、培良、凯哥、国圣、志源、顾大神、渊总、荣哥、君姐、Vivi姐及其他更多同事,我从你们身上学到了太多太多,这些知识会让我受用终身!
感谢挚友飞哥、乔姐和Lisa,是你们让我在沪四年不再有漂泊感。
感谢机械工业出版社华章公司的高婧雅编辑,给予这本书出版的机会,并在写作期间一直给予我支持和鼓励,并引导我顺利完成全部书稿。
最后感谢我的爸爸、妈妈、弟弟、三叔、四叔、小姨、爷爷和奶奶,感谢你们伴随我成长。虽然很多话我说不出口,但我是在乎你们的!
谨以此书献给我最亲爱的家人,以及众多热爱Java的朋友们!
周爽
2019年10月