Kafka是⼀个分布式、分区的、多副本的、多⽣产者、多订阅者,基于zookeeper协调的分布式⽇志系统(或消息系统),可用作消息中间件。
常⻅可以⽤于高吞吐要求的:web/nginx⽇志处理和聚合,消息服务等等。因此是生产中常见的中间件,我们有必要对其研究。
优势:
劣势:
对比:
应用场景 | 描述 |
---|---|
⽇志收集 | 可以⽤Kafka收集各种服务的Log |
消息系统 | 解耦⽣产者和消费者、缓存消息等,实现异步解耦削峰 |
⽤户活动跟踪 | 记录Web⽤户或者App⽤户的各种活动,如浏览⽹⻚、搜索、点击等活动,这些活动信息被各个服务器发布到Kafka的Topic中,然后消费者通过订阅这些Topic来做实时的监控分析 |
运营指标 | Kafka也经常⽤来记录运营监控数据。包括收集各种分布式应⽤的数据,⽣产各种操作的集中反馈,⽐如报警和报告 |
流式处理 | 大数据实时计算。⽐如Spark Streaming和Storm |
broker:
⼀个独⽴的Kafka服务器称为broker。broker接收来⾃⽣产者的消息,为消息设置偏移量,并提交消息到磁盘保存。broker为消费者提供服务,对读取分区的请求做出响应,返回已经提交到磁盘上的消息。单个broker可以轻松处理数千个分区以及每秒百万级的消息量。
每个集群都有⼀个broker是集群控制器(⾃动从集群的活跃成员中选举出来)。负责:将分区分配给broker,监控broker
- Producer创建时,会创建⼀个Sender线程并设置为守护线程。
- ⽣产消息时,内部其实是异步流程;⽣产的消息先经过拦截器->序列化器->分区器,然后将消息缓存在缓冲区(该缓冲区也是在Producer创建时创建)。
- 批次发送的条件为:缓冲区数据⼤⼩达到batch.size或者linger.ms达到上限,哪个先达到就算哪个。
- 批次发送后,发往指定分区,然后落盘到broker;如果⽣产者配置了retrires参数⼤于0并且失败原因允许重
试,那么客户端内部会对该消息进⾏重试。- 落盘到broker成功,返回⽣产元数据给⽣产者。
- 元数据返回有两种⽅式:⼀种是通过阻塞直接返回,另⼀种是通过回调返回。使用send()方法发送消息,它会返回一个Future对象,调用get()方法进行等待, 就可以知道消息是否发送成功
Kafka的数据单元称为消息。可以把消息看成是数据库⾥的⼀个“数据⾏”或⼀条“记录”。消息由字节数组组成。
为了提⾼效率,消息被分批写⼊Kafka。批次就是⼀组消息,这些消息属于同⼀个主题和分区。
主题:
每条发布到Kafka集群的消息都有⼀个类别,这个类别被称为Topic(主题)。
分区:
⼀个分区可以分配给多个broker,此时会发⽣分区复制。
分区的复制提供了消息冗余,⾼可⽤。副本分区不负责处理消息的读写。
ack: 指定了必须要有多少个分区副本收到消息,生产者才会认为消息写入是成功的
buffer.memory:生产者内存缓冲区的大小,生产者用它缓冲要发送到服务器的消息。
如果应用程序发送消息的速度超过发送到服务器的速度,会导致生产者空间不足。这个时候, send() 方法调用要么被阻塞,要么抛出异常,取决于如何设置 block.on.buffer.full/max.block.ms 参数.
compression.type: 默认情况下,消息发送时不会被压缩。
该参数可以设置为 snappy、gzip 或 lz4,它指定了 消息被发送给 broker 之前使用哪一种压缩算法进行压缩.
retries: 生产者可以重发消息的次数
如果达到这个次数,生产者会 放弃重试并返回错误。默认情况下,生产者会在每次重试之间等待 100ms,不过可以通过 retry.backoff.ms 参数来改变这个时间间隔.
batch.size: 指定了一个批次可以使用的内存大小,按照字节数计算(而不是消息个数)。
当批次被填满, 批次里的所有消息会被发送出去。不过生产者并不一定都会等到批次被填满才发送,半满 的批次,甚至只包含一个消息的批次也有可能被发送。所以就算把批次大小设置得很大, 也不会造成延迟,只是会占用更多的内存而已。但如果设置得太小,因为生产者需要更频 繁地发送消息,会增加一些额外的开销.
linger.ms: 指定了生产者在发送批次之前等待更多消息加入批次的时间。
KafkaProducer会在批次填满或linger.ms达到上限时把批次发送出去。默认情况下,只要有可用的线程,生产者就会把消息发送出去,就算批次里只有一个消息。把 linger.ms 设置成比 0 大的数, 让生产者在发送批次之前等待一会儿,使更多的消息加入到这个批次。虽然这样会增加延 迟,但也会提升吞吐量(因为一次性发送更多的消息,每个消息的开销就变小了).
max.in.flight.requests.per.connection: 生产者在收到服务器响应之前可以发送多少个消息。
它的值越高,就会占用 越多的内存,不过也会提升吞吐量。把它设为 1 可以保证消息是按照发送的顺序写入服务 器的,即使发生了重试。
max.block.ms: 调用 send() 方法或使用 partitionsFor() 方法获取元数据时生产者的阻塞时间。
当生产者的发送缓冲区已满,或者没有可用的元数据时,这些方法就会阻塞。在阻 塞时间达到 max.block.ms 时,生产者会抛出超时异常.
max.request.size: 能发送的单个消息的最大值.
request.timeout.ms: 指定了生产者在发送数据时等待服务器返回响应的时间.
metadata.fetch.timeout.ms: 指定了生产者在获取元数据(比如目标分区的首领是谁)时等待服务器 返回响应的时间.
receive.buffer.bytes 和 send.buffer.bytes: 指定了 TCP socket 接收和发送数据包的缓冲区大小。
如果它们被设为 -1, 就使用操作系统的默认值。如果生产者或消费者与 broker 处于不同的数据中心,那么可以 适当增大这些值,因为跨数据中心的网络一般都有比较高的延迟和比较低的带宽
参考资料:
1、kafka介绍及使用场景