kafka笔记

Kafka笔记

kafka我们称之为中间件
Kafka官网:http://kafka.apache.org/
Kafka的文档:http://kafka.apache.org/0110/documentation.html
使用的版本:Kafka_2.11-0.11.0.1

Kafka® is used for building real-time data pipelines and streaming apps. It is horizontally scalable, fault-tolerant, wicked fast, and runs in production in thousands of companies.
Kafka是一个分布式的,分区的,容错的,具有副本的一个提交日志的服务

=====================================================================
kafka的生产者用于生产数据保存到kafka集群
生产者生产数据到kafka集群之前必须创建topic(主题),每个主题都应该设置分区数和副本数
还有主题的名称,然后将这些元数据信息保存到zookeeper里面

消费者用于消费kafka集群中保存到数据
首先确定消费者需要拉取哪个topic中的数据,首先应该去zookeeper中找到该topic的元数据信息,然后再去获取数据

=====================================================================
安装Kafka
-1.kafka是由scala编写的,所以需要scala的环境,同时也需要java的环境
kafka的元数据信息又需要储存在zookeeper里面,所以还需要zookeeper的环境
先安装好scala,java,zookeeper
-2.下载kafka的tar包
https://archive.apache.org/dist/kafka/0.11.0.1/kafka_2.11-0.11.0.1.tgz
-3.上传tar包到linux系统
-4.安装kafka的tar包
tar -zxvf …/…/kafka_2.11-0.11.0.1.tgz -C /opt/cdh5.14.2/modules
-5.切换到kafka的目录,修改配置文件
我们需要配置Kafka的broker服务
需要配置的文件是server.properties文件
一个server.properties文件就是一个broker服务
我们这边可以使用伪分布式来实现
也就是说我们这边在一台机器上配置4个broker服务
那就是配置4个server.properties文件
cp server.properties server0.properties
cp server.properties server1.properties
cp server.properties server2.properties
cp server.properties server3.properties
-6.修改这四个配置文件
broker.id:表示每个broker服务的具体的id编号,是唯一的
server0.properties ==> broker.id=0
server1.properties ==> broker.id=1
server2.properties ==> broker.id=2
server3.properties ==> broker.id=3

	listeners=PLAINTEXT://:9092:表示每个broker服务绑定的主机和端口号
	注意:一台机器上端口号只能使用一次,所以我们4个服务的端口号要设置为不一样
	server0.properties  ==> listeners=PLAINTEXT://superman-bigdata.com:9092
	server1.properties  ==> listeners=PLAINTEXT://superman-bigdata.com:9093
	server2.properties  ==> listeners=PLAINTEXT://superman-bigdata.com:9094
	server3.properties  ==> listeners=PLAINTEXT://superman-bigdata.com:9095
	
	log.dirs:表示数据和元数据储存的本地路径
	server0.properties  ==>  log.dirs=/opt/cdh5.14.2/modules/kafka_2.11-0.11.0.1/data/0
	server1.properties  ==>  log.dirs=/opt/cdh5.14.2/modules/kafka_2.11-0.11.0.1/data/1
	server2.properties  ==>  log.dirs=/opt/cdh5.14.2/modules/kafka_2.11-0.11.0.1/data/2
	server3.properties  ==>  log.dirs=/opt/cdh5.14.2/modules/kafka_2.11-0.11.0.1/data/3
	
	zookeeper.connect:表示zookeeper连接的地址和元数据储存的目录
	server0.properties  ==>  superman-bigdata.com:2181/yangpu1005
	server1.properties  ==>  superman-bigdata.com:2181/yangpu1005
	server2.properties  ==>  superman-bigdata.com:2181/yangpu1005
	server3.properties  ==>  superman-bigdata.com:2181/yangpu1005
	解释:superman-bigdata.com:2181表示zookeeper的连接地址
		/yangpu1005:表示元数据储存在zkcli中根目录下面的yangpu1005这个目录里面
		
-7.开启服务
	-1.必须先开启zookeeper的服务
		bin/zkServer.sh start
	-2.开启kafka的服务
		bin/kafka-server-start.sh -daemon config/server0.properties
		bin/kafka-server-start.sh -daemon config/server1.properties
		bin/kafka-server-start.sh -daemon config/server2.properties
		bin/kafka-server-start.sh -daemon config/server3.properties
		-daemon:表示可选项,后台进程

=====================================================================
Kafka的基本使用 ==> topic的使用
bin/kafka-topics.sh
Create, delete, describe, or change a topic.
-1.创建topic:create
bin/kafka-topics.sh --create --zookeeper superman-bigdata.com:2181/yangpu1005 --partitions 2 --replication-factor 2 --topic beifeng0
创建topic的完整的语句
表示创建一个名字叫做beifeng0的主题,这个主题有2个分区,每个分区存在2个副本,主题的元数据信息储存在zookeeper里面

问题1:创建topic的时候,分区数的大小可以任意大吗?
bin/kafka-topics.sh --create --zookeeper superman-bigdata.com:2181/yangpu1005 --partitions 1000 --replication-factor 2 --topic beifeng1
理论上,分区数是没有限制的

问题2:创建topic的时候,副本数的个数可以任意设置吗?
bin/kafka-topics.sh --create --zookeeper superman-bigdata.com:2181/yangpu1005 --partitions 2 --replication-factor 5 --topic beifeng2
报错:replication factor: 5 larger than available brokers: 4
表示我们设置的副本数的个数5 大于 集群的broker的个数 4
注意:副本数是不可以大于kafka集群的broker的个数的
一个broker节点,最多存放一份副本。

总结:
-1.一般情况下,主题的分区数可以设置为broker个数的1-2倍
-2.一般情况下,副本数不宜过多,2-3即可,副本数不可以大于broker的个数
副本数越多,容错性越强,但是也会影响吞吐量
-3.一般情况下,一个topic一旦被创建了,是不会再做修改的,如果一定要做修改
一般是做增大分区数,或者是添加配置信息等操作

-2.查看当前kafka集群中的topic:–list
bin/kafka-topics.sh --list --zookeeper superman-bigdata.com:2181/yangpu1005

-3.查看所有topic的详细信息:–describe
bin/kafka-topics.sh --describe --zookeeper superman-bigdata.com:2181/yangpu1005

查看某一个topic的详细信息
bin/kafka-topics.sh --describe --zookeeper superman-bigdata.com:2181/yangpu1005 --topic beifeng0
Topic:beifeng0 PartitionCount:2 ReplicationFactor:2 Configs:
Topic: beifeng0 Partition: 0 Leader: 0 Replicas: 0,3 Isr: 0,3
Topic: beifeng0 Partition: 1 Leader: 1 Replicas: 1,0 Isr: 1,0

-4.修改topic:–alter (一般做的是增大分区数,没有减少分区数,以及增删配置参数)
-4.1增大分区数
bin/kafka-topics.sh --alter --zookeeper superman-bigdata.com:2181/yangpu1005 --topic beifeng0 --partitions 4
Topic:beifeng0 PartitionCount:4 ReplicationFactor:2 Configs:
Topic: beifeng0 Partition: 0 Leader: 0 Replicas: 0,3 Isr: 3,0
Topic: beifeng0 Partition: 1 Leader: 1 Replicas: 1,0 Isr: 1,0
Topic: beifeng0 Partition: 2 Leader: 2 Replicas: 2,3 Isr: 2,3
Topic: beifeng0 Partition: 3 Leader: 3 Replicas: 3,0 Isr: 3,0

-4.2增删配置参数
bin/kafka-topics.sh --alter --zookeeper superman-bigdata.com:2181/yangpu1005 --topic beifeng0 --config “max.message.bytes=102400”
Topic:beifeng0 PartitionCount:4 ReplicationFactor:2 Configs:max.message.bytes=102400
Topic: beifeng0 Partition: 0 Leader: 0 Replicas: 0,3 Isr: 3,0
Topic: beifeng0 Partition: 1 Leader: 1 Replicas: 1,0 Isr: 1,0
Topic: beifeng0 Partition: 2 Leader: 2 Replicas: 2,3 Isr: 2,3
Topic: beifeng0 Partition: 3 Leader: 3 Replicas: 3,0 Isr: 3,0

bin/kafka-topics.sh --alter --zookeeper superman-bigdata.com:2181/yangpu1005 --topic beifeng0 --delete-config “max.message.bytes”
Topic:beifeng0 PartitionCount:4 ReplicationFactor:2 Configs:
Topic: beifeng0 Partition: 0 Leader: 0 Replicas: 0,3 Isr: 3,0
Topic: beifeng0 Partition: 1 Leader: 1 Replicas: 1,0 Isr: 1,0
Topic: beifeng0 Partition: 2 Leader: 2 Replicas: 2,3 Isr: 2,3
Topic: beifeng0 Partition: 3 Leader: 3 Replicas: 3,0 Isr: 3,0

-5.删除topic:–delete
bin/kafka-topics.sh --delete --zookeeper superman-bigdata.com:2181/yangpu1005 --topic beifeng1
Topic beifeng1 is marked for deletion.
Note: This will have no impact if delete.topic.enable is not set to true.
注意:–delete不是真正的删除topic的所有数据,只是打上一个删除的标记,也就是不可以使用了
如果你想彻底删除,你可以直接在zk中删除当前topic名称的整个目录即可
注意:进入zk储存空间的方式
执行命令:bin/zkCli.sh
切换目录的时候,必须使用ls + 全路径
删除目录的时候,必须使用rmr + 全路径

=====================================================================
官方的测试案例:生产者和消费者的测试脚本
Kafka自带生产者和消费者的模拟测试脚本:
生产者脚本:kafka-console-producer.sh
消费者脚本:kafka-console-consumer.sh
现在我们模拟操作,生产者往kafka集群中写入数据,消费者往kafka集群中拉取数据,打印控制台
-1.开启生产者的脚本
bin/kafka-console-producer.sh --broker-list superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095 --topic beifeng0

-2.开启消费者的脚本
bin/kafka-console-consumer.sh --zookeeper superman-bigdata.com:2181/yangpu1005 --topic beifeng0
警告:
Using the ConsoleConsumer with old consumer is deprecated and will be removed in a future major release. Consider using the new consumer by passing [bootstrap-server] instead of [zookeeper].
我们现在写的消费者的命令已经过期了,这个老版本写法,新版本要求使用bootstrap-server来代替–zookeeper
修改:
bin/kafka-console-consumer.sh --bootstrap-server superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095 --topic beifeng0

Kafka的核心概念:
Message(消息):传递的数据对象,主要由四部分构成:offset(偏移量)、key、value、timestamp(插入时间)
Broker(代理者):Kafka集群中的机器/服务被成为broker, 是一个物理概念。
Topic(主题):维护Kafka上的消息类型被称为Topic,是一个逻辑概念。
Partition(分区):具体维护Kafka上的消息数据的最小单位,一个Topic可以包含多个分区。Partition特性:ordered&immutable。(在数据的产生和消费过程中,不需要关注数据具体存储的Partition在那个Broker上,只需要指定Topic即可,由Kafka负责将数据和对应的Partition关联上)
Producer(生产者):负责将数据发送到Kafka对应Topic的进程
Consumer(消费者):负责从对应Topic获取数据的进程
Consumer Group(消费者组):每个consumer都属于一个特定的group组,一个group组可以包含 多个consumer,但一个组中只会有一个consumer消费数据。

一个Topic分为多个Partition来进行数据管理,一个Partition中的数据是有序、不可变 的,使用偏移量(offset)唯一标识一条数据,是一个long类型的数据
Partition接收到producer发送过来数据后,会产生一个递增的offset偏移量数据,同时 将数据保存到本地的磁盘文件中(文件内容追加的方式写入数据);Partition中的数据存活时间超过参数值(log.retention.{ms,minutes,hours},默认7天)的时候进行删除(默认)
Consumer根据offset消费对应Topic的Partition中的数据(也就是每个Consumer消费的每个Topic的Partition都拥有自己的offset偏移量)
注意:Kafka的数据消费是顺序读写的,磁盘的顺序读写速度(600MB/sec)比随机读写 速度(100k/sec)快

Kafka集群中由producer负责数据的产生,并发送到对应的Topic;Producer通 过push的方式将数据发送到对应Topic的分区
Producer发送到Topic的数据是有key/value键值对组成的,Kafka根据key的不 同的值决定数据发送到不同的Partition,默认采用Hash的机制发送数据到对应
Topic的不同Partition中,配置参数为{partitioner.class}
Producer发送数据的方式分为sync(同步)和async(异步)两种,默认为同步方式, 由参数{producer.type}决定;当发送模式为异步发送的时候,Producer提供重 试机制,默认失败重试发送3次

Kafka有两种模式消费数据:队列和发布订阅;在队列模式下,一条数据只会发 送给customer group中的一个customer进行消费;在发布订阅模式下,一条数 据会发送给多个customer进行消费
Kafka的Customer基于offset对kafka中的数据进行消费,对于一个customer
group中的所有customer共享一个offset偏移量
Kafka中通过控制Customer的参数{group.id}来决定kafka是什么数据消费模式,  如果所有消费者的该参数值是相同的,那么此时的kafka就是类似于队列模式,  数据只会发送到一个customer,此时类似于负载均衡;否则就是发布订阅模式

Kafka的数据是按照分区进行排序的(插入的顺序),也就是每个分区中的数据是有 序的。在Consumer进行数据消费的时候,也是对分区的数据进行有序的消费的, 但是不保证所有数据的有序性(多个分区之间)
Consumer Rebalance:当一个consumer group组中的消费者数量和对应
Topic的分区数量一致的时候,此时一个Consumer消费一个Partition的数据;  如果不一致,那么可能出现一个Consumer消费多个Partition的数据或者不消费 数据的情况,这个机制是根据Consumer和Partition的数量动态变化的
Consumer通过poll的方式主动从Kafka集群中获取数据

Kafka的Replication指的是Partition的复制,一个Partition的所有分区中只有 一个分区是leader节点,其它分区是follower节点。
Replication对Kafka的吞吐率有一定的影响,但是极大的增强了可用性
Follower节点会定时的从leader节点上获取增量数据,一个活跃的follower节点 必须满足一下两个条件:
所有的节点必须维护和zookeeper的连接(通过zk的heartbeat实现)
follower必须能够及时的将leader上的writing复制过来,不能“落后太多”;落后太多,由参数{replica.lag.time.max.ms}和{replica.lag.max.messages}决定

MessageDeliverySemantics是消息系统中数据传输的可靠性保证的一个定义,主要 分为三种类型:
	At most once(最多一次):消息可能会丢失,但是不可能重复发送 At least once(最少一次):消息不可能丢失,但是可能重复发送 Exactly once(仅仅一次):消息只发送一次,但不存在消息的丢失
Kafka的Producer通过参数{request.required.acks}来定义确定Producer和Broker之间 是哪种消息传递类型

=====================================================================
flume和kafka的集成案例:
案例一:Flume收集文本文件中的数据,并将数据发送到Kafka(公司常用)
在Flume中使用KafkaSink将数据发送到Kafka中
分析:
source:
type:EXEC
channel:
type:memory
sink:
type:KafkaSink
Flume作为Kafka的生产者,目前没有消费者
配置文件:
#Definied agent,source,channel,sink
a1.sources = r1
a1.channels = c1
a1.sinks = k1

#Definied sources ==> EXEC
a1.sources.r1.type = exec
a1.sources.r1.command = tail -f -c +0 /home/robin/data/access.log
a1.sources.r1.channels = c1

#Definied channel ==> memory
a1.channels.c1.type = memory
a1.channels.c1.capacity = 10000
a1.channels.c1.transactionCapacity = 10000
a1.channels.c1.byteCapacityBufferPercentage = 20
a1.channels.c1.byteCapacity = 800000

#Definied sink ==> KafkaSink
a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.k1.kafka.topic = mytopic
a1.sinks.k1.kafka.bootstrap.servers = superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095
a1.sinks.k1.kafka.flumeBatchSize = 20
a1.sinks.k1.kafka.producer.acks = 1
a1.sinks.k1.kafka.producer.linger.ms = 1
a1.sinks.k1.channel = c1

执行命令:
bin/flume-ng agent --conf /opt/cdh5.14.2/modules/flume-1.6.0-cdh5.14.2/conf/yangpu1005 --conf-file /opt/cdh5.14.2/modules/flume-1.6.0-cdh5.14.2/conf/yangpu1005/flume2Kafka.properties --name a1 -Dflume.root.logger = INFO,console

为了演示效果,flume是否真正的发送数据到kafka中,我们是看不到的,所以我们需要开启kafka自带的消费者脚本,来消费者这个topic,如果可以消费到数据,就说明flume是真正的发送数据成功了
开启kafka的消费者:
bin/kafka-console-consumer.sh --bootstrap-server superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095 --topic mytopic

案例二:Flume收集Kafka中的数据,并将数据日志打印到控制台
在Flume中使用KafkaSource收集Kafka中的数据
分析:
source:
type:KafkaSource
channel:
type:memory
sink:
type:logger
flume作为kafka的消费者
配置文件:
#Definied agent,source,channel,sink
a2.sources = r2
a2.channels = c2
a2.sinks = k2

#Definied sources ==> KafkaSource
a2.sources.r2.type = org.apache.flume.source.kafka.KafkaSource
a2.sources.r2.kafka.bootstrap.servers = superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095
a2.sources.r2.kafka.topics = mytopic
a2.sources.r2.batchSize = 5000
a2.sources.r2.batchDurationMillis = 2000
a2.sources.r2.kafka.consumer.group.id = flume
a2.sources.r2.channels = c2

#Definied channel ==> memory
a2.channels.c2.type = memory
a2.channels.c2.capacity = 10000
a2.channels.c2.transactionCapacity = 10000
a2.channels.c2.byteCapacityBufferPercentage = 20
a2.channels.c2.byteCapacity = 800000

#Definied sink ==> logger
a2.sinks.k2.type = logger
a2.sinks.k2.channel = c2

执行命令:
bin/flume-ng agent --conf /opt/cdh5.14.2/modules/flume-1.6.0-cdh5.14.2/conf/yangpu1005 --conf-file /opt/cdh5.14.2/modules/flume-1.6.0-cdh5.14.2/conf/yangpu1005/Kafka2Logger.properties --name a2 -Dflume.root.logger = INFO,console

问题:
我们执行了案例二flume的命令,也就是已经开始消费kafka的mytopic中的数据了,程序并没有报错
也已经成功连接上了kafka集群,那为什么控制台没有打印消费到的数据呢????
没有打印数据说明:flume作为消费者没有拉取到数据,并不是表示这个程序错误了
这个消费者本身是没有任何问题的,只不过呢,是没有拉取到topic中的数据
原因是因为,kafka的消费者默认消费的时候,是从当前的topic中的最新一条数据开始消费的

所以我们可以将案例一和案例二并在一起使用
案例一的flume作为kafka的生产者
bin/flume-ng agent --conf /opt/cdh5.14.2/modules/flume-1.6.0-cdh5.14.2/conf/yangpu1005 --conf-file /opt/cdh5.14.2/modules/flume-1.6.0-cdh5.14.2/conf/yangpu1005/flume2Kafka.properties --name a1 -Dflume.root.logger = INFO,console

案例二的flume作为kafka的消费者
bin/flume-ng agent --conf /opt/cdh5.14.2/modules/flume-1.6.0-cdh5.14.2/conf/yangpu1005 --conf-file /opt/cdh5.14.2/modules/flume-1.6.0-cdh5.14.2/conf/yangpu1005/Kafka2Logger.properties --name a2 -Dflume.root.logger = INFO,console

报错:
20/03/05 10:49:19 ERROR kafka.KafkaSource: KafkaSource EXCEPTION, {}
org.apache.flume.ChannelException: Put queue for MemoryTransaction of capacity 1000 full, consider committing more frequently, increasing capacity or increasing thread count

Cannot commit transaction. Byte capacity allocated to store event body 640000.0reached. Please increase heap space/byte capacity allocated to the channel as the sinks may not be keeping up with the sources

修改配置参数
最终版:
案例一的配置和案例二的配置
配置文件:
#Definied agent,source,channel,sink
a1.sources = r1
a1.channels = c1
a1.sinks = k1

#Definied sources ==> EXEC
a1.sources.r1.type = exec
a1.sources.r1.command = tail -f -c +0 /home/robin/data/access.log
a1.sources.r1.channels = c1

#Definied channel ==> memory
a1.channels.c1.type = memory
a1.channels.c1.capacity = 1000
a1.channels.c1.transactionCapacity = 100
a1.channels.c1.byteCapacityBufferPercentage = 20

#Definied sink ==> KafkaSink
a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
a1.sinks.k1.kafka.topic = mytopic
a1.sinks.k1.kafka.bootstrap.servers = superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095
a1.sinks.k1.kafka.flumeBatchSize = 20
a1.sinks.k1.kafka.producer.acks = 1
a1.sinks.k1.kafka.producer.linger.ms = 1
a1.sinks.k1.channel = c1

配置文件:
#Definied agent,source,channel,sink
a2.sources = r2
a2.channels = c2
a2.sinks = k2

#Definied sources ==> KafkaSource
a2.sources.r2.type = org.apache.flume.source.kafka.KafkaSource
a2.sources.r2.kafka.bootstrap.servers = superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095
a2.sources.r2.kafka.topics = mytopic
a2.sources.r2.batchSize = 5000
a2.sources.r2.batchDurationMillis = 2000
a2.sources.r2.kafka.consumer.group.id = flume
a2.sources.r2.channels = c2

#Definied channel ==> memory
a2.channels.c2.type = memory
a2.channels.c2.capacity = 10000
a2.channels.c2.transactionCapacity = 10000
a2.channels.c2.byteCapacityBufferPercentage = 20

#Definied sink ==> logger
a2.sinks.k2.type = logger
a2.sinks.k2.channel = c2

=====================================================================
Kafka的监控:
以KafkaOffsetMonitor为例:
1.去github上下载jar包
githun网址:https://github.com/quantifind/KafkaOffsetMonitor
jar包下载地址:https://github.com/quantifind/KafkaOffsetMonitor/releases/latest
2.下载完成之后,把这个包上传到linux
3.执行启动命令
java -cp KafkaOffsetMonitor-assembly-0.2.1.jar
com.quantifind.kafka.offsetapp.OffsetGetterWeb
–zk superman-bigdata.com:2181/yangpu1005
–port 8080
–refresh 10.seconds
–retain 2.days

=====================================================================
Kafka的生产者和消费者的API代码实现:
1.生产者代码(producer)
注意:PPT上的配置参数和代码都是0.8版本的,可以不用看,主要看一下的资料
生产者代码的api:
http://kafka.apache.org/0110/documentation.html#producerapi
http://kafka.apache.org/0110/javadoc/index.html?org/apache/kafka/clients/producer/KafkaProducer.html
生产者代码需要使用的配置参数
http://kafka.apache.org/0110/documentation.html#producerconfigs

编写代码:
-1.在maven项目的pom文件中添加依赖

org.apache.kafka
kafka-clients
0.11.0.1

-2.Kafka生产者的代码是有步骤的
第一步:
创建Properties对象,用于保存生产者的配置参数
第二步:
构建KafkaProducer对象
第三步:
调用KafkaProducer的send方法,向kafka的集群发送数据
第四步:
关闭KafkaProducer对象

=====================================================================
生产者案例1java代码:
package com.bigdata.Kafka;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;

import java.util.Properties;

public class ProducerJavaDemo {
public static void main(String[] args) {
//1.构建properties对象
Properties props = new Properties();
//1.1设置配置参数
//给定储存数据的kafka的broker集群地址
props.put(“bootstrap.servers”,“superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095”);
//1.2消息传输的可靠性保证 acks
//acks 一共有三个值-1,0,1
//0:不等待任何副本返回信息 1:等待leader副本接收到之后再发送下一条数据 -1:等待所有副本接收到之后再发送下一条数据
props.put(“acks”,“1”);
//1.3如果发送失败,可以重新发送
props.put(“retries”,“0”);
//1.4批量发送
props.put(“batch.size”,“16384”); //16k
//1.5批次产生的时间间隔,一般和batch.size连用
props.put(“linger.ms”,“1”);
//1.6缓冲区的大小
props.put(“buffer.memory”,“33554432”);
//1.7key和value的序列化
props.put(“key.serializer”, “org.apache.kafka.common.serialization.StringSerializer”);
props.put(“value.serializer”, “org.apache.kafka.common.serialization.StringSerializer”);

    //2.构建KafkaProducer对象
    Producer<String,String> producer  =  new KafkaProducer<>(props);

    //3.调用KafkaProducer的send方法,向kafka的集群发送数据

/* String topic = “beifeng0”;
String key = “9999”;
String value = “hello kafka”;
ProducerRecord<String,String> record = new ProducerRecord<>(topic,key,value);
producer.send(record);*/

    for(int i = 1;i < 10001;i++){
        String topic = "beifeng0";
        //String key = String.valueOf(i);
        String key = Integer.toString(i);
        String value = String.valueOf(i);
        ProducerRecord<String,String>  record  = new ProducerRecord<>(topic,key,value);
        producer.send(record);
    }

    //4.关闭KafkaProducer对象
    producer.close();
}

}

=====================================================================
生产者案例1scala代码:
package com.bigdata.Kafka

import java.util.Properties

import org.apache.kafka.clients.producer.{KafkaProducer, Producer, ProducerRecord}

object ProducerScalaDemo {
def main(args: Array[String]): Unit = {

//1.构建properties对象
val props = new Properties()
//1.1设置配置参数
//给定储存数据的kafka的broker集群地址
props.put("bootstrap.servers", "superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095")
//1.2消息传输的可靠性保证 acks
//acks 一共有三个值-1,0,1
//0:不等待任何副本返回信息 1:等待leader副本接收到之后再发送下一条数据 -1:等待所有副本接收到之后再发送下一条数据
props.put("acks", "1")
//1.3如果发送失败,可以重新发送
props.put("retries", "0")
//1.4批量发送
props.put("batch.size", "16384") //16k

//1.5批次产生的时间间隔,一般和batch.size连用
props.put("linger.ms", "1")
//1.6缓冲区的大小
props.put("buffer.memory", "33554432")
//1.7key和value的序列化
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer")
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer")

//2.构建KafkaProducer对象
val producer = new KafkaProducer[String, String](props)

//3.调用KafkaProducer的send方法,向kafka的集群发送数据
/*        String topic = "beifeng0";
        String key = "9999";
        String value = "hello kafka";
        ProducerRecord<String,String>  record  = new ProducerRecord<>(topic,key,value);
        producer.send(record);*/

var i = 1
while ( {
  i < 10001
}) {
  val topic = "beifeng0"
  //String key = String.valueOf(i);
  val key = Integer.toString(i)
  val value = String.valueOf(i)
  val record = new ProducerRecord[String, String](topic, key, value)
  producer.send(record)

  {
    i += 1; i - 1
  }
}

//4.关闭KafkaProducer对象
producer.close()

}
}

=====================================================================
生产者案例2java代码:
package com.bigdata.Kafka;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;

import java.util.Properties;
import java.util.Random;

public class ProducerJavaDemo2 {
public static void main(String[] args) {
char[] chars = “qwertyuiopasdfghjklzxcvbnm”.toCharArray();
int length = chars.length;

    //1.构建properties对象
    Properties props = new Properties();
    //1.1设置配置参数
    //给定储存数据的kafka的broker集群地址
    props.put("bootstrap.servers","superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095");
    //1.2消息传输的可靠性保证 acks
    //acks 一共有三个值-1,0,1
    //0:不等待任何副本返回信息 1:等待leader副本接收到之后再发送下一条数据 -1:等待所有副本接收到之后再发送下一条数据
    props.put("acks","1");
    //1.3如果发送失败,可以重新发送
    props.put("retries","0");
    //1.4批量发送
    props.put("batch.size","16384"); //16k
    //1.5批次产生的时间间隔,一般和batch.size连用
    props.put("linger.ms","1");
    //1.6缓冲区的大小
    props.put("buffer.memory","33554432");
    //1.7key和value的序列化
    props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
    props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

    //1.8自定义分区
    props.put("partitioner.class","com.bigdata.Kafka.PartitionDemo");

    //2.构建KafkaProducer对象
    Producer<String,String> producer  =  new KafkaProducer<>(props);

    //3.以多线程的方式模拟产生数据
    Random random  = new Random();
    for(int i= 0 ;i < 3 ;i++){
        new Thread(new Runnable() {
            @Override
            public void run() {
                //初始化一个发送数据条数的值
                int events = random.nextInt(10000) + 10;
                String threadName =  Thread.currentThread().getName();
                for(int j = 0 ; j < events;j++){
                    if(j !=0 && j %100 == 0){
                        System.out.println("线程【" + threadName + "】已经发送了" + j + "条数据!!");
                    }
                    //1.先构建message的key的值
                    String key = "key_" + random.nextInt(1000);
                    //2.假设value是由若干个单词组成的,每一行的单词数在【3,10】
                    StringBuilder  sb1  = new StringBuilder();
                    int wordNums = random.nextInt(8) + 3;
                    for(int k = 0 ; k < wordNums ;k ++){
                        //单词是由若干个英文字母组成的,每个单词的字母数在【2,5】
                        StringBuilder  sb2  = new StringBuilder();
                        int charNums = random.nextInt(4) + 2;
                        for(int o= 0 ; o <charNums ; o ++){
                            sb2.append(chars[random.nextInt(length)]);
                        }
                        sb1.append(sb2.toString()).append("==");
                    }
                    String value = sb1.toString();

                    ProducerRecord<String,String> record  = new ProducerRecord<>("beifeng0",key,value);

                    //4.调用KafkaProducer的send方法,向kafka的集群发送数据
                    producer.send(record);
                }
                System.out.println("线程【" + threadName + "】已经发送完了" + events + "条数据!!!");
            }
        },"Thread-" + i).start();

        //5.关闭KafkaProducer对象
        //注意:不要使用producer.close();关闭,因为如果你使用这个代码关闭,可能会出现,数据还没有发送完毕,生产者对象就被关掉了
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                producer.close();
            }
        }));
    }
}

}

自定义分区
package com.bigdata.Kafka;

import org.apache.kafka.clients.producer.Partitioner;
import org.apache.kafka.common.Cluster;

import java.util.Map;

public class PartitionDemo implements Partitioner {
@Override
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {

    //return后面的数字,就是表示数据写入哪一个分区
    return 3;
}

@Override
public void close() {

}

@Override
public void configure(Map<String, ?> configs) {

}

}

=====================================================================
消费者的API代码
官网消费者的api文档:
http://kafka.apache.org/0110/documentation.html#consumerapi
http://kafka.apache.org/0110/javadoc/index.html?org/apache/kafka/clients/consumer/KafkaConsumer.html
官网本版本配置文档:
http://kafka.apache.org/0110/documentation.html#newconsumerconfigs

注意:kafka消费者有两种方式
-1.Automatic Offset Committing:自动偏移量提交
消费者在消费topic的时候,会产生对应的偏移量的记录
这个偏移量记录需要去管理,用途就是记录当前消费者消费当前topic的分区中的偏移量的信息,如果知道了这个信息,那么消费者下一次消费的时候,可以继续从上一次结束的地方继续消费。
什么是自动偏移量提交呢?
就是消费者不需要管这个偏移量的记录,程序会自己帮你把当前的消费者的偏移量记录保存在本地,那么该消费者下一次去消费这个topic的时候,程序会首先读取之前保存在本地的偏移量记录,然后接着上一次的记录,继续消费

消费者代码的步骤:
-1.构建properties对象,用来保存消费者的配置参数信息
-2.构建KafkaConsumer消费者对象
-2.1使用KafkaConsumer的subscribe来描述一下待消费者topic信息
-3.使用KafkaConsumer的poll方法开始拉取数据

总结:
当我们选择"enable.auto.commit",“true"自动提交偏移量的时候,
程序会把当前消费者的每一次执行的偏移量的记录保存在本地
然后如果你第一次运行的时候,选择"auto.offset.reset”,“earliest”
那么,消费者就是从0开始消费的,以后再运行的时候,
都是先读取本地的偏移量的记录,然后继续消费
如果你第一次的时候,选择"auto.offset.reset",“latest”
那么,你以后都是先读取本地的偏移量的记录,然后继续消费

代码:
package com.bigdata.Kafka.consumer;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;

import java.util.Arrays;
import java.util.Properties;

public class AutoOffsetConsumer {
public static void main(String[] args) {

    //1.构建properties对象,用来保存消费者的配置参数信息
    Properties props  = new Properties();
    //1.1给定kafka集群的地址
    props.put("bootstrap.servers","superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095");

    //1.2给定消费者的组的id
    props.put("group.id","GroupB");

    //1.3是否选择偏移量自动提交,
    //注明:该参数表示是否开启自动提交偏移量,true:表示自动提交  false:表示不自动提交
    //注意:如果你选择了false,表示不自动提交偏移量,但是如果你代码中自己保存了偏移量,那么就是手动管理偏移量
    //如果你代码中并没有对偏移量做任何操作,那么偏移量是没有管理的,也就是说当消费者下次再消费的时候,
    // 是不知道自己上一次的记录
    props.put("enable.auto.commit","true");

    //1.4偏移量自动提交的时间间隔默认是5000ms
    props.put("auto.commit.interval.ms","5000");

    //1.5kafka的消费者默认是从最新开始消费者,可以使用以下参数来选择是从最新(latest),还是最老(earliest)开始消费
    props.put("auto.offset.reset","earliest"); //默认是latest

    //1.6key和value的序列化
    //注意:在大数据中,一般情况下,键值对的key都是String类型
    props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
    props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

    //2.构建KafkaConsumer消费者对象
    KafkaConsumer<String,String> consumer =  new KafkaConsumer<>(props);
    //-2.1使用KafkaConsumer的subscribe来描述一下待消费者topic信息
    consumer.subscribe(Arrays.asList("beifeng0"));

    //3.使用KafkaConsumer的poll方法开始拉取数据
    while(true){
        ConsumerRecords<String, String>  records =  consumer.poll(100);
        for(ConsumerRecord<String, String> record:records){
            String topicName = record.topic(); //当前的topic的名称
            int partitionID  = record.partition(); //当前分区的ID编号
            long offset = record.offset(); //当前拉取的数据的偏移量
            String value  = record.value(); //当前拉取的数据
            StringBuilder sb = new StringBuilder();
            sb.append(topicName)
                    .append(">")
                    .append(partitionID)
                    .append(">")
                    .append(offset)
                    .append(">")
                    .append(value);

            System.out.println(sb.toString());
        }
    }
}

}

-2.Manual Offset Control 手动偏移量控制
代码:
package com.bigdata.Kafka.consumer;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

public class ManualOffsetConsumer {

public static void main(String[] args) throws SQLException {

    //1.构建properties对象,用来保存消费者的配置参数信息
    Properties props = new  Properties();
    //1.1给定kafka集群的地址
    props.put("bootstrap.servers","superman-bigdata.com:9092,superman-bigdata.com:9093,superman-bigdata.com:9094,superman-bigdata.com:9095");
    //1.2给定消费者的组的id
    props.put("group.id","beifeng");
    //1.3是否选择偏移量自动提交,
    props.put("enable.auto.commit","false");
    //1.4kafka的消费者默认是从最新开始消费者,可以使用以下参数来选择是从最新(latest),还是最老(earliest)开始消费
    props.put("auto.offset.reset","earliest"); //默认是latest
    //1.5key和value的序列化
    //注意:在大数据中,一般情况下,键值对的key都是String类型
    props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
    props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

    //2.构建KafkaConsumer消费者对象
    KafkaConsumer<String,String> consumer =  new KafkaConsumer<>(props);
    //-2.1使用KafkaConsumer的subscribe来描述一下待消费者topic信息
    consumer.subscribe(Arrays.asList("beifeng0"));

    //3.使用KafkaConsumer的poll方法开始拉取数据
    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){
            String topicName = record.topic(); //当前的topic的名称
            int partitionID  = record.partition(); //当前分区的ID编号
            long offset = record.offset(); //当前拉取的数据的偏移量
            String value  = record.value(); //当前拉取的数据
            StringBuilder sb = new StringBuilder();
            sb.append(topicName)
                    .append(">")
                    .append(partitionID)
                    .append(">")
                    .append(offset)
                    .append(">")
                    .append(value);
            System.out.println(sb.toString());
            buffer.add(record);
        }
        if(buffer.size() >= minBatchSize){
            insertIntoDb(buffer);
        }

    }
}
public static void insertIntoDb(List<ConsumerRecord<String, String>> buffer) throws SQLException {

    //报错:
    //com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException:
    // Data source rejected establishment of connection,  message from server: "Too many connections"
    //当往数据库中写入数据的时候,获取的数据库连接的次数太多,超过了目前mysql的最大连接数
    //解决方案:
    //1.代码没有写好,把Connection connect构建在for循环里面
    //2.打开linux中的 vi /etc/my.cnf 在[mysqld]标签下添加
        //max_connections=1000 设置最大链接数(一般500-1000合理)
        //wait_timeout=120
        //interactive_timeout=300

    //使用JDBC的方式插入数据库
    Connection connect = null;
    PreparedStatement pstmt = null;
    String url = null;
    String user = null;
    String password = null;
    for(ConsumerRecord<String, String> record:buffer){
        String topicName = record.topic(); //当前的topic的名称
        int partitionID  = record.partition(); //当前分区的ID编号
        long offset = record.offset(); //当前拉取的数据的偏移量
        String value  = record.value(); //当前拉取的数据

        //-1.获取数据库连接
        url = "jdbc:mysql://superman-bigdata.com:3306/yangpu1005";
        user = "root";
        password = "123456";
        try {
            connect = DriverManager.getConnection(url,user,password);
            //-2.编写插入数据的sql语句,首先在mysql中创建表
            String sql = "replace into ManualOffsetConsumer values (?,?,?)";
            //-3.执行sql
            pstmt = connect.prepareStatement(sql);
            //-4.设置变量值
            pstmt.setString(1,topicName);
            pstmt.setInt(2,partitionID);
            pstmt.setLong(3,offset);

            //4.开始执行插入
            pstmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    pstmt.close();
    connect.close();
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值