Kafka权威指南
上QQ阅读APP看书,第一时间看更新

2.7 生产环境的注意事项

当你准备把Kafka从测试环境部署到生产环境时,需要注意一些事项,以便创建更可靠的消息服务。

2.7.1 垃圾回收器选项

为应用程序调整Java垃圾回收参数就像是一门艺术,我们需要知道应用程序是如何使用内存的,还需要大量的观察和试错。幸运的是,Java 7为我们带来了G1垃圾回收器,让这种状况有所改观。在应用程序的整个生命周期,G1会自动根据工作负载情况进行自我调节,而且它的停顿时间是恒定的。它可以轻松地处理大块的堆内存,把堆内存分为若干小块的区域,每次停顿时并不会对整个堆空间进行回收。

正常情况下,G1只需要很少的配置就能完成这些工作。以下是G1的两个调整参数。

MaxGCPauseMillis:

该参数指定每次垃圾回收默认的停顿时间。该值不是固定的,G1可以根据需要使用更长的时间。它的默认值是200ms。也就是说,G1会决定垃圾回收的频率以及每一轮需要回收多少个区域,这样算下来,每一轮垃圾回收大概需要200ms的时间。

InitiatingHeapOccupancyPercent:

该参数指定了在G1启动新一轮垃圾回收之前可以使用的堆内存百分比,默认值是45。也就是说,在堆内存的使用率达到45%之前,G1不会启动垃圾回收。这个百分比包括新生代和老年代的内存。

Kafka对堆内存的使用率非常高,容易产生垃圾对象,所以可以把这些值设得小一些。如果一台服务器有64GB内存,并且使用5GB堆内存来运行Kafka,那么可以参考以下的配置:MaxGCPauseMillis可以设为20ms; InitiatingHeapOccupancyPercent可以设为35,这样可以让垃圾回收比默认的要早一些启动。

Kafka的启动脚本并没有启用G1回收器,而是使用了Parallel New和CMS(Concurrent Mark-Sweep,并发标记和清除)垃圾回收器。不过它可以通过环境变量来修改。本章前面的内容使用start命令来修改它:

        # export JAVA HOME=/usr/java/jdk1.8.0 51
        # export KAFKA JVM PERFORMANCE OPTS="-server -XX:+UseG1GC
        -XX:MaxGCPauseMillis=20-XX:InitiatingHeapOccupancyPercent=35
        -XX:+DisableExplicitGC -Djava.awt.headless=true"
        # /usr/local/kafka/bin/kafka-server-start.sh -daemon
        /usr/local/kafka/config/server.properties
        #

2.7.2 数据中心布局

在开发阶段,人们并不会太关心Kafka服务器在数据中心所处的物理位置,因为即使集群在短时间内出现局部或完全不可用,也不会造成太大影响。但是,在生产环境,服务不可用意味着金钱的损失,具体表现为无法为用户提供服务或者不知道用户正在做什么。这个时候,使用Kafka集群的复制功能就变得尤为重要(请参考第6章),而服务器在数据中心所处的物理位置也变得重要起来。如果在部署Kafka之前没有考虑好这个问题,那么在后续的维护过程中,移动服务器需要耗费更高的成本。

在为broker增加新的分区时,broker并无法获知机架的信息。也就是说,两个broker有可能是在同一个机架上,或者在同一个可用区域里(如果运行在像AWS这样的的云服务上),所以,在为分区添加副本的时候,这些副本很可能被分配给同一个机架上的broker,它们使用相同的电源和网络连接。如果该机架出了问题,这些分区就会离线,客户端就无法访问到它们。更糟糕的是,如果发生不完整的主节点选举,那么在恢复时就有可能丢失数据(第6章将介绍更多细节)。

所以,最好把集群的broker安装在不同的机架上,至少不要让它们共享可能出现单点故障的基础设施,比如电源和网络。也就是说,部署服务器需要至少两个电源连接(两个不同的回路)和两个网络交换器(保证可以进行无缝的故障切换)。除了这些以外,最好还要把broker安放在不同的机架上。因为随着时间的推移,机架也需要进行维护,而这会导致机器离线(比如移动机器或者重新连接电源)。

2.7.3 共享Zookeeper

Kafka使用Zookeeper来保存broker、主题和分区的元数据信息。对于一个包含多个节点的Zookeeper群组来说,Kafka集群的这些流量并不算多,那些写操作只是用于构造消费者群组或集群本身。实际上,在很多部署环境里,会让多个Kafka集群共享一个Zookeeper群组(每个集群使用一个chroot路径)。

Kafka消费者和Zookeeper

在Kafka 0.9.0.0版本之前,除了broker之外,消费者也会使用Zookeeper来保存一些信息,比如消费者群组的信息、主题信息、消费分区的偏移量(在消费者群组里发生失效转移时会用到)。到了0.9.0.0版本,Kafka引入了一个新的消费者接口,允许broker直接维护这些信息。这个新的消费者接口将在第4章介绍。

不过,消费者和Zookeeper之间还是有一个值得注意的地方,消费者可以选择将偏移量提交到Zookeeper或Kafka,还可以选择提交偏移量的时间间隔。如果消费者将偏移量提交到Zookeeper,那么在每个提交时间点上,消费者将会为每一个消费的分区往Zookeeper写入一次偏移量。合理的提交间隔是1分钟,因为这刚好是消费者群组的某个消费者发生失效时能够读取到重复消息的时间。值得注意的是,这些提交对于Zookeeper来说流量不算小,特别是当集群里有多个消费者的时候。如果Zookeeper群组无法处理太大的流量,就有必要使用长一点的提交时间间隔。不过不管怎样,还是建议使用最新版本的Kafka,让消费者把偏移量提交到Kafka服务器上,消除对Zookeeper的依赖。

虽然多个Kafka集群可以共享一个Zookeeper群组,但如果有可能的话,不建议把Zookeeper共享给其他应用程序。Kafka对Zookeeper的延迟和超时比较敏感,与Zookeeper群组之间的一个通信异常就可能导致Kafka服务器出现无法预测的行为。这样很容易让多个broker同时离线,如果它们与Zookeeper之间断开连接,也会导致分区离线。这也会给集群控制器带来压力,在服务器离线一段时间之后,当控制器尝试关闭一个服务器时,会表现出一些细小的错误。其他的应用程序因重度使用或进行不恰当的操作给Zookeeper群组带来压力,所以最好让它们使用自己的Zookeeper群组。