hadoop实时day22–Apache Kafka
今日内容大纲
1、消息队列 MQ
模式:点对点 发布订阅
使用场景、好处
2、Apache kafka 介绍
分布式存储消息队列
3、Apache Kafka集群组件 名词
4、Apache Kafka集群搭建
5、Apache kafka工作流程-整体
6、Apache kafka 操作
shell基础操作
java api 操作
生产者代码
消费者代码
7、Apache kafka 分区 副本机制
消息队列
-
基本概念
- 消息:应用程序之间通信传输的数据。
- 队列:先进先出的数据结构。队列能够保证消息的顺序的。
- ==消息队列:Message Queue。简称MQ.==是应用程序之间进行通信的一种方式,可以简单理解为把要传递的消息数据放置在队列中。
- 消息中间件:实现消息队列功能的软件。
- 在java语系中,有一个规范JMS(Java Message Service).描述了消息队列应该具备何种属性何种模型。
- 上面上大多数软件都遵循了JMS规则:ActiveMQ …
- Kafka没有遵循JMS规范。在功能实现上靠近JMS。
-
消息队列应用场景
-
异步处理
-
系统解耦
-
流量削峰
-
大数据领域作为数据源 数据传输存储的中间件
数据源---->kafka---->spark streaming|flink
-
-
消息队列模型–生产者消费模型
-
点对点模式
p2p模型 一对一的模型
-
发布订阅模式
一对多 甚至多对多模型。 topic:消息的不同分类。对消息进行划分。 生产者:发布消息要指定消息所属的主题。 消费者:在消费消息之前必须定义主题 消费指定的主题。
-
Apache kafka
-
kafka介绍
Apache Kafka是一个分布式流平台。一个分布式的流平台应该包含3点关键的能力: 1. 发布和订阅流数据流,类似于消息队列或者是企业消息传递系统 2. 以容错的持久化方式存储数据流 3. 处理数据流
- 当下企业中核心功能还是两个。 分布式消息队列(发布订阅模型) 数据存储
- 未来kafka致力于成为流处理平台 参与实时计算分析领域。
- 核心:kafka集群、生产者、消费者。
- kafka来自于linkedin公司,用于解决linkedin内部消息传递问题。现属于Apache .
- kafka底层是使用scala和java语言实现的。
-
kafka优点
-
分布式。
多台机器存储。 解决了消息数据存储不下的问题。
-
副本机制。
保证了数据安全 可用。
-
高并发
-
API 开发规范成熟。
-
支持消息存储 持久化。
某种程度上kafka也是一个大数据存储软件。
-
kafka 组件、集群结构
-
kafka是一个分布式集群软件。
-
zookeeper集群
当下kafka运行需要依赖zk集群。管理kafka的元数据。
-
broker
在每台机器上运行的kafka进程,叫做broker.称之为kafka代理。 多个broker就组成了kafka集群。
-
producer
生产者。负责将数据写入kafka集群的主题中。必须指定主题。
-
consumer group
消费者组。多个消费者可以组成一个组 共同订阅主题进行消费。 消费者组存在某种程度上还实现了消费者容错机制。一个消费者出了问题。组内其他消费者可以继续代替消息。
-
consumer
消费者。订阅主题消费数据。 通过不断轮询拉取自己订阅的主题消息。
-
topic
消息的主题。对消息按类别进行划分归类。 比如:OrderTopic UserTopic 在发送消息 处理的消息必须要指定消息主题。
-
partition
主题的分区。为什么要把主题分区?解决了一台机器存储不下的问题。 把一个主题的消息进行分区之后 不同的分区就可以存储在kafka不同的broker。
-
Replicas
分区的副本,备份。 为什么要备份? 解决了机器故障导致分区数据容易丢失的问题。 所有的副本中分为两类: leader副本:只有leader才会负责生产者消费者读写请求。 follower副本:只用于同步leader副本的数据。 当leader挂掉之后 成为leader提供服务。容错。
-
-
offset
偏移量。 在一个分区中,消息是有顺序的方式存储着,每个在分区的消费都是有一个递增的id。这个就是偏移量offset。 注意:在分区中 可以保证消息顺序 全局无法保证有序 如果想要消息全局有序?可以把消息分区设置为1. 此外这个offset还影响着消费者从哪里消费数据。可以指定位置读取消息进行消费。
Kafka集群搭建
-
前提:因为kafka依赖zk保存一些元数据。在启动kafka之前保证zk集群启动且服务可用。
-
注意:kafka的消息是直接存储在linux系统上的。所以不需要依赖hadoop。
-
安装包
kafka_2.11-1.0.0.tgz #注意 2.11指的scala版本号。
-
上传解压
-
修改配置文件
-
/export/servers/kafka/config/server.properties
#brokerid 不重复 大于等于0(每台机器不一样) broker.id=0 #kafka消息存储路径 log.dirs=/export/data/kafka #zk集群地址 zookeeper.connect=node-1:2181,node-2:2181 #是否允许直接删除主题数据 delete.topic.enable=true #当前机器的主机名(每台机器不一样) host.name=node-1 #对外暴露的协议(每台机器不一样) listeners=PLAINTEXT://node-1:9092
-
-
scp安装包给其他机器
-
拷贝后, 需要修改每一台的==broker.id 和 host.name和listeners==
-
kafka集群启动
kafka集群启动之前 必须先启动zk集群。 sh startZk.sh
#前端启动 bin/kafka-server-start.sh config/server.properties #后台启动: bin/kafka-server-start.sh -daemon config/server.properties #作业 根据之前的一键启动zk的脚本改写一键启动kafka. #关闭 bin/kafka-server-stop.sh
Kafka shell操作
-
创建主题
bin/kafka-topics.sh --create --topic test1 --partitions 3 --replication-factor 2 --zookeeper node-1:2181,node-2:2181 --create #表示创建主题 --topic #主题的名称 --partitions #主题分区个数 --replication-factor #主题每个分区副本数 --zookeeper
-
列出当前所有主题
bin/kafka-topics.sh --list --zookeeper node-1:2181,node-2:2181
-
查看主题的详细信息
bin/kafka-topics.sh --describe --topic test1 --zookeeper node-1:2181,node-2:2181 Topic:test1 PartitionCount:3 ReplicationFactor:2 Configs: Topic: test1 Partition: 0 Leader: 0 Replicas: 0,1 Isr: 0,1 Topic: test1 Partition: 1 Leader: 1 Replicas: 1,2 Isr: 1,2 Topic: test1 Partition: 2 Leader: 2 Replicas: 2,0 Isr: 2,0
-
增加、修改主题的配置
bin/kafka-topics.sh --zookeeper node01:2181 --alter --topic test --config flush.messages=1 bin/kafka-topics.sh --zookeeper node01:2181 --alter --topic test --delete-config flush.messages 将flush.messages设置为1,那么每一条消息都会刷盘。
-
删除主题
delete.topic.enable=true 然后执行以下命令进行删除topic bin/kafka-topics.sh --delete --topic test2 --zookeeper node-1:2181,node-2:2181 Topic test2 is marked for deletion. Note: This will have no impact if delete.topic.enable is not set to true.
-
增加分区
bin/kafka-topics.sh --zookeeper node-1:2181,node-2:2181 --alter --topic test1 --partitions 5 #把test1主题 分区修改为5 之前是3 WARNING: If partitions are increased for a topic that has a key, the partition logic or ordering of the messages will be affected Adding partitions succeeded! #增加分区的个数是允许的操作 但是要注意可能会影响生产者发送策略 基于key % 分区个数 #能不能减少分区个数呢? bin/kafka-topics.sh --zookeeper node-1:2181,node-2:2181 --alter --topic test1 --partitions 2 #日志显示 减少分区操作是不允许 失败 Error while executing topic command : The number of partitions for a topic can only be increased. Topic test1 currently has 5 partitions, 2 would not be an increase. #kafka为什么不允许减少分区。 着重考虑被删除的分区的数据该如何处理 而不影响已有分区数据安全、顺序问题。
-
producer生产消息到kafka
-
命令
kafka-console-consumer.sh
-
用法
bin/kafka-console-producer.sh --broker-list node-1:9092 --topic test1
-
-
consumer读取kafka消息消费
-
命令
kafka-console-producer.sh
-
用法
bin/kafka-console-consumer.sh --topic test1 --bootstrap-server node-1:9092 --from-beginning --bootstrap-server #指定kafka集群地址 老版本中是指定zk集群地址 在老版本中 消费者消费记录offset是保存在zk中 新版本中kafka为了脱离zk 自己保存消费记录。 --from-beginning #详细解释见画图
-
Kafka java API
-
开发环境准备
-
官方api地址:http://kafka.apache.org/21/javadoc/
-
生产者
-
核心类 KafkaProducer
-
消息核心类 ProducerRecord
private final String topic; //主题 private final Integer partition; //分区 private final Headers headers; //头信息 private final K key; //消息的key 默认为空 如果有值 影响分区策略 private final V value; //消息内容 private final Long timestamp; //时间戳
-
在kafka中数据也是以kv形式存在的。其中v重要 保存消息数据。
-
kv在底层都是以==bytes[]==形式存在的。意味着在写数据读数据的时候 需要进行类型转换。
kafka需要指定序列化类 和反序列化的类。 如果把数据从各个类型序列化成为bytes[] 在kafka中 使用最多的类型是String
-
配置对象类 ProducerConfig
-
核心参数
-
ACK机制 后面知识点说
-
retries 重试次数
retries //生产者的重试次数 默认0 retry.backoff.ms //两次重试之间的时间间隔 默认100ms
-
-
-
-
-
消费者
-
核心类 KafkaConsumer
-
注意事项
#创建消费者要指定消费者所属的消费者组。 #消费数据之前首先订阅主题。 猜想:能否只消费主题中某个分区数据? 猜想:能否指定从什么位置消费呢? #消费的过程是一个拉模式的过程。消费者不断轮询拉取自己订阅的消息。 #消费者需要提交自己的消费记录 消费偏移量offset 消费记录保存在哪里? 老版本中:保存在zk集群中。 新版本中:kafka内置的主题:__consumer_offsets 默认有50个分区。 还可以自己存储在外部介质中:比如mysql redis。(Spark Streaming)
-
-
消费者配置核心类==ConsumerConfig==
-
消费者
-
自动提交消费偏移量
-
手动提交消费偏移量–批处理提交,不是一个一个提交。
Properties props = new Properties(); props.put("bootstrap.servers", "localhost:9092"); props.put("group.id", "test"); props.put("enable.auto.commit", "false"); //关闭自动提交 props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props); consumer.subscribe(Arrays.asList("foo", "bar")); final int minBatchSize = 200; //定义缓存大小 List<ConsumerRecord<String, String>> buffer = new ArrayList<>(); //用于缓存消息 while (true) { ConsumerRecords<String, String> records = consumer.poll(100); for (ConsumerRecord<String, String> record : records) { buffer.add(record);//把拉取过来的数据添加到集合缓存中 } if (buffer.size() >= minBatchSize) {//当满足大小的时候 insertIntoDb(buffer);// 执行相关的业务操作 consumer.commitSync();//处理完数据之后 手动提交 buffer.clear(); //清空缓存 便于下次处理数据 } }
-
手动提交消费offset存储在外部介质中。
后续课程中Spark Streaming中使用mysql自己来存储offset 确保精准一次性。
-
-