jjzjj

最重要的Kafka集群参数配置

故里学Java 2024-06-12 原文

今天学习一下Kafka集群配置中的一些重要参数配置,很多参数并未在官方文档,但是实际表现来看,对系统的影响还是很大的,。

Broker端参数

目前Kafka Broker相关的参数还是很多的,绝大部分都无需修改,下边按照用途,分组介绍,主要有以下几方面:

首先Broker是需要配置存储信息的
  • log.dirs:这是非常重要的参数,指定了Broker需要使用的若干个文件目录路径。要知道这个参数是没有默认值的,这说明它必须由你亲自指定。
  • log.dir:注意这个是dir,结尾是没有s的,说明它只能表示单个路径,它是补充上一个参数用的。

这两个参数应该怎么设置呢?很简单,你只要设置log.dirs,即第一个参数就好了,不要设置log.dir。而且更重要的是,在线上生产环境中一定要为log.dirs配置多个路径,如:/home/kafka1,/home/kafka2,/home/kafka3这样。如果有条件的话你最好保证这些目录挂载到不同的物理磁盘上。这样做的好处:

  • 提升读写性能:比起单块磁盘,多块物理磁盘同时读写数据有更高的吞吐量。
  • 能够实现故障转移:即Failover。这是Kafka1.1版本新引入的强大功能。要知道在以前,只要Kafka使用的任何一块磁盘挂掉了,整个Broker进程都会关闭,但是自1.1开始,这种情况被修正了,坏掉的磁盘上的数据会自动转移到其他正常的磁盘上,而且Broker还能正常工作,还记得上一期关于Kafka是否需要使用RAID的讨论了吗?这个改进正是我们舍弃RAID方案的基础:没有这种Failover的话,我们只能依靠RAID来提供保障。
Zookeeper相关的设置

首先Zookeeper是做什么的呢?它是一个分布式协调框架,负责协调管理并保存Kafka集群的所有元数据信息,比如集群都有哪些Broker在运行、创建了哪些Topic,每个Topic都有多少分区以及这些分区的Leader副本都在哪些机器上等信息。

Kafka与Zookeeper相关的重要的参数当属zookeeper.connect。这也是一个CSV格式的参数,比如我们可以指定它的值zk1:2181,zk2:2181,zk3:2181。2181是Zookeeper的默认端口。

现在问题来了,如果我绕过多个Kafka集群使用同一套Zookeeper集群,那么这个参数应该怎么设置呢?这时候chroot就派上用场了。这个chroot是Zookeeper的概念,类似与别名。

如果你有两套Kafka集群,假设分别叫它们Kafka1和Kafka2,那么两套集群的zookeeper.connect参数可以这样配置:zk1:2181,zk2:2181,zk3:2181/kafka1和zk1:2181,zk2:2181,zk3:2181/kafka2。切记chroot只需要写一次,而且是加到最后的。

第三组参数是与Broker连接相关的

即客户端程序或者、其他Broker如何与该Broker进行通信的设置,有以下三个参数:

  • listeners:学名叫监听器,其实就是告诉外部连接者要通过什么协议访问指定主机名和端口开放的Kafka服务。
  • advertised.listenters:和listeners相比多了个advertised。Advertised的含义表示宣称的、公布的,就是说这组监听器是Broker用于对外发布的。
  • host.name/port:列出这两个参数就是想说你把它们忘掉吧,压根不要为它们指定值,毕竟都是过期的参数了。

我们具体说说监听器的概念,从构成上来说,它是若干逗号分隔的三元组,每个三元组的格式为<协议名称,主机名,端口号>。这里的协议名称可能是标准的名字,比如PLAINTEXT表示明文传输、SSL表示使用SSL或者TLS加密传输等;也可能是你自己定义的协议名字,比如CONTROLLER://localhost:9092。

一旦你自己定义了协议名称,你必须还要指定listener.security.protocol.map参数告诉这个协议底层使用了哪种安全协议,比如指定listener.security.protocol.map=CONTROLLER:PLAINTEST表示CONTROLLER这个自定义协议底层使用明文不加密传输数据。

至于三元组的主机名和端口号则比较直观,不需要做过多解释。不过有个事情你还是要注意一下,经常有人会问主机名这个设置中我到底使用IP还是主机名。这里给出统一建议:**最好全部使用主机名,即Broker端和Client端应用配置中全部填写主机名。**Broker源代码中也使用的是主机名,如果你在某些地方使用了IP地址进行连接,可能会发生无法连接的问题。

第四组参数是关于Topic管理的。

我来讲讲下面三个参数:

  • auto.create.topic.enable:是否允许自动创建Topic。
  • unclean.leader.election.enable:是否允许Unclean Leader选举
  • auto.leader.rebalance.enable:是否允许定期进行Leader选举。

还是一个一个说。

auto.create.topic.enable参数我建议设置成false,即不允许自动创建Topic。在我们的线上环境里面有很多名字稀奇古怪的Topic,我想大概都是因为这个参数设置成了true的缘故。

你可能有这样的经历,要为名为test的Topic发送事件,但是不小心拼写错误了,把test写操了tst,之后启动了生产者程序。恭喜你,一个名为tst的Topic就被自动创建了。

所以我一直相信好的运维应该防止这种情形的发生,特别是对于那些大公司而言,每个部门被分配的Topic应该由运维严格把控,决不允许自行创建任何Topic。

unclean.leader.election.enable是关闭Unclean Leader选举,所谓Unclean?还记得Kafka有多个副本这件事吗?每个分区都有多个副本来提供高可用。在这些副本中只能有一个副本对外提供服务,即所谓的Leader副本。

那么问题来了,这些副本都有资格竞争Leader吗?显然不是,只有保存数据比较多的那些副本才有资格竞选,那些落后进度太多的副本没资格做这件事。

好了,现在出现这种情况了:假设那些保存比较多的副本都挂了怎么办?我们还要不要进行Leader选举了?此时这个参数就排上了用场。

如果设置成false,那么就坚持之前的原则,坚决不能让那些落后太多的副本竞争Leader。这样做的后果是这个分区就不能用了,因为没有Leader了。反之如果是true,那么Kafka允许你从那些“跑得慢”的副本中选一个出来当Leader,这样做的后果是数据有可能就丢失了,因为这些副本保存的数据本来就不全,当了Leader之后它本人就会变得膨胀了,认为自己的数据才是权威。

这个参数在最新版的Kafka中默认就是false,本来不需要特意提的,但是比较搞笑的是社区对这个参数的默认值来来回回改了好几版。建议还是显式地把它设置成false吧。

auto.leader.rebalance.enable的影响貌似没什么人提,但其实对生产环境影响非常大,设置它的值为true表示允许Kafka定期地对一些Topic分区进行Leader重选举,当然这个重选举不是无脑进行的,它要满足一定的条件才会发生。严格来说它于上一个参数中的Leader选举的最大不同在于,它不是选举Leader,而是换Leader!比如 Leader A 一直表现得很好,但若auto.leader.rebalance.enable=true,那么有可能一段时间后 Leader A 就要被强行卸任换成 Leader B。

你要知道换一次Leader代价很高的,原本向A发送请求的所有客户端都要切换成向B发送请求,而且这种换Leaader本质上没有任何性能收益,因此建议生产环境把这个参数设置成false。

最后一组参数是数据留存方面的。
  • log.retention.{hours|minutes|ms}:这是个“三兄弟”,都是控制一条消息数据被保存多长时间。从优先级上来说ms设置最高、minutes次之、hours最低。
  • log.retention.bytes:这是指定Broker为消息保存的总磁盘容量大小。
  • message.max.bytes:控制Broker能够接收的最大消息大小。

先说这给“三兄弟”,虽然ms设置有最高的优先级,但是通常情况下我们还是设置hours基级别的多一些,比如log.retention.bytes=168表示默认保存七天的数据,自动删除7天前的数据。很多公司把Kafka当作存储来使用,这个值就要相应地调大。

其次是这个log.retention.bytes。这个值默认是-1,表明你想在这台Broker上保存多少数据都是可以的,至少在容量方面Broker绝对为你开绿灯,不会做任何阻拦。这个参数真正发挥作用的场景其实是在云上构建多租户的Kafka集群,设想你要做一个云上的Kafka服务,每个租户只能使用100GB的磁盘空间,为了避免有个恶意租户使用过多的磁盘空间,设置这个参数就显得至关重要了。

最后说message.max.bytes。实际上今天我和你说的重要参数都是指那些不能使用默认值的参数,这个参数也是一样,默认的1000012太少了,还不到1MB,实际场景突破1MB的消息都是屡见不鲜的,因此在线上设置一个比较大的值还算比较保险的做法。毕竟它只是一个标尺而已,仅仅衡量Broker能够处理的最大消息大小,即使设置大一点也不会消耗什么磁盘空间。

学习来源: 极客时间 《Kafka核心技术与实战》学习笔记 Day10

有关最重要的Kafka集群参数配置的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  2. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  3. ruby - 如何在 Ruby 中拆分参数字符串 Bash 样式? - 2

    我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"

  4. ruby-on-rails - 独立 ruby​​ 脚本的配置文件 - 2

    我有一个在Linux服务器上运行的ruby​​脚本。它不使用rails或任何东西。它基本上是一个命令行ruby​​脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg

  5. ruby - 检查方法参数的类型 - 2

    我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)

  6. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

  7. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  8. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  9. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  10. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

随机推荐