1、生产者Producer
1.1 发送消息Demo
位置:java\kafka\examples\Producer.java
- 生产者往Kafka发送消息:
步骤:
1、初始化生产者类,配置Brokers源
2、准备消息并不断开始发送消息
3、利用异步或同步发送消息,若是异步,重写回调函数,处理消息返回与发送状态;若是同步,则发送完后等待消息返回值,可能会产生阻塞
4、由于生产者是一个不断发送消息机制,不需要关闭
//消息生产者
public class Producer extends Thread {
private final KafkaProducer<Integer, String> producer;
private final String topic;
private final Boolean isAsync;
//利用构造方法初始化各种参数
public Producer(String topic, Boolean isAsync) {
Properties props = new Properties();
//配置Broker数据,方便用户拉取Kafka元数据
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KafkaProperties.KAFKA_SERVER_URL + ":" + KafkaProperties.KAFKA_SERVER_PORT);
//客户端ID,没有时会自动生成一个客户ID
props.put(ProducerConfig.CLIENT_ID_CONFIG, "DemoProducer");
//设置消息k/v传输时序列化的类 消费者消费抓取数据时再进行反序列化
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, IntegerSerializer.class.getName());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//构建生产者
producer = new KafkaProducer<>(props);
this.topic = topic;
//Kafka发送数据的方式:isAsync为true则异步发送,为false则同步进行发送
this.isAsync = isAsync;
}
public void run() {
int messageNo = 1;
//一启动就会一直往kafka发送数据
while (true) {
//模拟生成消息,不断生成并进行发送
String messageStr = "Message_" + messageNo;
long startTime = System.currentTimeMillis();
if (isAsync) { // Send asynchronously
//异步发送:消息发送完成后直接进行下一条消息发送,不需要等待,
// 而是等服务调用DemoCallBack回调函数获取消息发送的返回信息,以判断消息是否发送成功而执行相应的操作
/**
* ProducerRecord:一条消息结构
* topic:消息的主题 messageNo:消息key messageStr:消息本身value
*/
producer.send(new ProducerRecord<>(topic,
messageNo,
messageStr), new DemoCallBack(startTime, messageNo, messageStr));
} else { // Send synchronously
//同步发送:消息发送完后,需要等get返回才能进行下面的下一条消息,可能会出现阻塞
try {
producer.send(new ProducerRecord<>(topic,
messageNo,
messageStr)).get();
System.out.println("Sent message: (" + messageNo + ", " + messageStr + ")");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
++messageNo;
}
}
}
//异步时的回调函数
class DemoCallBack implements Callback {
private final long startTime;
private final int key;
private final String message;
public DemoCallBack(long startTime, int key, String message) {
this.startTime = startTime;
this.key = key;
this.message = message;
}
/**
* A callback method the user can implement to provide asynchronous handling of request completion. This method will
* be called when the record sent to the server has been acknowledged. Exactly one of the arguments will be
* non-null.
*
* @param metadata The metadata for the record that was sent (i.e. the partition and offset). Null if an error
* occurred.
* @param exception The exception thrown during processing of this record. Null if no error occurred.
*/
//重写onCompletion方法,metadata:服务端元数据信息,
public void onCompletion(RecordMetadata metadata, Exception exception) {
long elapsedTime = System.currentTimeMillis() - startTime;
if (metadata != null) {
System.out.println(
"message(" + key + ", " + message + ") sent to partition(" + metadata.partition() +
"), " +
"offset(" + metadata.offset() + ") in " + elapsedTime + " ms");
} else {
exception.printStackTrace();
}
}
}
- 消息类ProducerRecord
//消息类:每一条消息都会以主题为单位进行归类,Kafka根据消息的key按主题不同而分配不同的分区,相同的Key会被划分到同一个分区之中
public class ProducerRecord<K, V> {
//主题
private final String topic;
//分区号
private final Integer partition;
//消息头
private final Headers headers;
//消息key:用来指定消息的键,可以计算出分区号进而可以将消息发往特定的分区中,
private final K key;
//消息的值,一般不为空,若为空则为墓碑消息
private final V value;
//消息时间戳
private final Long timestamp;
/**
* Creates a record with a specified timestamp to be sent to a specified topic and partition
*
* @param topic The topic the record will be appended to
* @param partition The partition to which the record should be sent
* @param timestamp The timestamp of the record, in milliseconds since epoch. If null, the producer will assign
* the timestamp using System.currentTimeMillis().
* @param key The key that will be included in the record
* @param value The record contents
* @param headers the headers that will be included in the record
*/
public ProducerRecord(String topic, Integer partition, Long timestamp, K key, V value, Iterable<Header> headers) {
if (topic == null)
throw new IllegalArgumentException("Topic cannot be null.");
if (timestamp != null && timestamp < 0)
throw new IllegalArgumentException(
String.format("Invalid timestamp: %d. Timestamp should always be non-negative or null.", timestamp));
if (partition != null && partition < 0)
throw new IllegalArgumentException(
String.format("Invalid partition: %d. Partition number should always be non-negative or null.", partition));
this.topic = topic;
this.partition = partition;
this.key = key;
this.value = value;
this.timestamp = timestamp;
this.headers = new RecordHeaders(headers);
}
/**
* Creates a record with a specified timestamp to be sent to a specified topic and partition
*
* @param topic The topic the record will be appended to
* @param partition The partition to which the record should be sent
* @param timestamp The timestamp of the record, in milliseconds since epoch. If null, the producer will assign the
* timestamp using System.currentTimeMillis().
* @param key The key that will be included in the record
* @param value The record contents
*/
public ProducerRecord(String topic, Integer partition, Long timestamp, K key, V value)