Kafka 0.10 安装及使用
来源:互联网 发布:八格切图软件下载 编辑:程序博客网 时间:2024/05/16 17:05
1 总体说明
笔记本:i5第六代,16G内存,256G固态硬盘
使用VirtualBox 5.0.22建立3台虚拟机。
2 主机规化
主机名
IP
用途
master
192.168.56.101
slave1
192.168.56.102
slave2
192.168.56.103
3 目录规化
组件
目录
说明
JDK
/usr/java/jdk1.8.0_92
ln -s /usr/java/jdk1.8.0_92 /usr/java/default
zookeeper
/opt/zookeeper
kafka
/opt/kafka
各输出目录的根
/var/kafka/
/var/zookeeper/
4 端口规化
端口
说明
9092
2888
ZooKeeper,如果是Leader,用来监听Follower的连接
3888
ZooKeeper,用于Leader选举
2181
ZooKeeper,用来监听客户端的连接
5 操作系统配置
5.1 OS安装
l 使用CentOS 6.5版
l 磁盘划分:
/boot 500MB ext4 boot partition # 迫使主分区
swap 2GB swap # 与物理内存一样大
/ 剩余空间 ext4 # 迫使主分区
/data1 sda所有空间 ext4
/data2 sdb所有空间 ext4
l 安装软件:sysstat , httpd, tftp-server, ntp
启动sysstat :/etc/init.d/sysstat start
设置sysstat自启动:checkfig sysstat on
l 安装pssh:
wget https://pypi.python.org/packages/source/p/pssh/pssh-2.3.1.tar.gz
tar zxf pssh-2.3.1.tar.gz
cd pssh-2.3.1
python setup.py install
pssh 多主机并行运行命令
pscp 传输文件到多个hosts,他的特性和scp差不多
pslurp 从多台远程机器拷贝文件
pnuke kill远程机器的进程
pslurp 从远程主机考本文件到本地
prsync 使用rsync协议从本地计算机同步到远程主机
5.2 配置SSH免密登陆
原理:就是我把我的公钥放到你的authorized_keys里面,然后我就可以ssh无密码登录你了
5.2.1 配置规则
u NameNode 能免密码登录所有的 DataNode
u SecondaryNameNode 能免密码登录所有的 DataNode
u NameNode 能免密码登录自己
u SecondaryNameNode 能免密码登录自己
u NameNode 能免密码登录 SecondaryNameNode
u SecondaryNameNode 能免密码登录 NameNode
u DataNode 能免密码登录自己
u DataNode 不需要配置免密码登录 NameNode、SecondaryNameNode和其它 DataNode。
5.2.2 配置步骤
l 在master(NameNode)上执行:
cd ~
ssh-keygen -t rsa # 一路回车。
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
ssh localhost # 验证本机无密码登陆
ssh master # 验证本机无密码登陆
for ip in `seq 1 2`; do scp ~/.ssh/authorized_keysroot@slave$ip:~/keys.master; done
l 在slave1(SecondaryNameNode, DataNode)上执行:
cd ~
ssh-keygen -t rsa
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
ssh localhost # 验证本机无密码登陆
ssh slave1 # 验证本机无密码登陆
cat ~/keys.master >> ~/.ssh/authorized_keys # 实现master免密码登陆slave1
scp ~/.ssh/id_rsa.pub root@slave2:~/keys.slave1
scp ~/.ssh/id_rsa.pub root@master:~/keys.slave1
到master主机上执行:cat ~/keys.slave1 >> ~/.ssh/authorized_keys
l 在slave2(DataNode)上执行:
cd ~
ssh-keygen -t rsa
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
ssh localhost # 验证本机无密码登陆
ssh slave2 # 验证本机无密码登陆
cat ~/keys.master >> ~/.ssh/authorized_keys
cat ~/keys.slave1 >> ~/.ssh/authorized_keys
l 免密码登陆验证
master(NN)登录所有节点
slave1(SNN)登录所有的 DataNode(slave2)
slave1(SNN)登录NameNode(master)
5.3 系统配置
每台主机执行以下操作:
1. 修改hosts,增加各个主机
echo "# cluster hosts" >> /etc/hosts
echo "192.168.56.101 master" >>/etc/hosts
echo "192.168.56.102 slave1" >>/etc/hosts
echo "192.168.56.103 slave2" >>/etc/hosts
cat /etc/hosts
for ip in `seq 1 2`; do scp /etc/hostsroot@slave$ip:/etc; done
2. 定义其他节点主机列表文件
cd ~
vi hdp-other-hosts
slave1
slave2
该文件是为了使用pssh进行批量操作用。文件内容为每行一个主机名字。
之后,可使用类似如下命令从master主机上批量在其他主机上执行命令:
例如:pssh -h hdp-other-hosts "mkdir -p /hadoop /test"
3. 修改OS参数,如关闭防火墙,增加打开文件数等
l 修改主机名
cat /etc/sysconfig/network #看看主机名字是否正确,不对则修改
service network restart
l # 关闭iptables.
service iptables stop
service iptables status
chkconfig iptables off
chkconfig --list | grep iptables
pssh -h hdp-other-hosts "service iptables stop;chkconfig iptables off"
# 两个命令中间用分号;以实现连续执行,即使有错也会继续。
# 如果每个命令被 && 号分隔,那么这些命令会一直执行下去,如果中间有错误的命令存在,则不再执行后面的命令。
l # 关闭selinux.
vi /etc/selinux/config
SELINUX=disabled
l # 关闭透明大页
vi /etc/rc.local
if test -f /sys/kernel/mm/transparent_hugepage/enabled;then
echo never> /sys/kernel/mm/transparent_hugepage/enabled
fi
if test -f /sys/kernel/mm/transparent_hugepage/defrag;then
echo never> /sys/kernel/mm/transparent_hugepage/defrag
fi
l # 让机器尽量使用物理内存。如果设置成0,则意味着只使用物理内存。
echo "vm.swappiness=10" >>/etc/sysctl.conf
l # 最大打开文件数据和最大进程数 limits.conf 每台机器都修改
vi /etc/security/limits.conf # 添加下面的内容
* - nofile 65535
* - nproc 65535
l 每台机器重启:reboot
l 检查修改效果:
/usr/sbin/sestatus -v # 如果SELinux status参数为enabled即为开启状态
或者使用:getenforce,也可检查SELinux的状态
# 如果输出结果为[always]表示透明大页启用了。[never]表示禁用、[madvise]表示
cat /sys/kernel/mm/transparent_hugepage/enabled, defrag
cat /proc/sys/vm/swappiness #应该是10
ulimit -a #应该是65535
5.4 NTP配置
l NTP服务器(master)配置
vi /etc/ntp.conf
server 127.127.1.0
fudge 127.127.1.0 stratum 10
restrict 192.168.56.0 255.255.255.0 nomodifynotrap
chkconfig ntpd on
service ntpd start
ntpstat # show : synchronised to local net at stratum 11
l 其他主机配置
vi /etc/ntp.conf
server master
service ntpd start
chkconfig ntpd on
crontab -e
*/10 * * * * /usr/sbin/ntpdate -u master&>/var/log/ntpdate-cron.log
# 每10分钟同步一次,'&'开始是为了调试用
10分钟后,各主机上执行:ntpstat,看是否同步了
如果机器重启了,slave上执行ntpstat,显示未同步,运行一次:service ntpd restart,再执行ntpstat,就显示同步了。不知道这个怎么搞好
6 JAVA安装
l 在master上安装JAVA
rpm -ivh jdk8.rpm
echo "export JAVA_HOME=/usr/java/default">> /etc/profile
echo "exportCLASSPATH=.:$JAVA_HOME/lib/tools.jar" >> /etc/profile
echo "export PATH=$JAVA_HOME/bin:$PATH">> /etc/profile
source /etc/profile
gtar cf java.gz /usr/java/
for ip in `seq 1 2`; do scp java.gz root@slave$ip:/usr;done
for ip in `seq 1 2`; do scp /etc/profileroot@slave$ip:/etc; done
rm java.gz
java -version # 检验
l 在其他各主机上安装JAVA
cd /usr
gtar xf java.gz
rm java.gz
java -version # 检验
7 Kafka安装
7.1 安装zookeeper
1. tar zxvfzookeeper-3.4.8.tar.gz
2. cpzookeeper/conf/zoo_sample.cfg zookeeper/conf/zoo.cfg
3. 编辑zoo.cfg
ü 修改:dataDir=/var/zookeeper/datadir
ü 添加:dataLogDir=/var/zookeeper/logsdir
ü 添加:
server.1=master:2888:3888
server.2=slave1:2888:3888
server.3=slave2:2888:3888
4. 分发到其他节点
tar cf zookeeper-done.tar zookeeper-3.4.8
for ip in `seq 1 2`; do scp /hadoop/zookeeper-done.tarroot@slave$ip:/hadoop; done
分别解压缩即可。
5. 在各节点上创建ZK的目录:数据文件和日志存放目录
1) mkdir -p/var/zookeeper/datadir /var/zookeeper/logsdir
2) pssh -h~/hdp-other-hosts "mkdir -p /var/zookeeper/datadir /var/zookeeper/logsdir"
6. 编辑各节点的myid值
echo 1 > /var/zookeeper/datadir/myid
pssh -H root@slave1 "echo 2 > /var/zookeeper/datadir/myid"
pssh -H root@slave2 "echo 3 > /var/zookeeper/datadir/myid"
或者使用下面循环代替上面的两句:
for ip in `seq 1 2`; do pssh -H root@slave$ip "echo$[$ip + 1] > /var/zookeeper/datadir/myid"; done
7. 启动
zkServer.sh
start :这个命令使得zk服务进程在后台进行
zkServer.sh start-foreground :在前台中运行
zkServer.sh print-cmd :可以查看zookeeper启动的各个参数,包括java路径等,也可以便于查找问题。
运行日志在zookeeper安装目录下的zookeeper.out。另外要注意的是,zookeeper重启会自动清除zookeeper.out日志,所以如果出错要注意先备份这个文件。
看了下zkServer.sh的代码,这个zookeeper.out实际上是nohup的输出。
研究了下bin/zkServer.sh和conf/log4j.properties,发现zookeeper其实是有日志相关的输出的配置,只要定义相关的变量就可以了。
主要是ZOO_LOG_DIR和ZOO_LOG4J_PROP这两个环境变量:
如果是连接同一台主机上的zk进程,那么直接运行bin/目录下的zkCli.cmd(Windows环境下)或者zkCli.sh(Linux环境下),即可连接上zk。
直接执行zkCli.cmd或者zkCli.sh命令默认以主机号 127.0.0.1,端口号 2181 来连接zk,如果要连接不同机器上的zk,可以使用 -server 参数,例如:
zkCli.sh -server 192.168.229.160:2181,192.168.229.161:2181,192.168.229.162:2181
7.2 安装Kafka
1. 解压tar
2. 修改配置文件:config/server.properties
ü broker.id=1 (另外几台机器依次设置为2,3,)
ü log.dirs=/var/kafka/kfklogs (日志地址,kafka的topic以及数据文件存放位置)
ü zookeeper.connect=master:2181,slave1:2181,slave2:2181
ü listeners = PLAINTEXT://ip:9092 :监听列表(以逗号分隔)。hostname如果设置为0.0.0.0则绑定所有的网卡地址;如果hostname为空则绑定默认的网卡。如果没有配置则默认为java.net.InetAddress.getCanonicalHostName()
ü auto.create.topics.enable=false 免得程序中写错了topic名字时被自动创建了
ü delete.topic.enable 可以物理上删除一个topic
注意:broker级的配置参数(也就是server.properties),可以由topic级别的覆写。
n server.properties参数说明
############################# Server Basics#############################
# 唯一标识一个broker.
broker.id=1
############################# Socket Server Settings#############################
#绑定服务监听的地址和端口,要填写hostname -i 出来的地址,否则可能会绑定到127.0.0.1,producer可能会发不出消息
listeners=PLAINTEXT://172.23.8.144:9092
#broker对producers和consumers服务的地址和端口,如果没有配置,使用listeners的配置,本文没有配置该项
#advertised.listeners=PLAINTEXT://your.host.name:9092
# 处理网络请求的线程数
num.network.threads=3
# 处理磁盘I/O的线程数
num.io.threads=8
# socket server的发送buffer大小 (SO_SNDBUF)
socket.send.buffer.bytes=102400
# socket server的接收buffer大小 (SO_RCVBUF)
socket.receive.buffer.bytes=102400
#一个请求的最大size,用来保护防止oom
socket.request.max.bytes=104857600
############################# Log Basics#############################
#存放日志和消息的目录,可以是用逗号分开的目录,同样不推荐使用/tmp
log.dirs=/usr/local/services/kafka/kafka-logs
#每个topic默认partitions的数量,数量较大表示消费者可以有更大的并行度。
num.partitions=2
# The number of threads per data directory to be usedfor log recovery at startup and flushing at shutdown.
# This value is recommended to be increased forinstallations with data dirs located in RAID array.
num.recovery.threads.per.data.dir=1
#日志的过期时间,超过后被删除,单位小时
log.retention.hours=168
#一个日志文件最大大小,超过会新建一个文件
log.segment.bytes=1073741824
#根据过期策略检查过期文件的时间间隔,单位毫秒
log.retention.check.interval.ms=300000
############################# Zookeeper#############################
#Zookeeper的连接配置,用逗号隔开,也可以用172.23.8.59:2181/kakfa这样的方式指定kafka数据在zk中的根目录
zookeeper.connect=172.23.8.144:2181,172.23.8.179:2181,172.23.8.59:2181
# 连接zk的超时时间
zookeeper.connection.timeout.ms=6000
7.3 启动kafka
先启动各台机器上的zookeeper:bin/zkServer.sh start
日志就在zookeeper的根目录下的zoo.out
然后在每台机器上都启动Kafka:
nohup bin/kafka-server-start.sh config/server.properties> kfk.out &
tail -f -n500 kfk.out
停止kafka,在每台机器上执行:bin/kafka-server-stop.sh
7.4 单机伪集群
因为server.properties就是一个broker的配置,所以,复制几份不同名字的server.properties,并修改broker.id,listeners,port,log.dirs,即可实现单机伪集群。
注意:listeners中的端口号必须与port的值一致。
然后,在单机上,用“bin/kafka-server-start.sh每个server.properties”,依次启动即可。
7.5 使用Kafka
l 创建topic
bin/kafka-topics.sh--create --zookeeper localhost:2181 --replication-factor 2 --partitions 6--topic test01
u partitions:表示创建了一个有6个分区的topic
如果有三台机器(Broker)的话,每台机器上会随机两个目录:如test01-1,test01-3。
u replication-factor:表示该topic需要在不同的broker中保存几份
以上设置为2,因此,某两个broker上会有相同的两个目录:test01-1(其中一个是备份)。
所以,以上命令,由于是6个分区、2个备份、3台机器,所以,每台机器上有4个目录。
u 创建topic参数可以设置一个或多个--config "Property(属性)"
bin/kafka-topics.sh –create ... --configmax.message.bytes=64000 --config flush.messages=1
l 修改topic
使用—alter替换—create,即可修改。
使用--alter--topic my-topic --deleteConfigmax.message.bytes,即可删除某个参数。
l 查看topic
看列表:bin/kafka-topics.sh--list --zookeeper localhost:2181
看属性:bin/kafka-topics.sh--describe --zookeeper localhost:2181 --topic test01
n Leader: 如果有多个broker保存同一个topic,那么同时只能有一个Broker负责该topic的读写,其它的Broker作为实时备份。负责读写的Broker称为Leader.
每个Replication集合中的Partition都会选出一个唯一的Leader,所有的读写请求都由Leader处理。其他Replicas从Leader处把数据更新同步到本地。
n Replicas : 表示该topic的0分区在1号和2号broker中保存
n Isr : 表示当前有效的broker, Isr是Replicas的子集
现在,杀掉一个broker(模拟此点的崩溃):kill -9 PID即可。再看属性:
会发现Leader已经进行了切换,而且,当前有效的broker中,3已经不存在了。
仔细看前后两张图,可以知道,因为3没了,所以,原来Leader为3的分区,就使用了Replicas中的备选,所以,分区1的Leader由3变成了2,而分区4的Leader由3变成了1。
l 发送一个消息
bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test01
每输入一行,就是一条消息
l 消费消息
bin/kafka-console-consumer.sh --bootstrap-server 192.168.56.101:9092 --topic test01 --from-beginning
任意一台机器上执行以上命令,即可看到消息。
l 批量构造大量消息
bin/kafka-verifiable-producer.sh --topic test01 --max-messages 20 --broker-list localhost:9092
l 删除Topic
bin/kafka-topics.sh --delete --zookeeper localhost:2181 --topic test01
如果kafaka启动时加载的配置文件中server.properties没有配置delete.topic.enable=true,那么此时的删除并不是真正的删除,而是把topic标记为:marked for deletion。
删除kafka存储目录(server.properties文件log.dirs配置)相关topic目录。
7.6 Kafka注意事项
listeners一定要配置成为IP地址;如果配置为localhost或服务器的hostname,在使用Java发送数据时就会抛出异 常:org.apache.kafka.common.errors.TimeoutException: Batch Expired 。因为在没有配置advertised.host.name 的情况下,Kafka并没有像官方文档宣称的那样改为广播我们配置的host.name,而是广播了主机配置的hostname。远端的客户端并没有配置 hosts,所以自然是连接不上这个hostname的。
消费者线程数必须是小等于topic的partition分区数;可以通过命令:
./kafka-topics.sh --describe --zookeeper"172.16.49.173:2181" --topic "producer_test"命令来查看分区的情况。
kafka会根据partition.assignment.strategy指定的分配策略来指定线程消费那些分区的消息;没有单独配置该项即是采用的默认值range策略(按照阶段平均分配)。比如分区有10个、线程数有3个,则线程 1消费0,1,2,3,线程2消费4,5,6,线程3消费7,8,9。另外一种是roundrobin(循环分配策略),官方文档中写有使用该策略有两个前提条件的,所以一般不要去设定。
props.put(“auto.offset.reset”, “smallest”) 是指定从最小没有被消费offset开始;如果没有指定该项则是默认的为largest,这样的话该consumer就得不到生产者先产生的消息。
使用New Consumer API
Properties props = new Properties();
//brokerServer(kafka)ip地址,不需要把所有集群中的地址都写上,可是一个或一部分
props.put("bootstrap.servers", "172.16.49.173:9092");
//设置consumer group name,必须设置
props.put("group.id", a_groupId);
//设置自动提交偏移量(offset),由auto.commit.interval.ms控制提交频率
props.put("enable.auto.commit", "true");
//偏移量(offset)提交频率
props.put("auto.commit.interval.ms", "1000");
//设置使用最开始的offset偏移量为该group.id的最早。如果不设置,则会是latest即该topic最新一个消息的offset
//如果采用latest,消费者只能得道其启动后,生产者生产的消息
props.put("auto.offset.reset", "earliest");
//设置心跳时间
props.put("session.timeout.ms", "30000");
//设置key以及value的解析(反序列)类
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);
//订阅topic
consumer.subscribe(Arrays.asList("topic_test"));
while (true) {
//每次取100条信息
ConsumerRecords<String, String> records = consumer.poll(100);
for (ConsumerRecord<String, String> record : records)
System.out.printf("offset = %d, key = %s, value = %s", record.offset(), record.key(), record.value());
}
group.id :必须设置
auto.offset.reset:如果想获得消费者启动前生产者生产的消息,则必须设置为earliest;如果只需要获得消费者启动后生产者生产的消息,则不需要设置该项
enable.auto.commit(默认值为true):如果使用手动commit offset则需要设置为false,并再适当的地方调用consumer.commitSync(),否则每次启动消费折后都会从头开始消费信息(在auto.offset.reset=earliest的情况下);
官方对于consumer与partition的建议
1. 如果consumer比partition多,是浪费,因为kafka的设计是在一个partition上是不允许并发的,所以consumer数不要大于partition数
2. 如果consumer比partition少,一个consumer会对应于多个partitions,这里主要合理分配consumer数和partition数,否则会导致partition里面的数据被取的不均匀。最好partiton数目是consumer数目的整数倍,所以partition数目很重要,比如取24,就很容易设定consumer数目
3. 如果consumer从多个partition读到数据,不保证数据间的顺序性,kafka只保证在一个partition上数据是有序的,但多个partition,根据你读的顺序会有不同
4. 增减consumer,broker,partition会导致rebalance,所以rebalance后consumer对应的partition会发生变化
5. High-level接口中获取不到数据的时候是会block的
replication-factor副本:replication factor 控制消息保存在几个broker(服务器)上,一般情况下等于broker的个数。
查看topic属性:bin/kafka-topics.sh --zookeeper zk1:2181 --describe --topictopicname
8 Kafka开发
8.1 Producer
KafkaProducer是一个发送record到Kafka Cluster的客户端API。这个类线程安全的。在应用程序中,通常的作法是:所有发往一个KafkaCluster的线程使用同一个Producer对象.。如果需要给多个Cluster发送消息,则需要使用多个Producer。
样例代码:
Properties properties = new Properties();
properties.put("bootstrap.servers", "192.168.56.101:9092");
properties.put("acks", "all");
properties.put("retries", 0);
properties.put("batch.size", 16384);
properties.put("linger.ms", 1);
properties.put("buffer.memory", 33554432);
properties.put("key.serializer",
"org.apache.kafka.common.serialization.StringSerializer");
properties.put("value.serializer",
"org.apache.kafka.common.serialization.StringSerializer");
KafkaProducer<String, String> producer = new KafkaProducer<String, String>(properties);
ProducerRecord<String, String> pducerRecord = new ProducerRecord<String, String>(TOPIC, message);
producer.send(pducerRecord, new CallBack(){ ... ... });
8.1.1 ProducerRecord
Producer要发送的消息记录类是ProducerRecord。看它的源码可知;
一条ProducerRecord通常包括5个字段:
l topic:指定该record发往哪个topic下。[Required]
l partition:指定该record发到哪个partition中。[Optional]
l key:一个key。[Optional]
l value:记录人内容。[Required]
l timestamp:时间戳。[Optional]
如果用户指定了partition,那么就发往用户指定的partition。如果用户没有指定partition,那么就会根据key来决定放到哪个partition,如果key也没有指定,则由producer随机选取一个partition。
在Producer端,如果用户指定了timestamp,则record使用用户指定的时间,如果用户没有指定,则会使用producer端的当前时间。在broker端,如果配置了时间戳采用createtime方式,则使用producer传给Broker的record中的timestramp时间,如果指定为logappendtime,则在broker写入到Log文件时会重写该时间。
8.1.2 send
新版本(0.10)里面的send函数是一个异步函数,用户线程调用send方法是将record放到BufferPool中缓存,并根据batch.size和linger.ms等参数来批量提交。执行流程是:
1. 由interceptorchain对ProducerRecord做发送前的处理
拦截器接口是:ProducerInterceport,用户可以自定义自己的拦截器实现。
该拦截器链,在Producer对象初始化时初始化,之后不会再变了。所以呢,拦截器链中的拦截器都是公用的,如果要自定义拦截器的话,这个是需要注意的。
n ProducerInterceptor有两个方法:
1) onSend:KafkaProducer#send 调用时就会执行此方法。
2) onAcknowledgement:发送失败,或者发送成功(broker 通知producer代表发送成功)时都会调用该方法。
2. 阻塞方式获取到broker cluster 上broker cluster的信息
采用RPC方式获取到的broker信息,由一个MetaData类封装。它包括了broker cluster的必要信息,譬如有:所有的broker信息(id\host\port等)、所有的topic名称、每一个topic对于的partition情况(id、leader node、replica nodes、ISR nodes等)。
虽然该过程是阻塞的,但并不是每发送一个record都会通过RPC方式来获取的。Metadata会在Producer端缓存,只有在record中指定的topic不存在时、或者MetaData轮询周期到时才会执行。
3. 对record中key、value进行序列化
内置了基于String、Integer、Long、Double、Bytes、ByteBuffer、ByteArray的序列化工具。
4. 为record设置partition属性
前面说过,创建ProducerRecord时,partition是Optional的。所以如果用户创建record时,没有指定partition属性,则由partition计算工具(Partitioner 接口)来计算出partition。这个计算方式可以自定义。Kafka Producer 提供了内置的实现:
ü 如果提供了Key值,会根据key序列化后的字节数组的hashcode进行取模运算。
ü 如果没有提供key,则采用迭代方式(其实取到的值并非完美的迭代,而是类似于随机数)。
5. 校验record的长度是否超出阈值
MAX_REQUEST_SIZE_CONFIG=”max.request.size”
BUFFER_MEMORY_CONFIG=”buffer.memory”
超出任何一项就会抛出异常。
6. 为record设置timestamp
如果用户创建ProducerRecord时没有指定timestamp,设置为producer的当前时间。
其实在java client中,设计了一个Time接口,专门用于设置这个时间的。内置了一个实现SystemTime,这里将record timestamp设置为当前时间,就是由SystemTime来完成的。所以如果希望在kafka producer java client中使用其它的时间,可以自定义Time的实现。
这个时间戳有什么要注意的,比如producer和kafkaserver在两台机器上,时间不同步,会对后续有什么影响吗?
7. 将该record压缩后放到BufferPool中
这一步是由RecordAccumulator来完成的。RecordAccumulator中为每一个topic维护了一个双端队列Deque<RecordBatch>,队列中的元素是RecordBatch(RecordBatch则由多个record压缩而成)。RecordAccumulator要做的就是将record压缩后放到与之topic关联的那个Deque的最后面。
在将record放到Deque中最后一个RecordBatch中的逻辑为:如果最后一个recordbatch可以放的下就放,放不下就新建一个RecordBatch。
RecordBatch实际上是存储于BufferPool中,所以这个过程实际上是把record放在BufferPool中。在创建BufferPool之初,会指定BufferPool的总大小,BufferPool中每一个RecordBatch的大小等等配置。
8. 唤醒发送模块
执行到上一步时,KafkaProducer#sender的处理基本算是完毕。这个一步的目的就是唤醒NIO Selector。
此外,在上述步骤2~8,不论哪一步出现问题,都会抛出异常。而抛出异常时,就会被KafkaProducer捕获到,然后交由Sensor(传感器)进行处理。而Sensor通常会调用第1步中提到的interceptorchain 执行onAcknowledgement告知用户。
8.1.3 send函数的发送调度处理
KafkaProducer#sender只是将record放到BufferPool中,并没有将record发出去,而发送调度,则是由另外一个线程(Sender)来完成的。
Sender的执行过程如下:
1. 取出就绪的record
这一步是检查要发送的record是否就绪:根据KafkaProducer维护的Metadata检查要每一个record要发往的Leader node是否存在。如果有不存在的,就设置为需要更新,并且这样的record认为还未就绪。以保证可以发到相关partition的leader node。
2. 取出RecordBatch,并过滤掉过期的RecordBatch
对于过期的RecordBatch,会通过Sensor通知Interceptor发送失败。
3. 为要发送的RecordBatch创建请求
一个RecordBatch一个ClientRequest。
4. 保留请求并发送
把请求对象保留到一个inFlightRequest 集合中。这个集合中存放的是正在发送的请求,是一个topic到Deque的Map。当发送成功,或者失败都会移除。
5. 处理发送结果
如果发送失败,会尝试retry。并由Sensor调度Interceptor。
如果发送成功,会由Sensor调度Interceptor。
8.1.4 Producer实现总结
从上述处理流程中,可以看到在javaclient中的一些设计:
1. InterceptorChain:可以做为用于自定义插件的接口。
2. MetaData:producer 不按需以及定期的发送请求获取最新的Cluster状态信息。Producer根据这个信息可以直接将record batch发送到相关partition的Leader中。也就是在客户端完成Load balance。
3. Partitioner:分区选择工具,选择发送到哪些分区,结合Metadata,完成Load balance。
4. RecordBatch:在客户端对record压缩进RecordBatch,然后一个RecordBatch发一次。这样可以减少IO操作的次数,提高性能。
5. 异步方式发送:提高用户应用性能。
8.1.5 Producer 配置说明
l bootstrap.servers
用于配置cluster中borker的host/port对。可以配置一项或者多项,不需要将cluster中所有实例都配置上。因为它后自动发现所有的broker。
如果要配置多项,格式是:host1:port1,host2:port2,host3:port3….
l key.serializer、value.serializer
配置序列化类名。指定 的这些类都要实现Serializer接口。
l acks
为了确保message record被broker成功接收。Kafka Producer会要求Borker确认请求(发送RecordBatch的请求)完成情况。
对于message接收情况的确认,Kafka Broker支持了三种情形:1、不需要确认;2)leader接收到就确认;3)等所有可用的follower复制完毕进行确认。可以看出,这三种情况代表不同的确认粒度。在JavaProducer Client中,对三种情形都做了支持,上述三种情形分别对应了三个配置项:0、1、-1。其实还有一个值是all,它其实就是-1。
Kafka Producer Java Client 是如何支持这三种确认:
1) 在为RecordBatch创建请求时,acks的值会被封装为请求头的一部分。
2) 发送请求后(接收到Broker响应前),立即判断是否需要确认该请求是否完成(即该RecordBatch是否被Broker成功接收),判断依据是acks的值是否是0。如果是0,即不需要进行确认。那么就认定该请求成功完成。既然认定是成功,那么就不会进行retry了。
如果值不是0,就要等待Broker的响应了。根据响应情况,来判断请求是否成功完成。
该配置项默认值是1,即leader接收后就响应。
l buffer.memory
BufferPool Size,也就是等待发送的Record的空间大小。默认值是:33554432,即32MB。
配置项的单位是byte,范围是:[0,….]
l compression.type
Kafka提供了多种压缩类型,可选值有4个: none, gzip, snappy, lz4。默认值是none。
l retries
当一个RecordBatch发送失败时,就会重新改善以确保数据完成交付。该配置设置了重试次数,值范围[0, Integer.Max]。如果是0,即便失败,也不会进行重发。
如果允许重试(即retries>0),但max.in.flight.requests.per.connection 没有设置成1。这种情况下,就可能会出现records的顺序改变的现象。例如:一个prodcuder client的sender线程在一次轮询中,如果有两个recordbatch都要发送到同步一个partition中,此时它们肯定是发往同一个broker的,并且是用的同一个TCP connection。如果出现RecordBatch1先发,但是发送失败,RecordBatch2紧接着RecordBatch1发送,它是发送成功的。然后RecordBatch1会进行重发。这样一来,就出现了broker接收到的顺序是RecordBatch2先于RecordBatch1的情况。
l batch.size
RecordBatch的最大容量。默认值是16384(16KB)。
l ssl.key.password
Keystore 文件中私钥的密码。可选的。
l ssl.keystore.location
Keystore文件的位置。可选的。
l ssl.keystore.password
Keystore 文件的密码。可选的。
l ssl.truststore.location
Trust store 文件的位置。可选的。
l ssl.truststore.password
Trust store文件的密码。可选的。
l client.id
逻辑名,client给broker发请求是会用到。默认值是:””。
l connections.max.idle.ms
Connection的最大空闲时间。默认值是540000 (9 min)
l linger.ms
Socket :solinger。延迟。默认值:0,即不延迟。
l max.block.ms
当需要的metadata未到达之前(例如要发送的record的topic,在Client中还没有相关记录时),执行KafkaProducer#send时,内部处理会等待MetaData的到达。这是个阻塞的操作。为了防止无限等待,设置这个阻塞时间是必要的。范围:[0, Long.MAX]
l max.request.size
最大请求长度,在将record压缩到RecordBatch之前会进行校验。超过这个大小会抛出异常。
l partitioner.class
用于自定义partitioner算法。默认值是:
org.apache.kafka.clients.producer.internals.DefaultPartitioner
l receive.buffer.byte
TCP receiver buffer的大小。取值范围:[-1, …]。这个配置项的默认值是32768(即 32KB)。
如果设置为-1,则会采用操作系统的默认值。
l request.timeout.ms
最大请求时长。因为发起请求后,会等待broker的响应,如果超过这个时间就认为请求失败。
l timeout.ms
这个时间配置的是follower到leader的ack超时时间。这个时间和producer发送的请求的网络无关。
l block.on.buffer.full
当bufferPool用完后,如果client还在使用KafkaProducer发送record,要么是BufferPool拒绝接收,要么是抛出异常。
这个配置是默认值是flase,也就是当bufferpool满时,不会抛出BufferExhaustException,而是根据max.block.ms进行阻塞,如果超时抛出TimeoutExcpetion。
如果这个属性值是true,则会把max.block.ms值设置为Long.MAX。另外该配置为true时,metadata.fetch.time.ms将不会生效了。
l interceptor.class
自定义拦截器类。默认情况下没有指定任何的interceptor。
l max.in.flight.requests.per.connection
每个连接中处于发送状态的请求数的最大值。默认值是5。范围是[1, Integer.MAX]
l metric.reporters
MetricReporter的实现类。默认情况下,会自动的注册JmxReporter。
l metrics.num.samples
计算metric时的采样数。默认值是2。范围:[1,Integer.MAX]
l metrics.sample.window.ms
采样的时间窗口。默认值是30000(30s)。范围:[0, Long.MAX]
如果我的kafka集群已经开始运行了2个小时,有一个消费程序在持续的处理着消息。
8.2 Consumer
- Kafka 0.10 安装及使用
- kafka安装及使用
- kafka安装及Kafka-PHP扩展的使用
- Kafka安装及部署
- Kafka安装及部署
- Kafka安装及部署
- kafka安装及部署
- kafka安装及测试
- kafka安装及命令
- kafka安装及应用
- kafka安装及配置
- Kafka配置及使用
- KAFKA安装和使用
- kafka安装与使用
- kafka安装使用
- Kafka集群安装使用
- Kafka安装使用入门
- kafka 安装,使用教程
- java并发编程实践学习(1)线程安全
- Spring 注释 Autowired 和@Resource 的区别
- Python库安装
- ios 上滑隐藏导航下拉显示导航栏实现
- TCP三次握手与四次挥手最简洁易懂的解释
- Kafka 0.10 安装及使用
- VB.NET读取版本信息
- 百度语音的网页接口(待续)
- Hadoop2.5.2学习04--HDFS原理及操作
- Redis-类型检查与命令多态
- 小博老师解析Java核心技术 ——AJAX第二弹
- Glide进阶详解(八)
- tensorflow 安装 和集成到IDEA
- Boost.Log v2 : 5.设计概要