kafka java端详解

生产者api
创建生产者:
Properties props = new Properties();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, “192.168.65.60:9092,192.168.65.60:9093,192.168.65.60:9094”);
分区发送消息
指定分区
ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>(TOPIC_NAME
, 0, order.getOrderId().toString(), JSON.toJSONString(order));
TOPIC_NAME:我们的主题名
0:我们的分区
未指定分区
ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>(TOPIC_NAME
, order.getOrderId().toString(), JSON.toJSONString(order));
当我们未指定分区的时候,他会根据一个公式通过order.getOrderId().toString()来计算出hash散列值,再通过取模,获得具体分区发送;
计算公式:hash(key)%partitionNum (这里的key就是上面的order.getOrderId().toString())
同步发送消息
RecordMetadata metadata = producer.send(producerRecord).get();
异步发送消息
producer.send(producerRecord, new Callback() {
public void onCompletion(RecordMetadata metadata, Exception exception) {
if (exception != null) {
System.err.println(“发送消息失败:” + exception.getStackTrace());

                }
                if (metadata != null) {
                    System.out.println("异步方式发送消息结果:" + "topic-" + metadata.topic() + "|partition-"
                            + metadata.partition() + "|offset-" + metadata.offset());
                }
                countDownLatch.countDown();
            }
        });
        countDownLatch.await(5, TimeUnit.SECONDS);

同步与异步的区别:同步会等待消息成功返回后再执行下一步,异步则是发送完消息就不管了,等到消息发送成功后,执行回调方法;
ack设置:
props.put(ProducerConfig.ACKS_CONFIG, “1”);
假设有三个分区,分区有三个副本;
0:发送完消息就返回成功,不管消息是否持久化到副本中;
1:发送完消息,leader副本持久化成功后返回成功
-1或者all:需要设置min.insync.replicas值,默认为大于等于2,当这个值为2时,那么除了主节点需要持久化成功,还需要一个从节点也持久化成功,才返回成功;
分析:0在网络波动的情况下会丢失一点数据,-1则很安全即使主节点宕机也能保证数据在从节点已经保存了,从而重选主节点时数据也不会丢失。1则居中除了在宕机的时候会丢一点数据以外,网络波动一般也不会使得他丢失数据;但是相对的其效率0,1,-1依次变慢,根据具体业务,是否允许丢失数据来设置ack的值;
失败重试
props.put(ProducerConfig.RETRIES_CONFIG, 3);
消息发送失败后重试,次数为3
props.put(ProducerConfig.RETRY_BACKOFF_MS_CONFIG, 300);
重试间隔时间
批量发送设置
发送消息的时候也能根据偏移量一个一个发送给服务端,但是一般不怎么做,这样做效率很低。一般都是把消息一批一批的发送给服务端;
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
本地放置缓存总量设置32M左右
props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
本地线程会从缓存区取出数据,当取到数据达到16K就往服务端发送
props.put(ProducerConfig.LINGER_MS_CONFIG, 10);
这里设置如果在10ms时间内从本地缓存接收的数据16K但是时间已经过了10ms,那么消息也会马上发送;
消费者api
创建消费者
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, “192.168.65.60:9092,192.168.65.60:9093,192.168.65.60:9094”);
设置主题(topic):
consumer.subscribe(Arrays.asList(TOPIC_NAME));
如果要指定分区:
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
主题可以设置多个,一个消费者可以同时订阅多个主题;
长轮询拉取消息
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
在1秒的时间内去kafka取消息,如果没有取到消息,会在一秒的时间内多次取消息;加上内循环即可实现监听kafka不断的取消息出来;
自动提交和手动提交
自动提交:
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, “true”);
默认为true,默认是开启自动提交模式
props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, “1000”);
设置自动提交间隔时间 这里是1秒

我们一般要把自动提交改成手动提交的方式,因为自动提交存在多种弊端;如果我们这边设置1秒钟后自动提交。如果我们程序出现意外,5秒钟才能处理完,他1秒钟就把消息处理完毕的结果返回回去了。三秒钟的时候程序挂了。这样我们就提交了错误的信息,再者如果我们消息处理非常快0.1秒就处理完毕了,那么就很可能出现重复消费的情况;所以我们一般把自动提交关闭掉;
手动提交分为同步提交和异步提交:
手动同步提交:consumer.commitSync();
手动异步提交:
consumer.commitAsync(new OffsetCommitCallback() {
@Override
public void onComplete(Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception) {
if (exception != null) {
System.err.println("Commit failed for " + offsets);
System.err.println("Commit failed exception: " + exception.getStackTrace());
}
}
});
区别在于:同步阻塞线程直到返回成功,异步不会阻塞线程,会有一个回调方法;一般同步提交用的比较多;
消息回溯消费
把分区中的所有消息全部消费一遍,与消息是否被消费过无关。之前被消费过的消息依然会被消费;
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
consumer.seekToBeginning(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
指定offset消费
consumer.assign(Arrays.asList(new TopicPartition(TOPIC_NAME, 0)));
consumer.seek(new TopicPartition(TOPIC_NAME, 0), 10);
指定时间开始消费
List topicPartitions = consumer.partitionsFor(TOPIC_NAME);
//从1小时前开始消费
long fetchDataTime = new Date().getTime() - 1000 * 60 * 60;
Map<TopicPartition, Long> map = new HashMap<>();
for (PartitionInfo par : topicPartitions) {
map.put(new TopicPartition(TOPIC_NAME, par.partition()), fetchDataTime);
}
Map<TopicPartition, OffsetAndTimestamp> parMap = consumer.offsetsForTimes(map);
for (Map.Entry<TopicPartition, OffsetAndTimestamp> entry : parMap.entrySet()) {
TopicPartition key = entry.getKey();
OffsetAndTimestamp value = entry.getValue();
if (key == null || value == null) continue;
Long offset = value.offset();
System.out.println(“partition-” + key.partition() + “|offset-” + offset);
System.out.println();
if (value != null) {
consumer.assign(Arrays.asList(key));
consumer.seek(key, offset);
}
}
这里我们设置1小时内的消息被消费;
寻找消息
根据偏移量和时间怎么找到消息的呢?
kafka的日志是分段存储的,如果一个分区的数据量非常大,放在一个文件里面那么就非常不合理了,如果文件达到一个1T那么要把这个文件加载到内存中很吓人,需要很多时间;在kafka中规定每个log文件最大是1G;这使得kafka的文件方便于加载到内存中操作;在kafka中不仅仅有log文件,还有配套产生了index和indextime文件;当log的消息每发满4k时就会存储一个偏移量(offset)到index文件中,当我们根据偏移量去查找消息的时候。(0-4000-8000-16000);这样我们通过offset查找的时候如果我们的偏移量值为5000,那么通过二分法查找找到他在4000到8000这个区间。然后直接去这个区间所在的文件在4000到8000这个区间顺序查找比对得到具体消息。同样的indextime则是保存时间戳也是同样的方式找到我们要找的时间点后的第一个偏移量。然后查找出所有之后的数据即可;
最大pull消息
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 50);
一次poll最大拉取消息的条数,如果消费者处理速度很快,可以设置大点,如果处理速度一般,可以设置小点,这里我们设置为50条;
props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 30 * 1000);
这里我们设置30秒内,没有第二次处理消息,那么服务端认定这个消费者很弱鸡,消费能力很差。不让他再次来消费我们的消息;
心跳
props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, 1000);
props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 10 * 1000);
这里设置每秒钟感受消费者心跳,如果10秒钟都没有感受到消费者的心跳。那么将该消费组会被认定为故障机踢出消费范围。

当消费主题的是一个新的消费组,或者指定offset的消费方式,offset不存在,那么应该如何消费
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, “earliest”);
latest 默认:只消费自己启动后发送到主题的消息;
earliest:第一次从头开始消费,之后根据自己记录的offset来消费跟上面的回溯消费不同,回溯消费是每一次消费都是从头开始消费一遍才算完;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值