创建topic:
kafka-topics.sh --create --topic test --zookeeper s102:2181 --partitions 3 --replication-factor 2
列出topic:
kafka-topics.sh --list --zookeeper s102:2181
启动生产者:
kafka-console-producer.sh --broker-list s102:9092 --topic test
启动消费者:
kafka-console-consumer.sh --zookeeper s102:2181 --topic test //老API
kafka-console-consumer.sh --bootstrap-server s102:9092 --topic t2 //新API
kafka数据在zk中的分布
====================================
/broker/topics/t1 partitions":{"1":[103,104],"0":[102,103]}
/broker/topics/t1/partition/0/stat "leader":102,"isr":[103,102] //in-sync
/broker/topics/t1/partition/1/stat "leader":103,"isr":[104,103]
分区 0 102
103
1 103
104
kafka真实数据,在linux中以 68byte头+数据
s102: tt-2 √ √
s103: tt-0 √ √
s104: tt-1 √ √
分区:水平分散节点压力
kafka的数据是以轮询方式发送到每个分区
随意指定,依赖于硬件情况
副本:提供备份
最大不能超过broker数量
leader:
kafka产生数据时,先发送给leader
非leader节点在之后同步leader数据 in-sync
消费者和消费者组:
==============================
/consumers //消费者组
//默认创建控制台消费者,会创建一个新组
创建消费者手动指定消费者组
kafka-console-consumer.sh --topic t1 --zookeeper s102:2181 --group g2
一个消费者组包括多个消费者,当数据产生时,消费者组中只有一个消费者能收到消息
从指定位置重复消费数据:
===================================
原理:当消费者消费数据后,zk会记录一个偏移量,每消费一条数据,偏移量+1
1、修改偏移量 /consumer/g2/offsets/
将值设为自定义值
2、在相同消费者组中启动另一个消费者
消费线程数:
====================================
分区数 = 2 钱袋
线程数 = 2 手
一个消费者有多个消费线程,一个消费线程处理一个分区
当分区 = 4 , 线程 = 2 消费者 = 1 此时消费者只能一个线程轮训监控两个分区 1X2X2 抢银行来回两次
消费者 = 2 此时两个消费者能够分别使用两个线程监控四个分区 2X2X1 抢银行两个人一次过
分区 = 2 , 线程 = 2 消费者 = 1 此时消费者两个线程能够处理两个分区 一个人一次过
消费者 = 2 第二个消费者不监控任何分区 人多了,不带另一个玩
producer配置文件:
================================
metadata.broker.list //连接kafka服务器的地址列表
//s102:9092,s103:9092,s104:9092
serializer.class //指定message的消息格式 Encoder
//默认DefaultEncoder ==> byte[]
//kafka.serializer.IntegerEncoder
//kafka.serializer.LongEncoder
//kafka.serializer.NullEncoder
//kafka.serializer.StringEncoder
key.serializer.class //指定key的串行化格式
producer.type //生产者类型
//包括sync和async
//sync ===> 数据的同步传输
类似于hbase中的put(put)
多用于严格有序,速度稍慢
//async ==> 数据异步(批次)传输
类似于hbase中的put(List<put>)
不严格有序,速度稍快
request.required.acks //控制生产者接收确认回执
//0 代表不接收确认回执,最低延迟,持久性最差
//1 只接收来自leader的确认回执,延迟稍低,持久性略好
//-1 接收所有副本确认回执,延迟最差,持久性最好
partitioner.class //kafka分区类,可以自定义
compression.codec //压缩编解码器
//none, gzip, and snappy.
batch.num.messages //async模式设置批次大小
//指定message个数
//默认200
测试同步和异步生产:
sync: 10000 //52,479ms
async: 10000 //4,871ms
测试确认回执:
async: 1 //3,319ms
async: 0 //3,039ms
async: -1 //4,744ms
Consumer配置文件:
=========================================
group.id //对消费者组的唯一标识 id
consumer.id //对消费者的唯一标识
zookeeper.connect //指定zk连接串
//s102:2181,s103:2181,s104:2181
client.id //客户端的唯一标识,类似于group.id
zookeeper.session.timeout.ms //zk会话超时
zookeeper.connection.timeout.ms //zk连接超时
zookeeper.sync.time.ms //zk的follower和leader的同步时间
auto.commit.enable //启用自动提交consumer偏移量
//消费数据,偏移量会自动修改
//默认为true
//注意:偏移量在消费者组下
auto.commit.interval.ms //设置自动提交间隔
auto.offset.reset //重置偏移量
//自定义消费位置
//largest: 重置消费位置为最大偏移量
//smallest: 重置消费位置为最小偏移量
//anything else: 抛出异常
consumer.timeout.ms //消费者超时设置
偏移量:
smallest:数据的最小偏移量 不一定是0 和zk无关,数据的起始
largest: zk中数据偏移量
日志保留时间:server.properties
# 日志的删除时间,经过7天后自动删除
log.retention.hours=168
# 超过此字节,数据会删除
# log.retention.bytes=1073741824
# 超过此字节,创建一个新数据段
log.segment.bytes=1073741824
kafka中的分区:partition
=================================
1、编写自定义分区:
public class MyPartition implements Partitioner {
public MyPartition(VerifiableProperties props) {
}
public int partition(Object key, int numPartitions) {
Integer k = (Integer)key;
return k % numPartitions;
}
}
2、在生产者中指定分区
public class MyProducer {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
Properties props = new Properties();
props.put("metadata.broker.list", "s102:9092, s103:9092, s104:9092");
props.put("serializer.class", "kafka.serializer.StringEncoder");
//**********指定key序列化类型**********
props.put("key.serializer.class", "kafka.serializer.IntegerEncoder");
//**********指定分区类**********
props.put("partitioner.class","kafka.MyPartition//完整类名");
props.put("producer.type","async");
props.put("request.required.acks", "0");
ProducerConfig config = new ProducerConfig(props);
Producer<Integer, String> producer = new Producer<Integer, String>(config);
for (int i = 0; i < 10000; i++) {
String msg = "tom" + i;
//**********将key写入**********
KeyedMessage<Integer, String> data = new KeyedMessage<Integer,String>("t3",i, msg);
producer.send(data);
Thread.sleep(2000);
}
producer.close();
System.out.println(System.currentTimeMillis()- start);
}
}
3、消费者观察数据与所在的分区对应关系
kafka新API:Producer配置文件
============================================
kafka中发送方法:send() 是默认异步
key.serializer
value.serializer
acks //0 没有回执
//1 leader回执
//all 所有回执
retries //发送数据失败重新发送的次数
bootstrap.servers //相当于老API中的metadata.broker.list
//broker列表,kafka地址
buffer.memory //数据缓冲区内存大小,到达则会被发送,字节数long
compression.type //
batch.size //数据发送到同一分区,尝试将指定大小的数据量变为一个批次
//小批次减少吞吐量 单位int
client.id //客户端id string
linger.ms //记录在缓冲区中停留时间 long
partitioner.class //分区类
将kafka中异步发送改成同步
============================================
Future future = producer.send(record, callback);
Callback对象:send方法中的回调机制。在数据发送后的信息汇报
信息包括元数据(metadata),异常(exception)
使用场景:当数据发送失败,能够从中获取元数据和异常状态
避免数据丢失,对数据再次处理
new Callback() {
public void onCompletion(RecordMetadata metadata, Exception exception) {
}
}
Future对象:send方法的返回值,通过future.get()获取到元数据(metadata)
通过调用future.get(),可以将异步变为同步
原理:下一条记录必须要等前一个记录返回值调用之后才能够进行发送
性能比较:
async: 1000条数据:1239ms
sync: 1000条数据:3360ms
kafka数据在分区中有序,跨分区无序,相当于Hadoop中的部分排序
producer新API的创建
============================================
public class NewProducer {
public static void main(String[] args) throws Exception {
Properties props = new Properties();
props.put("bootstrap.servers", "s102:9092");
props.put("acks", "0");
props.put("linger.ms", 1);
props.put("buffer.memory", 33554432);
props.put("key.serializer", "org.apache.kafka.common.serialization.IntegerSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
long start = System.currentTimeMillis();
//初始化kafka生产者对象
Producer<Integer, String> producer = new KafkaProducer<Integer, String>(props);
for (int i = 0; i < 1000; i++) {
//初始化kafka记录,包括topic,key,value
ProducerRecord record = new ProducerRecord("t3",i,"tom"+ i);
Future future = producer.send(record, new Callback() {
public void onCompletion(RecordMetadata metadata, Exception exception) {
System.out.println(metadata.toString());
exception.printStackTrace();
}
});
future.get();
}
producer.close();
System.out.println(System.currentTimeMillis() - start);
}
}
Consumer新API的创建
============================================
public class NewConsumer {
public static void main(String[] args) {
Properties props = new Properties();
props.put("bootstrap.servers", "s102:9092");
props.put("group.id", "g3");
props.put("enable.auto.commit", "true");
props.put("key.deserializer", "org.apache.kafka.common.serialization.IntegerDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer<Integer, String> consumer = new KafkaConsumer<Integer, String>(props);
//新ConsumerAPI可以指定多主题订阅
List<String> topics = new ArrayList<String>();
topics.add("t3");
topics.add("t2");
consumer.subscribe(topics);
while (true) {
//通过poll方式获取数据,返回的是一个批次的数据
ConsumerRecords<Integer, String> records = consumer.poll(1000);
for (ConsumerRecord<Integer, String> record : records)
System.out.println(
"par: " + record.partition() +
",offset: "+ record.offset() +
", key: " + record.key() +
", value: " +record.value());
}
}
}
Consumer新API的新特性:
============================================
kafka-console-consumer.sh --bootstrap-server s102:9092 --topic t2 //启动控制台消费者
1、指定分区消费,不能和 consumer.subscribe(topics)一起用
List<TopicPartition> tps = new ArrayList<TopicPartition>();
TopicPartition tp = new TopicPartition("t3",0);
tps.add(tp);
consumer.assign(tps);
2、指定偏移量消费:off:3534 key:30 val:tom30
前提:需要先指定分区分区进行消费
//新ConsumerAPI可以指定分区进行消费
List<TopicPartition> tps = new ArrayList<TopicPartition>();
TopicPartition tp = new TopicPartition("t3",0);
tps.add(tp);
consumer.assign(tps);
1、 //新ConsumerAPI可以指定偏移量消费
//程度在于控制偏移量,类似于修改zk偏移量数据
//consumer.commitSync(offsets)之后,所有消费者都从此处开始消费
Map<TopicPartition, OffsetAndMetadata > offsets = new HashMap<TopicPartition, OffsetAndMetadata>();
OffsetAndMetadata om = new OffsetAndMetadata(3534);
offsets.put(tp,om);
consumer.commitSync(offsets);
2、 //将poll的指针移动到指定的偏移量,对consumer影响较小
consumer.seek(tp,3500)
kafka新API:Consumer配置文件
============================================
key.deserializer //
value.deserializer //
bootstrap.servers //
fetch.min.bytes //consumer抓取请求数 int
group.id //自定义消费者组id
heartbeat.interval.ms //适用于consumer组,确保会话有效 int
max.partition.fetch.bytes //consumer抓取单个分区请求数 int
session.timeout.ms //超时视为会话失效
auto.offset.reset //重置偏移量
//自定义消费位置
//latest: 重置消费位置为最大偏移量
//earliest: 重置消费位置为最小偏移量
//none:
//other:抛出异常
enable.auto.commit //自动提交偏移量 boolean
exclude.internal.topics //决定内部topic是否暴露数据给消费者
fetch.max.bytes //一个批次获取的最大数据字节数 int
isolation.level //read_committed:只能读到提交的数据
//read_uncommitted:能读到未提交的数据
//producer的事务性
props.put("transactional.id", "my-transactional-id");
producer.initTransactions();
producer.beginTransaction();
producer.commitTransaction();
producer.abortTransaction();
max.poll.interval.ms
max.poll.records //设置一次poll中最大记录数