text: (window.webpackJsonp=window.webpackJsonp||[]).push([[5],{177:function(a,r,e){"use strict";e.r(r);var t=e(0),v=Object(t.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var a=this,r=a.$createElement,e=a._self._c||r;return e("div",{staticClass:"content"},[e("p",[a._v("你好，我是胡夕。今天我想和你聊聊如何选择Kafka版本号这个话题。今天要讨论的内容实在是太重要了，我觉得它甚至是你日后能否用好Kafka的关键。")]),a._v(" "),e("p",[a._v("上一期我介绍了目前流行的几种Kafka发行版，其实不论是哪种Kafka，本质上都内嵌了最核心的Apache Kafka，也就是社区版Kafka，那今天我们就来说说Apache Kafka版本号的问题。在开始之前，我想强调一下后面出现的所有“版本”这个词均表示Kafka具体的版本号，而非上一篇中的Kafka种类，这一点切记切记！")]),a._v(" "),e("p",[a._v("那么现在你可能会有这样的疑问：我为什么需要关心版本号的问题呢？直接使用最新版本不就好了吗？当然了，这的确是一种有效的选择版本的策略，但我想强调的是这种策略并非在任何场景下都适用。如果你不了解各个版本之间的差异和功能变化，你怎么能够准确地评判某Kafka版本是不是满足你的业务需求呢？因此在深入学习Kafka之前，花些时间搞明白版本演进，实际上是非常划算的一件事。")]),a._v(" "),e("h2",{attrs:{id:"kafka版本命名"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#kafka版本命名","aria-hidden":"true"}},[a._v("#")]),a._v(" Kafka版本命名")]),a._v(" "),e("p",[a._v("当前Apache Kafka已经迭代到2.2版本，社区正在为2.3.0发版日期进行投票，相信2.3.0也会马上发布。但是稍微有些令人吃惊的是，很多人对于Kafka的版本命名理解存在歧义。比如我们在官网上下载Kafka时，会看到这样的版本：")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://static001.geekbang.org/resource/image/c1/23/c10df9e6f72126e9c721fba38e27ac23.png",alt:""}})]),a._v(" "),e("p",[a._v("于是有些同学就会纳闷，难道Kafka版本号不是2.11或2.12吗？其实不然，前面的版本号是编译Kafka源代码的Scala编译器版本。Kafka服务器端的代码完全由Scala语言编写，Scala同时支持面向对象编程和函数式编程，用Scala写成的源代码编译之后也是普通的“.class”文件，因此我们说Scala是JVM系的语言，它的很多设计思想都是为人称道的。")]),a._v(" "),e("p",[a._v("事实上目前Java新推出的很多功能都是在不断向Scala语言靠近罢了，比如Lambda表达式、函数式接口、val变量等。一个有意思的事情是，Kafka新版客户端代码完全由Java语言编写，于是有些人展开了“Java VS Scala”的大讨论，并从语言特性的角度尝试分析Kafka社区为什么放弃Scala转而使用Java重写客户端代码。其实事情远没有那么复杂，仅仅是因为社区来了一批Java程序员而已，而以前老的Scala程序员隐退罢了。可能有点跑题了，但不管怎样我依然建议你有空去学学Scala语言。")]),a._v(" "),e("p",[a._v("回到刚才的版本号讨论。现在你应该知道了对于kafka-2.11-2.1.1的提法，真正的Kafka版本号实际上是2.1.1。那么这个2.1.1又表示什么呢？前面的2表示大版本号，即Major Version；中间的1表示小版本号或次版本号，即Minor Version；最后的1表示修订版本号，也就是Patch号。Kafka社区在发布1.0.0版本后特意写过一篇文章，宣布Kafka版本命名规则正式从4位演进到3位，比如0.11.0.0版本就是4位版本号。")]),a._v(" "),e("p",[a._v("坦率说，这里我和社区的意见是有点不同的。在我看来像0.11.0.0这样的版本虽然有4位版本号，但其实它的大版本是0.11，而不是0，所以如果这样来看的话Kafka版本号从来都是由3个部分构成，即“大版本号 - 小版本号 - Patch号”。这种视角可以统一所有的Kafka版本命名，也方便我们日后的讨论。我们来复习一下，假设碰到的Kafka版本是0.10.2.2，你现在就知道了它的大版本是0.10，小版本是2，总共打了两个大的补丁，Patch号是2。")]),a._v(" "),e("h2",{attrs:{id:"kafka版本演进"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#kafka版本演进","aria-hidden":"true"}},[a._v("#")]),a._v(" Kafka版本演进")]),a._v(" "),e("p",[a._v("Kafka目前总共演进了7个大版本，分别是0.7、0.8、0.9、0.10、0.11、1.0和2.0，其中的小版本和Patch版本很多。哪些版本引入了哪些重大的功能改进？关于这个问题，我建议你最好能做到如数家珍，因为这样不仅令你在和别人交谈Kafka时显得很酷，而且如果你要向架构师转型或者已然是架构师，那么这些都是能够帮助你进行技术选型、架构评估的重要依据。")]),a._v(" "),e("p",[a._v("我们先从0.7版本说起，实际上也没什么可说的，这是最早开源时的“上古”版本了，以至于我也从来都没有接触过。这个版本只提供了最基础的消息队列功能，甚至连副本机制都没有，我实在想不出有什么理由你要使用这个版本，因此一旦有人向你推荐这个版本，果断走开就好了。")]),a._v(" "),e("p",[a._v("Kafka从0.7时代演进到0.8之后正式引入了"),e("strong",[a._v("副本机制")]),a._v("，至此Kafka成为了一个真正意义上完备的分布式高可靠消息队列解决方案。有了副本备份机制，Kafka就能够比较好地做到消息无丢失。那时候生产和消费消息使用的还是老版本的客户端API，所谓的老版本是指当你用它们的API开发生产者和消费者应用时，你需要指定ZooKeeper的地址而非Broker的地址。")]),a._v(" "),e("p",[a._v("如果你现在尚不能理解这两者的区别也没关系，我会在专栏的后续文章中详细介绍它们。老版本客户端有很多的问题，特别是生产者API，它默认使用同步方式发送消息，可以想见其吞吐量一定不会太高。虽然它也支持异步的方式，但实际场景中可能会造成消息的丢失，因此0.8.2.0版本社区引入了"),e("strong",[a._v("新版本Producer API")]),a._v("，即需要指定Broker地址的Producer。")]),a._v(" "),e("p",[a._v("据我所知，国内依然有少部分用户在使用0.8.1.1、0.8.2版本。"),e("strong",[a._v("我的建议是尽量使用比较新的版本。如果你不能升级大版本，我也建议你至少要升级到0.8.2.2这个版本，因为该版本中老版本消费者API是比较稳定的。另外即使你升到了0.8.2.2，也不要使用新版本Producer API，此时它的Bug还非常多。")])]),a._v(" "),e("p",[a._v("时间来到了2015年11月，社区正式发布了0.9.0.0版本。在我看来这是一个重量级的大版本更迭，0.9大版本增加了基础的安全认证/权限功能，同时使用Java重写了新版本消费者API，另外还引入了Kafka Connect组件用于实现高性能的数据抽取。如果这么多眼花缭乱的功能你一时无暇顾及，那么我希望你记住这个版本的另一个好处，那就是"),e("strong",[a._v("新版本Producer API在这个版本中算比较稳定了")]),a._v("。如果你使用0.9作为线上环境不妨切换到新版本Producer，这是此版本一个不太为人所知的优势。但和0.8.2引入新API问题类似，不要使用新版本Consumer API，因为Bug超多的，绝对用到你崩溃。即使你反馈问题到社区，社区也不会管的，它会无脑地推荐你升级到新版本再试试，因此千万别用0.9的新版本Consumer API。对于国内一些使用比较老的CDH的创业公司，鉴于其内嵌的就是0.9版本，所以要格外注意这些问题。")]),a._v(" "),e("p",[a._v("0.10.0.0是里程碑式的大版本，因为该版本"),e("strong",[a._v("引入了Kafka Streams")]),a._v("。从这个版本起，Kafka正式升级成分布式流处理平台，虽然此时的Kafka Streams还基本不能线上部署使用。0.10大版本包含两个小版本：0.10.1和0.10.2，它们的主要功能变更都是在Kafka Streams组件上。如果你把Kafka用作消息引擎，实际上该版本并没有太多的功能提升。不过在我的印象中自0.10.2.2版本起，新版本Consumer API算是比较稳定了。"),e("strong",[a._v("如果你依然在使用0.10大版本，我强烈建议你至少升级到0.10.2.2然后使用新版本Consumer API。还有个事情不得不提，0.10.2.2修复了一个可能导致Producer性能降低的Bug。基于性能的缘故你也应该升级到0.10.2.2。")])]),a._v(" "),e("p",[a._v("在2017年6月，社区发布了0.11.0.0版本，引入了两个重量级的功能变更：一个是提供幂等性Producer API以及事务（Transaction） API；另一个是对Kafka消息格式做了重构。")]),a._v(" "),e("p",[a._v("前一个好像更加吸引眼球一些，毕竟Producer实现幂等性以及支持事务都是Kafka实现流处理结果正确性的基石。没有它们，Kafka Streams在做流处理时无法向批处理那样保证结果的正确性。当然同样是由于刚推出，此时的事务API有一些Bug，不算十分稳定。另外事务API主要是为Kafka Streams应用服务的，实际使用场景中用户利用事务API自行编写程序的成功案例并不多见。")]),a._v(" "),e("p",[a._v("第二个重磅改进是消息格式的变化。虽然它对用户是透明的，但是它带来的深远影响将一直持续。因为格式变更引起消息格式转换而导致的性能问题在生产环境中屡见不鲜，所以你一定要谨慎对待0.11版本的这个变化。不得不说的是，这个版本中各个大功能组件都变得非常稳定了，国内该版本的用户也很多，应该算是目前最主流的版本之一了。也正是因为这个缘故，社区为0.11大版本特意推出了3个Patch版本，足见它的受欢迎程度。我的建议是，如果你对1.0版本是否适用于线上环境依然感到困惑，那么至少将你的环境升级到0.11.0.3，因为这个版本的消息引擎功能已经非常完善了。")]),a._v(" "),e("p",[a._v("最后我合并说下1.0和2.0版本吧，因为在我看来这两个大版本主要还是Kafka Streams的各种改进，在消息引擎方面并未引入太多的重大功能特性。Kafka Streams的确在这两个版本有着非常大的变化，也必须承认Kafka Streams目前依然还在积极地发展着。如果你是Kafka Streams的用户，至少选择2.0.0版本吧。")]),a._v(" "),e("p",[a._v("去年8月国外出了一本书叫Kafka Streams in Action（中文版：《Kafka Streams实战》），它是基于Kafka Streams 1.0版本撰写的。最近我用2.0版本去运行书中的例子，居然很多都已经无法编译了，足见两个版本变化之大。不过如果你在意的依然是消息引擎，那么这两个大版本都是适合于生产环境的。")]),a._v(" "),e("p",[a._v("最后还有个建议，不论你用的是哪个版本，都请尽量保持服务器端版本和客户端版本一致，否则你将损失很多Kafka为你提供的性能优化收益。")]),a._v(" "),e("h2",{attrs:{id:"小结"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#小结","aria-hidden":"true"}},[a._v("#")]),a._v(" 小结")]),a._v(" "),e("p",[a._v("我希望现在你对如何选择合适的Kafka版本能做到心中有数了。每个Kafka版本都有它恰当的使用场景和独特的优缺点，切记不要一味追求最新版本。事实上我周围的很多工程师都秉承这样的观念：不要成为最新版本的“小白鼠”。了解了各个版本的差异之后，我相信你一定能够根据自己的实际情况作出最正确的选择。")]),a._v(" "),e("h2",{attrs:{id:"开放讨论"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#开放讨论","aria-hidden":"true"}},[a._v("#")]),a._v(" 开放讨论")]),a._v(" "),e("p",[a._v("如何评估Kafka版本升级这件事呢？你和你所在的团队有什么独特的见解？")]),a._v(" "),e("p",[a._v("欢迎你写下自己的思考或疑问，我们一起讨论 。如果你觉得有所收获，也欢迎把文章分享给你的朋友。")]),a._v(" "),e("p",[e("img",{attrs:{src:"https://static001.geekbang.org/resource/image/c8/bf/c89da43deab85fe7cb06acec867aa5bf.jpg",alt:""}})])])}],!1,null,null,null);r.default=v.exports}}]);