1.基本信息
kafka版本:kafka_2.11-0.9.0.1
kafka jar包版本:0.9.0.1
kafka集羣:192.168.1.101,192.168.1.102,192.168.1.103
2.自定義partition分發策略, 新建MyPartitioner類, 根據key值進行自定義
import kafka.producer.Partitioner;
import kafka.utils.VerifiableProperties;
/**
*
* <p>類描述: 自定義分區分發策略 </p>
* <p>創建人:wanghonggang </p>
* <p>創建時間:2019年9月26日 下午3:30:11 </p>
*/
public class MyPartitioner implements Partitioner {
/**
* 構造函數
* 創建一個新的實例 MyPartitioner.
*
* @param props
*/
public MyPartitioner(VerifiableProperties props) {
}
/**
* 根據key和分區數量確定的分區號
*/
public int partition(Object key, int numPartitions) {
try {
long key_ = Long.parseLong((String) key);
return (int) Math.abs(key_ % numPartitions);
} catch (Exception e) {
return Math.abs(key.hashCode() % numPartitions);
}
}
}
3.生產者引用分配策略, 新建MessageProducer類
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig;
/**
*
* <p>類描述: 生產者 </p>
* <p>創建人:wanghonggang </p>
* <p>創建時間:2019年9月26日 下午3:32:58 </p>
*/
public class MessageProducer {
private Producer<String, String> inner;
private String topic="topic-test";
public static AtomicInteger count;
/**
* 構造函數
* 創建一個新的實例 MessageProducer.
*
* @throws Exception
*/
public MessageProducer() throws Exception {
Properties props = new Properties();
//集羣地址
props.put("metadata.broker.list", "192.168.1.101:9092,192.168.1.102:9092,192.168.1.103:9092");
props.put("key.serializer.class", "kafka.serializer.StringEncoder");
props.put("serializer.class", "kafka.serializer.StringEncoder");
//自定義分區分發策略
props.put("partitioner.class", "demo.producer.MyPartitioner");
ProducerConfig config = new ProducerConfig(props);
inner = new Producer<String, String>(config);
count = new AtomicInteger();
}
/**
*
* <p>方法描述:發送消息 TODO</p>
* <p>創建人: wanghonggang </p>
* <p>創建時間: 2019年9月26日 下午3:34:38 </p>
* <p>修改記錄:</p>
* @param message
* void
*/
public void send(String message) {
if (topic == null || message == null) {
return;
}
// 指定key值避免數據傾斜,使消息均勻分佈到各個分區中
KeyedMessage<String, String> km = new KeyedMessage<String, String>(topic, count.toString(), message);
inner.send(km);
count.incrementAndGet();
}
/**
*
* <p>方法描述:關閉 TODO</p>
* <p>創建人: wanghonggang </p>
* <p>創建時間: 2019年9月26日 下午3:34:28 </p>
* <p>修改記錄:</p>
* void
*/
public void close() {
try {
inner.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
*
* <p>方法描述:測試 TODO</p>
* <p>創建人: wanghonggang </p>
* <p>創建時間: 2019年9月26日 下午3:30:01 </p>
* <p>修改記錄:</p>
* @param args
* void
*/
public static void main(String[] args) {
MessageProducer p;
try {
p = new MessageProducer();
for(int i=1;i<100;i++){
p.send(i+"");
}
p.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
4, 運行測試方法, 可見kafka已產生數據, 並根據測試分配partition
5.新建消費者類MessageConsumer, 多線程消費
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import kafka.consumer.ConsumerConfig;
import kafka.consumer.ConsumerIterator;
import kafka.consumer.KafkaStream;
import kafka.javaapi.consumer.ConsumerConnector;
import kafka.message.MessageAndMetadata;
/**
*
* <p>類描述: 消費者 </p>
* <p>創建人:wanghonggang </p>
* <p>創建時間:2019年9月26日 下午3:37:23 </p>
*/
public class MessageConsumer {
private ConsumerConfig config;
private ConsumerConnector connector;
private String topicName;
private ExecutorService threadPool;
/**
* 構造函數
* 創建一個新的實例 MessageConsumer.
*
* @throws Exception
*/
public MessageConsumer() throws Exception{
}
/**
*
* <p>方法描述:消息消費 TODO</p>
* <p>創建人: wanghonggang </p>
* <p>創建時間: 2019年9月26日 下午3:51:22 </p>
* <p>修改記錄:</p>
* @throws Exception
* void
*/
public void consumer() throws Exception{
Properties props = new Properties();
//group名稱
props.put("group.id", "group-test");
props.put("auto.commit.interval.ms", "1000");
props.put("zookeeper.connect", "192.168.1.101:2181,192.168.1.102:2181,192.168.1.103:2181");
props.put("rebalance.max.retries", "10");
props.put("rebalance.backoff.ms", "60000");
config = new ConsumerConfig(props);
//topic名稱
topicName = "topic-test";
connector = kafka.consumer.Consumer.createJavaConsumerConnector(config);
Map<String, Integer> topics = new HashMap<String, Integer>();
//分區數
topics.put(topicName, 3);
Map<String, List<KafkaStream<byte[], byte[]>>> streams = connector.createMessageStreams(topics);
List<KafkaStream<byte[], byte[]>> partitions = streams.get(topicName);
//線程數
threadPool = Executors.newFixedThreadPool(3);
//根據分區開啓線程
for (KafkaStream<byte[], byte[]> partition : partitions) {
threadPool.execute(new ConsumerRunner(partition));
}
}
/**
*
* <p>類描述:線程實現 </p>
* <p>創建人:wanghonggang </p>
* <p>創建時間:2019年9月26日 下午3:51:00 </p>
*/
class ConsumerRunner implements Runnable {
private KafkaStream<byte[], byte[]> partition;
ConsumerRunner(KafkaStream<byte[], byte[]> partition) {
this.partition = partition;
}
@SuppressWarnings("static-access")
public void run() {
ConsumerIterator<byte[], byte[]> it = partition.iterator();
while (it.hasNext()) {
try {
MessageAndMetadata<byte[], byte[]> item = it.next();
String message = new String(item.message());
//輸出
System.out.println("message:"+message);
} catch (Exception e) {
e.printStackTrace();;
}
}
}
}
/**
*
* <p>方法描述:關閉 TODO</p>
* <p>創建人: wanghonggang </p>
* <p>創建時間: 2019年9月26日 下午3:50:23 </p>
* <p>修改記錄:</p>
* void
*/
public void close() {
try {
connector.shutdown();
threadPool.shutdownNow();
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
*
* <p>方法描述: 測試 TODO</p>
* <p>創建人: wanghonggang </p>
* <p>創建時間: 2019年9月26日 下午3:42:28 </p>
* <p>修改記錄:</p>
* @param args
* void
*/
public static void main(String[] args) {
MessageConsumer m;
try {
m = new MessageConsumer();
m.consumer();
// m.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
運行測試方法, 可以看到消費的數據結果.