Maven依赖:
org.apache.kafka
kafka-clients
0.10.1.0
一、生产者
首先先看官方API示例:
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("acks", "all");
props.put("retries", 0);
props.put("batch.size", 16384);
props.put("linger.ms", 1);
props.put("buffer.memory", 33554432);
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
Producer producer = new KafkaProducer<>(props);
for(int i = 0; i < 100; i++)
producer.send(new ProducerRecord("my-topic", Integer.toString(i), Integer.toString(i)));
producer.close();
生产者目前没有碰到太多问题,这也跟kafka一个生产,多个消费的机制有关。需要注意的有两点:
1、acks回令。如果必须等待回令,那么设置acks为all;否则,设置为-1;等待回令会有性能损耗。
2、生产者在发送消息的过程中,会自己默认批量提交。所以,如果单条指令的发送请求,记得发送完后flush才能生效。
二、消费者
依旧先看官方示例:
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "test");
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "1000");
props.put("session.timeout.ms", "30000");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
KafkaConsumer consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("foo", "bar"));
while (true) {
ConsumerRecords records = consumer.poll(100);
for (ConsumerRecord record : records)
System.out.printf("offset = %d, key = %s, value = %s", record.offset(), record.key(), record.value());
}
消费者问题说明:
1、配置bootstarp.servers后,如非特殊需求,不需要配置zookeeper.server。否则,容易造成zookeeper的数据不一致问题。
2、group.id是标识消费者的ID。每一个group.id消费后,kafka会记录该id消费的offset到zookeeper。所以,此处需要注意,(1)如果多个地方都使用相同的groupid,可能造成个别消费者消费不到的情况(2)如果单个消费者消费能力不足的话,可以启动多个相同groupid的consumer消费,处理相同的逻辑。但是,多线程的时候,需要增加每个groupid下的partition分区数量,便于每个线程稳定读取固定的partition,提高消费能力。
3、如非特殊需求,自动offset提交便可以满足;如果有消息的重复消费需求,可以手动存储每次的offset,自动提交。
4、自动提交周期。消费者每次从队列里拉出来的数量是多条,如果消费能力差,处理速度慢,在提交周期内没有完成处理,那么会导致提交失败,影响消费和系统稳定。所以可以考虑增加周期或者使用内存队列二次存储消息,提升处理速度。另外,可以查看IO是否使用批量提交的方式。如hbase,redis,mysql等。
5、消费者心跳超时时间。如果处理速度超出改时间,kafka认为消费者离线,提交也会失败,也会导致消费问题。所以,需要尽量让心跳超时大于offset自动提交周期。
6、consumer.subcribe(Arrays.asList("topic1","topics"))。消费者可以同时监听多个topic(通道),监听过程属于线程切换。如topic1中拉取10个,再去topic2中拉取10个,一起返回。具体监听topic的数量需要根据消息队列的设置和消息量确定。如果消息很少,一个线程完全可以胜任多个。接受完后增加处理逻辑就可以。如果消息量大,就一个线程一个topic,或者多个线程一个topic。
7、因为kafka会记住消费者的消息处理指针offset,所以,如果一旦消费者离线,那么会造成消息积压,无法保持最新的数据消费。除非每次更换groupid获取最新数据。但是这种更换的方式并不推荐,因为总会产生新的消费者,增加zookeeper的数据。所以,消费者尽量保持在线。如果不使用消息,可以存储、更新或丢弃。