Kafka 分布式消息对列

Kafka 概述

Kafka 是一个分布式消息队列,用于构建实时数据管道流处理。它具有横向扩展能力、容错能力,并且运行速度快,能够在上千台服务器上运行

Kafka的整体结构

kafka_7
在这里插入图片描述

  • Producer消息生产,就是向 kafka broker 发消息的客户端
  • Consumer消息消费,向 kafka broker 取消息的客户端
  • Topic主题Kafka根据主题对消息进行分类,在 Kafka中的每条消息都有Topic。在我们使用时会将不同类型的数据设置为不同的主题
  • Partition消息分区Kafka会将接收到的消息进行分区,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列partition 中的每条消息都会被分配一个有序的 id(offset)kafka 只保证按一个 partition 中的顺序将消息发给 consumer,不保证多个 partition 间的顺序
  • Consumer Group (CG):这是 kafka 用来实现topic 消息广播(发给所有的 consumer)的手段。一个 topic 可以给多个 CGtopic 的消息会发送到到所有的 CG 。用 CG 还可以将 consumer 进行自由的分组而不需要多次发送消息到不同的 topic
  • Broker消息中间件处理节点,一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。
    broker存储topic的数据。如果某topicNpartition,集群有Nbroker,那么每个broker存储该topic的一个partition(避免broker数少于N
  • ReplicationKafka 支持以 Partition 为单位对 Message 进行冗余备份,每个 Partition 都可以配置至少 1 个 Replication(当仅 1 个 Replication 时即仅该 Partition 本身)
  • Leader:每个 Replication 集合中的 Partition 都会选出一个唯一的 Leader,所有的读写请求都由Leader 处理。其他 ReplicasLeader 处把数据更新同步到本地。每个 Cluster 当中会选举出一个 Broker 来担任 Controller,负责处理 PartitionLeader 选举,协调 Partition 迁移等工作
  • ISR(In-Sync Replica):是 Replicas 的一个子集,表示目前 Alive 且与 Leader 能够Catch-upReplicas 集合。读写都是首先落到Leader

Kafka的选举机制:
首先Kafka会将接收到的消息分区(partition),每个主题(topic)的消息有不同的分区。
为了保证高可用,每个分区都会有一定数量的副本(replica)。这样如果有部分服务器不可用,副本所在的服务器就会接替上来,保证应用的持续性。
为了保证较高的处理效率,消息的读写都是在固定的一个副本上完成。这个副本就是所谓的Leader,而其他副本则是Follower。而Follower则会定期地到Leader上同步数据。

Kakfa Broker集群受Zookeeper管理(为什么先安装Zookeeper):
所有的Kafka Broker节点一起去Zookeeper上注册一个临时节点,因为只有一个Kafka Broker会注册成功,其他的都会失败,
这个成功在Zookeeper上注册临时节点的这个Kafka Broker会成为Kafka Broker Controller,其他的Kafka brokerKafka Broker follower。(这个过程叫ControllerZooKeeper注册Watch)。

一旦有一个broker宕机了怎么办:
这个kafka broker controller会读取该宕机broker上所有的partitionzookeeper上的状态,并选取ISR列表中的一个replica作为partition leader,这个broker宕机的事情,kafka controller也会通知zookeeperzookeeper就会通知其他的kafka broker

Kafka 安装和使用

  1. 安装Zookeeper
    0.1 下载zookeeper,配置ZK_HOME
    0.2 进入conf目录,创建配置文件

    cd  cd /usr/local/zookeeper/conf/    
    
    cp zoo_sample.cfg zoo.cfg   # conf目录下默认没有zoo.cfg,需要把zoo_sample.cfg改为zoo.cfg文件	
    # zk启动的时候会加载zoo.cfg	
    
    修改zoo.cfg 中的  dataDir=/xxx  项    # (默认为系统tmp目录下,重启后数据会丢失)
    
    修改 server.A=B:C:D 项  #  A代表这是第几号服务器;B是服务器的IP地址;C表示服务器与群集中的“领导者”交换信息的端口;当领导者失效后,D表示用来执行选举时服务器相互通信的端口。例:
    server.1=192.168.100.10:2888:3888   #  第一个服务器使用
    server.2=192.168.100.11:2888:3888   # 第二个服务器使用
    server.3=192.168.100.12:2888:3888  # 第三个服务器使用
    # 每个集群机上 一个  server.A=B:C:D 项 
    

    0.3 启动zookeeper集群
    在三个服务器上都执行:

    cd /usr/local/zookeeper/bin
    zkServer.sh start    # 启动zookeeper的服务端
    

    0.4 验证

    ./zkServer.sh  status
    

    0.5 测试连接服务端

    zkCli.sh  -server 192.168.100.10:2181#启动zookeeper的客户端 
    
  2. 下载 Kafka

  3. 解压

    tar -xzf kafka_2.12-2.4.1.tgz  -C /安装路径
    cd /安装路径
    ln -s kafka_2.12-2.4.1/ kafka  # 创建硬链接
    
  4. 修改配置文件

    cd /config
    vim server.properties 
    
    # 单节点单broker  设置
    broker.id=0 在集群中需要设置为唯一的
    log.dir=/日志路径/日志名
    
    # 单节点多broker 设置
    config/server-1.properties: 
        broker.id=1 
        listeners=PLAINTEXT://:9093 
    	log.dir=/日志路径/日志名-1
    
    config/server-2.properties: 
        broker.id=2 
        listeners=PLAINTEXT://:9094 
        log.dir=/日志路径/日志名-2
    
    # zookeeper集群 另外需要修改	
    	zookeeper.connect=192.168.100.10:2181,192.168.100.11:2181,192.168.100.12:2181 
    
  5. 启动 Kafka 自带的 zookeeper,以守护进程运行

    kafka安装路径/bin/zookeeper-server-start.sh -daemon kafka安装路径/config/zookeeper.properties
    

    注意:若在kafka 目录下 则不需要填写/kafka安装路径
    在这里插入图片描述

  6. 现在启动Kafka服务器 (集群则每台服务器都启动后台启动)
    /bin/kafka-server-start.sh -daemon /config/server.properties

  7. 创建topic
    创建一个单分区和三个副本创建topic

    ./bin/kafka-topics.sh --create --zookeeper 192.168.100.10:2181,192.168.100.11:2181,192.168.100.2012:2181 --replication-factor 3 --partitions 1 --topic test
    
    1. --zookeeperzk集群服务器的IP:端口
    2. --replication-factor:副本数量
    3. --partitions:分区数量
    4. --topictopic的名称

    现在,可以运行list topic命令,查看创建的所有topic

    bin/kafka-topics.sh --list --zookeeper 192.168.100.10:2181,192.168.100.11:2181,192.168.100.12:2181
    

    除了手动创建topic外,发布不存在的topic时也会自动创建topic(在代理配置中设置)

  8. 开启消费者与生产者(脚本模式开启)

    开启消费者
    /脚本路径/消费者脚本.sh --zookeeper 192.168.100.10:2181,192.168.100.11:2181,192.168.100.12:2181 --topic  topic名  # 若指定的topic名不存在,则取不到数据
    # --from-beginning:从最开始生产队的数据开始消费
    
    开启生产者(--sync表示异步开启)
    /脚本路径/生产者者脚本.sh --broker-list 192.168.100.10:2181,192.168.100.11:2181,192.168.100.2012:2181 --sync --topic topic名  # 若指定的topic名不存在,则新建 
    

Kafka python API

kafka-python 文档
下载安装kafka-pythonpip install kafka-python
验证是否安装成功:import kafka

创建生产者

三个基本属性:

  • bootstrap.servers:属性值是一个host:portbroker列表。
  • key.serializer:因此需要将这些对象序列化成字节数组key.serializer指定的类需要实现org.apache.kafka.common.serialization.Serializer接口,Kafka客户端包中包含了几个默认实现,例如ByteArraySerializerStringSerializerIntegerSerialier
  • value.serializer:属性值是类的名称。这个属性指定了用来序列化消息记录的类,与key.serializer差不多
  1. 发送普通字节数据的生产者

    >>> from kafka import KafkaProducer   # 导入KafkaProducer
    >>> producer = KafkaProducer(bootstrap_servers='IP地址:端口')  # 创建生产者实例
    >>> for _ in range(100):
    ...     producer.send('topic名', b'普通数据')   # 循环发送100次字节数据
    
  2. 发送json字符串 的生产者

    # 建立KafkaProducer的实例
    # value_serializer:将数据进行序列化的操作
    # lambda v: json.dumps(v).encode('utf-8'):将传入的数据转换为json,并进行utf-8编码
    >>> producer = KafkaProducer(bootstrap_servers='IP地址:端口',value_serializer=lambda v: json.dumps(v).encode('utf-8'))
    #发送一个字典数据
    >>> producer.send('topic名', {'key1': 'value1'})
    <kafka.producer.future.FutureRecordMetadata object at 0x2a9ebd0>
    >>>
    
  3. 压缩式字节数据 的生产者

    compression_type:压缩类型,'gzip'
    
    # 建立生产者和broker进程的连接,指定要锁方式为gzip
    >>> producer = KafkaProducer(bootstrap_servers='IP地址:端口',compression_type='gzip')
    # 发送数据
    >>> producer.send('topic名', b'要压缩的普通大数据')
    
  4. 发送 格式化 string keys 的生产者

    >>> producer = KafkaProducer(key_serializer=str.encode)
    >>> producer.send('topic名', key='ping', value=b'1234')
    
  5. 生产者的更多功能

    # 阻塞,直到发送一条消息(或超时)
    future = producer.send('foobar', b'another_message')
    result = future.get(timeout=60)
    
    # 阻塞,直到所有未决消息至少都发送到网络上
    # 请注意:这并不保证发送成功!只有在使用linger_ms配置内部批处理时才有用
    producer.flush()
    
    # 包括 record headers 。格式是包含字符串键和字节值的元组列表
    producer.send('topic',value = b'c29tZSB2YWx1ZQ ==',headers = [('content-encoding',b'base64')]))
    
    # 获取生产者绩效指标
    metrics = producer.metrics()
    

创建消费者

  1. 创建普通消费者

    >>> from kafka import KafkaConsumer    # 导入KafkaConsumer    
    >>> consumer = KafkaConsumer('topic名')   # 创建消费者实例,并指定topic名
    >>> for msg in consumer:               # 从消息对列取数据
    ...     print (msg)
    
    # 访问 record headers。返回的值是一个带有字符串键和字节值的元组列表
    >>> for msg in consumer:
    ...     print (msg.headers)
    
    # 获取消费者指标
    metrics= Consumer.metrics()
    
  2. 加入一个消费者组以进行 动态 分配 partitionoffset

    >>> from kafka import KafkaConsumer
    >>> consumer = KafkaConsumer('topic名', group_id='my_favorite_group')
    >>> for msg in consumer:
    ...     print (msg)
    
  3. 手动为消费者 分配 partition列表

    >>> from kafka import TopicPartition
    >>> consumer = KafkaConsumer(bootstrap_servers='IP地址:端口')
    >>> consumer.assign([TopicPartition('topic名', 2)])
    >>> msg = next(consumer)
    
  4. 反序列化msgpack-encoded的值

    >>> consumer = KafkaConsumer(value_deserializer=msgpack.loads)
    >>> consumer.subscribe(['msgpackfoo'])
    >>> for msg in consumer:
    ...     assert isinstance(msg.value, dict)
    

Kafka-python Demo

目的:利用producer将某个目录下的所有文件名发送到指定topic,并由consumer来接收

  1. 创建producer
from kafka import KafkaProducer
import json
import os
import time
from sys import argv
# 建立Producer与Kafka进程的连接
producer = KafkaProducer(bootstrap_servers='node-teach:9092')

# 定义一个日志输出方法,进行日志格式化输出
def log(str):
    t = time.strftime(r"%Y-%m-%d_%H-%M-%S",time.localtime())
    print("[%s]%s"%(t,str))
# 查看目录下的所有文件并利用生产者进行数据生产。
def list_file(path):
    dir_list = os.listdir(path);
    for f in dir_list:
         producer.send('test_topic',f.encode())
         producer.flush()
         log('send: %s' % (f))    

list_file(argv[1])
# 关闭生产者的连接
producer.close()
log('done')
  1. 创建consumer
from kafka import KafkaConsumer
import time

# 格式化输出日志
def log(str):
        t = time.strftime(r"%Y-%m-%d_%H-%M-%S",time.localtime())
        print("[%s]%s"%(t,str))

log('start consumer')
# 消费node-teach:9092上的test_topic 这个Topic
consumer=KafkaConsumer('test_topic',bootstrap_servers=['node-teach:9092'])
for msg in consumer:
        recv = "%s:%d:%d: key=%s value=%s" %(msg.topic,msg.partition,msg.offset,msg.key,msg.value)
        log(recv)

当前的consumer只能接收自身启动后的数据,如果需要启动后将之前的数据全部接收,需要设置consumergroup_idkafka内部会将数据广播到分组上,组会将数据转接到对应的consumer上。

consumer=KafkaConsumer('test_topic',group_id='test_group_id',bootstrap_servers=['node-teach:9092'])
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值