Kafak权威指南学习笔记

Kafak权威指南学习笔记

初识Kafka

发布与订阅消息系统

Kafka 是一款基于发布与订阅的梢息系统。它 般被称为“分布式提交日志”或者“分布式流平台”。
文件系统或数据库提交日志用来提供所有事务的持久记录 通过重放这些日志可以重建系统的状态。同样地, Kafka 的数据是按照一定顺序持久化保存的,可以按需读取 此外,Kafka 的数据分布在整个系统里,具备数据故障保护和性能伸缩能力

消息和批次
  • Kafka 的数据单元被称为消息
  • 为了提高效率,消息被分批次写入 Kafka 批次就是一组消息
主题和分区

Kafka 的悄息通过主题进行分类。主题就好比数据库的表,或者文件系统里的文件夹。主
题可以被分为若干个分区 。消息以追加的方式写入分区,然后以先入先出的顺序读取。要注意,由于一个主题一般包含几个分区,因此无法在整个主题范围内保证消息的顺序,但可以保证消息在单个分区内的顺序。 Kafka 通过分区来实现数据冗余和伸缩性。分区可以分布在不同的服务器上,也就是说, 主题可以横跨多个服务器,以此来提供比个服务器更强大的性能

生产者和消费者

生产者创建消息。一般情况下,一个消息会被发布到一个特定的主题上。生产者在默认情况下把消息均衡地分布到主题的所有分区上,而并不关心特定消息会被写到哪个分区。不过,在某些情况下,生产者会把消息直接写到指定的分区。这通常是通过消息键和分区器来实现的,分区器为键生成一个散列值,并将其映射到指定的分区上。这样可以保证包含同一个键的消息会被写到同一个分区上。生产者也可以使用自定义的分区器,根据不同的业务规则将消息映射到分区

消费者读取消息。消费者订阅一个或多个主题,并按照消息生成的顺序读取它们。消费者通过检查消息的偏移盘来区分已经读取过的消息。 偏移量是另一种元数据,它是一个不断递增的整数值,在创建消息时, Kafka 会把它添加到消息里。在给定的分区里,每个悄息的偏移量都是唯 的。消费者把每个分区最后读取的悄息偏移量保存在 Zookeeper Kafka 上,如果悄费者关闭或重启,它的读取状态不会丢失。

消费者是消费者群组的一部分,也就是说,会有一个或多个消费者共同读取 个主题
组保证每个分区只能被一个消费者使用

broker和集群

一个独立的 Kafka 服务器被称为 broker。 broker 接收来自 生产者的消息,为消息设置偏移量,并提交消息到磁盘保存。 broker 为消费者提供服务,对读取分区的请求作出响应,返回已经提交到磁盘上的消息。

broker 是集群的组成部分。每个集群都有 broker 同时充当了集群控制器的角色(自动
从集群的活跃成员中选举出来)。控制器负责管理工作,包括将分区分配给 broker 和监控
broker. 在集群中, 一个分区从属于 broker, broker 被称为分区的首领

分区复制: 一个分区可以分配给多个 broker ,这个时候会发生分区复制

安装 kafak

Kafka 安装及快速入门

验证 Zookeeper 是否安装正确: telnet localhost 2181 输入: srvr

向Kafka 写入数据

在这里插入图片描述
(1):创建一个ProducerRecord 对象开始, ProducerRecord 对象需要包含目标主题和要发
的内容。我们还可以指定键或分区。在发送 Producer ecord 对象时,生产者要先把键和
值对象序列化成字节数组,这样它们才能够在网络上传输

(2):数据被传给分区器。如果之前在 Produc rR cord 对象里指定了分区,那么分区器
就不会再做任何事情,直接把指定的分区返回。如果没有指定分区 ,那么分区器会根据
创建一个ProducerRecord对象的键来选择 个分区。选好分区以后 ,生产者就知道该往哪个主题和
分区发送这条记录了。紧接着,这条记录被添加到 个记录批次里,这个批次里的所有消
息会被发送到相同的主题和分区上。有 个独立的线程负责把这些记录批次发送到相应的
broker上

(3): 服务器在收到这些消息时会返回一个响应。如果消息成功写入 Kafka ,就返回
RecordMetaData 对象,它包含了主题和分区信息,以及记录在分区里的偏移量。如果写入
失败, 会返回一个错误。生产者在收到错误之后会尝试重新发送消息,几次之后如果还
是失败, 就返回错误信息

创建Kafka生产者

原生命令创建生成者
bin/kafka-console-producer.sh --broker-list localhost:9092 --topic Hello-Kafka
Python创建生成者

kafka-python API参考


#-*- encoding:utf-8 -*-
from kafka import KafkaProducer

import json

producer = KafkaProducer(bootstrap_servers=['127.0.0.1:9092'])
for i in range(0, 100):
    producer.send('Hello-Kafka', value=b'de msg', key=None, headers=None, partition=None, timestamp_ms=None)

# Block直到单条消息发送完或者超时
future = producer.send('Hello-Kafka', value=b'another msg',key=b'othermsg')
result = future.get(timeout=60)
print result

# 序列化json数据
# broker 希望接收到的消息的键和值都是字节数组
producer = KafkaProducer(bootstrap_servers='127.0.0.1:9092', value_serializer=lambda v: json.dumps(v).encode('utf-8'))
producer.send('Hello-Kafka', {'Hello-Kafka':'kafka'})

# 序列化字符串key
# broker 希望接收到的消息的键和值都是字节数组
producer = KafkaProducer(bootstrap_servers='127.0.0.1:9092', key_serializer=str.encode)
producer.send('Hello-Kafka', b'Hello-Kafka', key='strKey')
producer = KafkaProducer(bootstrap_servers='127.0.0.1:9092',compression_type='gzip')
for i in range(2):
    producer.send('Hello-Kafka', ('msg %d' % i).encode('utf-8'))
# 消息记录携带header
producer.send('Hello-Kafka', value=b'c29tZSB2YWx1ZQ==', headers=[('content-encoding', b'base64'),])
# 获取性能数据
metrics = producer.metrics()
print metrics
producer.flush()
生产者的配置

使用 python 的kafka-python库

更多参数见:KafkaProducer-文档

acks

如果 acks=0 生产者在成功写入悄息之前不会等待任何来自服务器的响应

如果 acks=1 ,只要集群的首领节点收到消息,生产者就会收到 个来自服务器的成功

响应。如果消息无撞到达首领节点(比如首领节点崩愤,新的首领还没有被选举出来),生产者会 个错误响应,为了避免数据丢失,生产者会重发消息

如果 acks=all ,只有 所有参与复制的节点全部收到消息时,生产者才会一个来自
服务器的成功响应。这种模式是最安全的

buffer_memory

该参数用来设置生产者内存缓冲区的大小,生产者用它缓冲要发送到服务器的消息

compression_type

默认情况下,消息发送时不会被压缩。该参数可以设置为 snappy gzi.p lz ,它指定了
消息被发送给 broker 之前使用哪一种压缩算也进行压缩

retries

生产者从服务器收到的错误有可能是临时性的错误(比如分区找不到首领)。在这种情况
下, etri.es 参数的值决定了生产者可以重发消息的次数

batch_size

当有多个消息需要被发送到同 个分区时,生产者会把它们放在罔 个批次里。该参数指
定了一个批次可以使用的内存大小,按照字节数计算(而不是消息个数)。当批次被填满,
批次里的所有消息会被发送出去。不过生产者井不一定都会等到批次被填满才发送,半捕
的批次,甚至只包含一个消息的批次也有可能被发送。所以就算把批次大小设置得很大,
也不会造成延迟,只是会占用更多的内存而已。但如果设置得太小,因为生产者需要更频
繁地发送消息,会增加 些额外的开销

linger_ms

该参数指定了生产者在发送 次之前等待更多消息加入批次的时间

client_id

该参数可以是任意的字符串,服务器会用它来识别消息的来橱,还可以用在日志和配额指
标里

max_in_flight_requests_per_connection

该参数指定了生产者在 到服务器晌应之前可以发送多少个消息。它的值越高,就会占用
越多的内存,不过 会提升吞吐量。 它设为 可以保证消息是按照发送的顺序 入服
器的,即使发生了重试

序列化器

生产者要先把键和值对象序列化成字节数组,这样它们才能够在网络上传输, 所以我们需要把传输的数据给序列化

自定义序列化器

Python 中的对象序列化: 使用 pickle 模块

分区

在之前的例子里, kafak 消息包含了目标主题、键和值。 Kafka 的消息是一个个
键值对, kafak 消息可以只包含目标主题和值,键可以设置为默认的 null ,不
过大多数应用程序会用到键。键有两个用途 :可以作为消息的附加信息,也可以用来
决定消息该被写到主题的哪个分区。

Python 发送带Key的kafak 消息


#-*- encoding:utf-8 -*-
from kafka import KafkaProducer

import json

producer = KafkaProducer(bootstrap_servers=['127.0.0.1:9092'])
future = producer.send('Hello-Kafka', value=b'another msg',key=b'othermsg')
result = future.get(timeout=60)
print result
关于Key

如果键值为 None 井且使用了默认的分区器,那么 录将被随机地发送到主题内各个可用
的分区上。分区器使用轮询(Round Robin )算陆将消息均衡地分布到各个分区上。

如果键不为空,并且使用了默认的分区器,那么 Kafka 会对键进行散列(使用 Kafka 自己
的散列算棒,即使升级 Java 版本,散列值也不会发生变化),然后根据散列值把消息映射到特定的分区上。

实现自定义分区策略

防止一些数据量较大的消息与其他消息量小的消息写在一份分区内,造成分区较大

在生成producer指定partitioner, 如何写自定义的,可以参考: DefaultPartitioner, 下面代码中写的也是kafka-python中的源码


from kafka import KafkaProducer

import json

class DefaultPartitioner(object):
    """Default partitioner.
    Hashes key to partition using murmur2 hashing (from java client)
    If key is None, selects partition randomly from available,
    or from all partitions if none are currently available
    """
    @classmethod
    def __call__(cls, key, all_partitions, available):
        """
        Get the partition corresponding to key
        :param key: partitioning key
        :param all_partitions: list of all partitions sorted by partition ID
        :param available: list of available partitions in no particular order
        :return: one of the values from all_partitions or available
        """
        if key is None:
            if available:
                return random.choice(available)
            return random.choice(all_partitions)

        idx = murmur2(key)
        idx &= 0x7fffffff
        idx %= len(all_partitions)
        return all_partitions[idx]

def murmur2(data):
    """Pure-python Murmur2 implementation.
    Based on java client, see org.apache.kafka.common.utils.Utils.murmur2
    Args:
        data (bytes): opaque bytes
    Returns: MurmurHash2 of data
    """
    # Python2 bytes is really a str, causing the bitwise operations below to fail
    # so convert to bytearray.
    if six.PY2:
        data = bytearray(bytes(data))

    length = len(data)
    seed = 0x9747b28c
    # 'm' and 'r' are mixing constants generated offline.
    # They're not really 'magic', they just happen to work well.
    m = 0x5bd1e995
    r = 24

    # Initialize the hash to a random value
    h = seed ^ length
    length4 = length // 4

    for i in range(length4):
        i4 = i * 4
        k = ((data[i4 + 0] & 0xff) +
            ((data[i4 + 1] & 0xff) << 8) +
            ((data[i4 + 2] & 0xff) << 16) +
            ((data[i4 + 3] & 0xff) << 24))
        k &= 0xffffffff
        k *= m
        k &= 0xffffffff
        k ^= (k % 0x100000000) >> r # k ^= k >>> r
        k &= 0xffffffff
        k *= m
        k &= 0xffffffff

        h *= m
        h &= 0xffffffff
        h ^= k
        h &= 0xffffffff

    # Handle the last few bytes of the input array
    extra_bytes = length % 4
    if extra_bytes >= 3:
        h ^= (data[(length & ~3) + 2] & 0xff) << 16
        h &= 0xffffffff
    if extra_bytes >= 2:
        h ^= (data[(length & ~3) + 1] & 0xff) << 8
        h &= 0xffffffff
    if extra_bytes >= 1:
        h ^= (data[length & ~3] & 0xff)
        h &= 0xffffffff
        h *= m
        h &= 0xffffffff

    h ^= (h % 0x100000000) >> 13 # h >>> 13;
    h &= 0xffffffff
    h *= m
    h &= 0xffffffff
    h ^= (h % 0x100000000) >> 15 # h >>> 15;
    h &= 0xffffffff

    return h


producer = KafkaProducer(bootstrap_servers=['127.0.0.1:9092'], partitioner=DefaultPartitioner())
future = producer.send('Hello-Kafka', value=b'another msg',key=b'othermsg')
result = future.get(timeout=60)
print result

从Kafka 读取数据

KafkaConsumer概念
消费者和消费者群组

Kafka 消费者从属于消费者群组。一个群组里的消费者订阅的是同 个主题,每个消费者
接收主题 部分分区的消息。

假设主题 Tl 4个分区,我们创建了消费者 Cl ,它是群组 Gl 里唯 的消费者,我们用
它订阅主题 Tl 。消费者 Cl 将收到主题 Tl 全部 个分区的消息,如图所示。
在这里插入图片描述
如果在群组 Gl 里新增 个消费者 C2 ,那么每个消费者将分别从两个分区接收消息。我们
假设消费者 Cl 接收分区0 和分区2的消息,消费者 C2 接收分区1 和分区 3的消息,如图所示。
在这里插入图片描述

如果群组 4个消费者,那么每个消费者可以分配到1个分区

在这里插入图片描述
注意

  • 如果消费者的数据超过分区数量,则会有消费者被闲置,不能收到消息
  • 不同的消费者群组是独立的,没有关系
消费者群组和分区再均衡

群组里的消费者共同读取主题的分区。一个新的悄费者加入群组时,它读取的是原本由其他消费者读取的消息。当一个消费者被关闭或发生崩愤时,它就离开群组,原本由它读取的分区将由群组里的其他消费者来读取。在主题发生变化时 比如管理员添加了新的分区,会发生分区重分配。

分区的所有权从 个消费者转移到另 个消费者,这样的行为被称为再均衡

在再均衡期间,消费者无法读取消费,造成整个群组一小段时间内的不可用。

创建Kafka 消费者

Python脚本消费kafka数据

from kafka import KafkaConsumer

consumer = KafkaConsumer('test',
                         group_id='my-group',
                         bootstrap_servers=['172.21.10.136:9092'])
for message in consumer:
    print ("%s:%d:%d: key=%s value=%s" % (message.topic, message.partition,
                                          message.offset, message.key,
消费者的配置

消费者的配置,见: kafka-python KafkaConsumer

partition_assignment_strategy

Range: 该策略会 主题 若干 连续的分区分配给消费者。

RoundRobin: 该策略把主题的所有分区逐个分配给消费者。

提交和偏移量

每次调用 poll () 方毡,它总是返回由生产者写入 Kafka 但还没有被消费者读取过的记录

我们把更新分区当前位置的操作叫作提交

那么消费者是如何提交偏移量的呢?消费者往一个叫作 _consumer_offset 特殊主题发送
消息,消息里包含每个分区的偏移量 如果消费者一直处于运行状态,那么偏移量就没有
什么用处。不过,如果悄费者发生崩愤或者有新的消费者加入群组,就会触发再均衡,完
成再均衡之后,每个消费者可能分配到新的分区,而不是之前处理的那个。为了能够继续
之前的工作,消费者需要读取每个分区最后一次提交的偏移量,然后从偏移量指定的地方
继续处理。

  • 自动提交: enable_auto_commit=True,默认也是 True, 自动提交间隔由auto_commit_interval_ms 设置,默认5s.

    • 影响: 在发生再均衡时,消费者会从最后一次提交的offset消费,会造成消息重复消费
  • 手动提交

    • 使用commit_async: 异步提交last_offset + 1
    • 使用commit: 同步提交last_offset + 1

from kafka import KafkaConsumer
tp='Hello-Kafka'
group_id = 'test-id'
consumer = KafkaConsumer(tp,bootstrap_servers='localhost:9092', group_id=group_id)
i = 0
for msg in consumer:
    i = i +1
    k,v = msg.key,msg.value
    print k,v
    # 同步提交
    # consumer.commit()
    # 异步提交
    consumer.commit_async()
为自己分配分区

不订阅主题,消费者自己为自建分配分区, 可以使用partitions_for_topic先获取到分区信息,再使用assign 订阅既可

深入Kafka

Kafka 如何进行复制

Kafka 如何处理来自生产者和消费者的请求

Kafka 的存储细节,比如文件格式和索引

集群成员关系

Kafka 使用 Zookeeper 来维护集群成员的信息。每个 broker 都有 个唯 标识符,这个
标识符可以在配置文件里指定 ,也可以自动生成。在 broker 启动的时候,它通过创建
临时节点把自己的 ID 注册到 Zookeeper Kafka 组件订阅 Zookeeper 的/brokers/ids 路径
(broker Zookeeper 上的注册路径),当有 broker 加入集群或退出集群时,这些组件就
可以获得通知。

控制器

负责分区首领的选举,其他 broker 在控制器节点上创建 Zookeeper watch 对象来获取控制器节点是否异常

复制

首领副本: 每个分区都有一个首领副本 为了保证 致性,所有生产者请求和消费者请求都会经过
这个副本。

跟随者副本: 首领以外的副本都是跟随者副本。跟随者副本不处理来自客户端的请求,它们唯 的任
务就是从首领那里复制消息,保持与首领一致的状态。如果首领发生崩渍,其中的跟随者会被提升为新首领。

物理存储

Kafka 的基本存储单元是分区。分区无法在多 broker 间进行再细分,也无法在同一个broker 的多个磁盘上进行再细分

可靠的数据传递

ACID:原子性 一致性、隔离性和持久性

Kafka 可以在哪些方面作出保证

1.Kafka 可以保证分区消息的顺序。

2.只有当消息被写入分区的所有同步副本时(但不一定要写入磁盘),它才被认为是“
提交”的。

3.只要还有 个副本是活跃的,那么已经提交的消息就不会丢失

4.消费者只能读取已经提交的悄息

复制

Kafka 的主题被分为多个分区 ,分区是基本的数据块。分区存储在单个磁盘上, Kafka 可以
保证分区里的事件是有序的,分区可以在线(可用),也可以离线(不可用) 。每个分区可
以有多个副本,其中 个副本是首领。所有的事件都直接发送给首领副本,或者直接从首
领副本读取事件。其他副本只需要与首领保持同步,并及时复制最新的事件。当首领副本
不可用时,其中一个同步副本将成为新首领。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CoLiuRs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值