kafka9使用demo
新的Comsumer API不再有high-level、low-level之分了,而是自己维护offset。这样做的好处是避免应用出现异常时,数据未消费成功,但Position已经提交,导致消息未消费的情况发生。通过查看API,新的Comsumer API有以下功能:
- Kafka可以自行维护Offset、消费者的Position。也可以开发者自己来维护Offset,实现相关的业务需求。
- 消费时,可以只消费指定的Partitions
- 可以使用外部存储记录Offset,如数据库之类的。
- 自行控制Consumer消费消息的位置。
- 可以使用多线程进行消费
以上来自网络。
我个人觉得kafka9与kafka8相比,能够同时消费多个topic的数据;不用再指定zookeeper的地址,只需要指定kafka的broker的地址即可;内部多线程消费kafka消息......
消费者demo:
ConsumerLoop.java
package com.kafka9.consumer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.errors.WakeupException;
/**
* @author 作者:
* @date 创建时间: 2017年3月20日 下午2:36:20
* @version 版本: 1.0
*/
public class ConsumerLoop implements Runnable {
private static final String topicKey = "topics";
private static final String autoCommitKey = "enable.auto.commit";
private final KafkaConsumer<String, String> consumer;
private final List<String> topics;
private final String autoCommit;
private final String id;
public ConsumerLoop(String id, Properties props) {
this.id = id;
this.autoCommit = props.getProperty(autoCommitKey)==null?"":props.getProperty(autoCommitKey);
this.topics = new ArrayList<String>();
String args [] = props.getProperty(topicKey).split(",");
for(int i =0;i<args.length;i++){
this.topics.add(args[i]);
}
this.consumer = new KafkaConsumer<String, String>(props);
}
public void run() {
int count = 0;
try {
consumer.subscribe(topics);
while (true) {
ConsumerRecords<String, String> records = consumer.poll(100);//当没有消息是,poll线程阻塞。
for (ConsumerRecord<String, String> record : records) {
Map<String, Object> data = new HashMap<String, Object>();
data.put("partition", record.partition());
data.put("offset", record.offset());
data.put("value", record.value());
System.out.println(this.id + ": " + data);
count ++;
System.out.println(count);
}
if(this.autoCommit.equals("false")){//当配置默认提交为false时,手动同步offset
consumer.commitAsync();
}
}
} catch (WakeupException e) {
e.printStackTrace();
} finally {
consumer.close();
}
System.out.println(count);
}
public void shutdown() {
consumer.wakeup();
}
}
ConsumerApp.java
package com.kafka9;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.kafka.common.serialization.StringDeserializer;
import com.kafka9.consumer.ConsumerLoop;
/**
* Hello world!
*
*/
public class ConsumerApp {
private static final int threadNum = 1;
private static final ExecutorService executor = Executors.newFixedThreadPool(threadNum);
private static final List<ConsumerLoop> consumers = new ArrayList<ConsumerLoop>();
private static final String threadName = "thread-";
public static void main( String[] args ){
Properties props = new Properties();
props.put("topics", "xqq");//设置监听的topic,多个用逗号分隔
props.put("bootstrap.servers", "172.16.13.123:9092,172.16.13.124:9092,172.16.13.125:9092");//配置kafka节点,获取元数据
props.put("group.id", "group-2");//配置kafka消费者的分组
props.put("key.deserializer", StringDeserializer.class.getName());//设置key string字符串的编码格式
props.put("value.deserializer", StringDeserializer.class.getName());//设置value string字符串的编码格式
/**
* 开始消费的历史
* latest 最新
* , earliest 最早
* , none 空
* */
props.put("auto.offset.reset", "earliest");
props.put("enable.auto.commit", "false");//是否自动提交,默认为true。如果需要手动提交offset需要设置成false,消费者线程中需要手动提交offset
/* 自动确认offset的时间间隔 */
props.put("auto.commit.interval.ms", "1000");
props.put("session.timeout.ms", "30000");
//消息发送的最长等待时间
//这个时间必须大于session.timeout.ms这个的时间
props.put("request.timeout.ms", "40000");
//一次从kafka中poll出来的数据条数
//max.poll.records条数据需要在在session.timeout.ms这个时间内处理完
props.put("max.poll.records","100");
//server发送到消费端的最小数据,若是不满足这个数值则会等待直到满足指定大小。默认为1表示立即接收。
props.put("fetch.min.bytes", "1");
//若是不满足fetch.min.bytes时,等待消费端请求的最长等待时间
props.put("fetch.wait.max.ms", "1000");
for(int i=0;i<threadNum;i++){
String currentThreadId = threadName + i;//拼接当前线程id
ConsumerLoop consumer = new ConsumerLoop(currentThreadId,props);//new一个线程对象
consumers.add(consumer);
executor.submit(consumer);
}
}
}
常用配置见代码及注释。
生成者demo:
KafkaProducer.java
package com.kafka9.producer;
import java.util.Properties;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
/**
* @author 作者:
* @date 创建时间: 2017年3月21日 上午9:46:53
* @version 版本: 1.0
*/
public class KafkaProducer {
private static final String topicKey = "topic";
private Properties prop;
private String topic;
private Producer<String, String> producer;
public KafkaProducer(Properties prop){
this.prop = prop;
this.topic = prop.getProperty(topicKey);
this.producer = new org.apache.kafka.clients.producer.KafkaProducer<String,String>(this.prop);
}
public void sendMessage(String key ,String message){
if(this.producer!=null){
producer.send(new ProducerRecord<>(topic, key, message));
}else{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void close(){
if(this.producer!=null){
this.producer.close();
}
}
}
ProducerApp.java
package com.kafka9;
import java.util.Properties;
import com.kafka9.producer.KafkaProducer;
import com.kafka9.producer.SimplePartitioner;
/**
* @author 作者: jxw
* @date 创建时间: 2017年3月21日 上午10:04:00
* @version 版本: 1.0
*/
public class ProducerApp {
public static void main(String args[]) {
Properties props = new Properties();
props.put("topic", "qxltest");// 设置生产者的topic
props.put("bootstrap.servers", "172.16.13.123:9092");// 设置集群中一台kafka节点,用于获取元数据信息
/**
* producer需要server接收到数据之后发出的确认接收的信号,此项配置就是指procuder需要多少个这样的确认信号。
* 此配置实际上代表了数据备份的可用性。以下设置为常用选项:(1)acks=0: 设置为0表示producer不需要等待任何确认收到的信息。
* 副本将立即加到socket buffer并认为已经发送。没有任何保障可以保证此种情况下server已经成功接收数据,
* 同时重试配置不会发生作用(因为客户端不知道是否失败)回馈的offset会总是设置为-1;(2)acks=1:
* 这意味着至少要等待leader已经成功将数据写入本地log,但是并没有等待所有follower是否成功写入。这种情况下,
* 如果follower没有成功备份数据,而此时leader又挂掉,则消息会丢失。 (3)acks=all:
* 这意味着leader需要等待所有备份都成功写入日志,这种策略会保证只要有一个备份存活就不会丢失数据。这是最强的保证。,
*/
props.put("acks", "all");
props.put("retries", 0);// 失败重试次数
/**
* producer将试图批处理消息记录,以减少请求次数。这将改善client与server之间的性能。这项配置控制默认的批量处理消息字节数。
* 不会试图处理大于这个字节数的消息字节数。发送到brokers的请求将包含多个批量处理,其中会包含对每个partition的一个请求。
* 较小的批量处理数值比较少用,并且可能降低吞吐量(0则会仅用批量处理)。较大的批量处理数值将会浪费更多内存空间,
* 这样就需要分配特定批量处理数值的内存大小。
*/
props.put("batch.size", 1);
/**
* producer组将会汇总任何在请求与发送之间到达的消息记录一个单独批量的请求。通常来说,这只有在记录产生速度大于发送速度的时候才能发生。
* 然而,在某些条件下,客户端将希望降低请求的数量,甚至降低到中等负载一下。这项设置将通过增加小的延迟来完成–即,不是立即发送一条记录,
* producer将会等待给定的延迟时间以允许其他消息记录发送,这些消息记录可以批量处理。这可以认为是TCP种Nagle的算法类似。
* 这项设置设定了批量处理的更高的延迟边界:一旦我们获得某个partition的batch.size,他将会立即发送而不顾这项设置,
* 然而如果我们获得消息字节数比这项设置要小的多, 我们需要“linger”特定的时间以获取更多的消息。
* 这个设置默认为0,即没有延迟。设定linger.ms=5,例如,将会减少请求数目,但是同时会增加5ms的延迟。
*/
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");
props.put("partitioner.class", SimplePartitioner.class.getName());
KafkaProducer producer = new KafkaProducer(props);
long t1 = System.currentTimeMillis();
for (int i = 1; i <= 50000; i++) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
producer.sendMessage(String.valueOf(i), "{\"number\":\""+i+"\",\"ClientAcctNo\":\"6217960701067177\",\"TranDate\":\"20170415\",\"TranTime\":\"174100123\"}");
}
long t2 = System.currentTimeMillis();
System.out.println("第一批:"+(t2-t1)+"ms");
//
producer.close();
}
}
完整的demo代码连接如下:
http://download.csdn.net/download/u011637069/9859723