前言
继上篇博客使用最简单的字符串序列发送消息时完全不能满足消息的类型的。所以,这里使用Apache Avro序列化消息的键和值。
如此一来,值的类型不只是字符串了,可以是一个实例化的对象。
环境:
Kafka-2.1.1 + Kafka 集群 + Eclipse
1. 配置Confluent
因为,Avro通过schema 来定义。schema通过JSON来描述。所以我们需要配置schema。
而schema 的配置通过向注册表来注册。
注册表的作用可以这样理解:
生产者通过注册表获得schema,通过schema来序列化数据。
消费者通过注册表获得schema,通过schema来反序列化数据。
注册表这里选择开源的Confluent,下载链接(感谢Q群里的大佬的帮忙下载):
链接:https://pan.baidu.com/s/1M3vY6Z2R_vgIUupiTq3-7g
提取码:2ay5
将下载的安装包上传至Linux:
- 解压
tar -zxvf confluent-oss-4.1.1-2.11.tar.gz -C ~/
- 修改配置
vim confluent-4.1.1/etc/schema-registry/schema-registry.properties
将黄框中的主机名改为自己的主机名即可:
3. 启动
hadoop@master:~/confluent-4.1.1$ bin/schema-registry-start \
./etc/schema-registry/schema-registry.properties
部分截图:
4. 注册schema
注册的schema 是预先根据应用程序数据设置好的(如果不同,应该修改schema)。
下面的代码时Java 程序中的schema,在注册表中的schema,应在此基础上加上
“schema”: “schemaStr的内容(具体见注册命令)”
String schemaStr = "{\"namespace\": \"customerManagement.avro\", \"type\": \"record\", " +
"\"name\": \"Customer\", " +
"\"fields\": [{\"name\": \"id\", \"type\": \"int\"}, " +
"{\"name\": \"name\", \"type\": \"string\"}, " +
"{\"name\": \"email\", \"type\": [\"null\", \"string\"], \"default\": \"null\"}]}";
注册命令(注意"schema": “上面的内容”):
curl -X POST -H "Content-Type: application/vnd.schemaregistry.v1+json" \
--data \
'{"schema": "{\"namespace\": \"customerManagement.avro\", \"type\": \"record\", \"name\": \"Customer\", \"fields\": [{\"name\": \"id\", \"type\": \"int\"}, {\"name\": \"name\", \"type\": \"string\"}, {\"name\": \"email\", \"type\": [\"null\", \"string\"], \"default\": \"null\"}]}"}' \
http://master:8081/subjects/customerAvro-value/versions
注:…/customerAvro-value/versions中的customerAvro是主题的名称,应该与Java程序中写入的主题相同。如果你要使用不同的主题,请做相应的修改。
执行后会返回一个schema 的id(从1开始):
2. 创建Java 工程
博主当前使用的是Java 工程,创建类 SendMessageAvro.java。
下载依赖:
链接:https://pan.baidu.com/s/1i_S1trdOGcNNZpdfPAPz1w
提取码:v705
-
将包里的依赖全部添加到工程:
-
SendMessageAvro.java :
package producer_write;
import java.util.Properties;
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericData;
import org.apache.avro.generic.GenericRecord;
import org.apache.kafka.clients.producer.Callback;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
public class SendMessageAvro {
public static void main(String[] args) {
// 创建生产者
Properties kafkaProps = new Properties();
// 指定broker
kafkaProps.put("bootstrap.servers", "slave1:9092,slave2:9092");
// 设置序列化,使用Avro序列化
kafkaProps.put("key.serializer", "io.confluent.kafka.serializers.KafkaAvroSerializer");
kafkaProps.put("value.serializer", "io.confluent.kafka.serializers.KafkaAvroSerializer");
kafkaProps.put("schema.registry.url", "http://master:8081");
// 因为没有使用Avro生成对象,所以需要提供Avro Schema
String schemaStr = "{\"namespace\": \"customerManagement.avro\", \"type\": \"record\", " +
"\"name\": \"Customer\", " +
"\"fields\": [{\"name\": \"id\", \"type\": \"int\"}, " +
"{\"name\": \"name\", \"type\": \"string\"}, " +
"{\"name\": \"email\", \"type\": [\"null\", \"string\"], \"default\": \"null\"}]}";
@SuppressWarnings("resource")
Producer<String, GenericRecord> producer = new KafkaProducer<String, GenericRecord>(kafkaProps);
Schema.Parser parser = new Schema.Parser();
Schema schema = parser.parse(schemaStr);
// 实例化SendMessageAvro
SendMessageAvro sMCallback = new SendMessageAvro();
// 发送多条消息
for (int nCustomers = 0; nCustomers < 20; nCustomers++) {
String name = "exampleCustomer" + nCustomers;
String email = "example" + nCustomers + "@example.com";
// ProducerRecord的值就是一个GenericRecord对象,它包含了shcema和数据
GenericRecord customer = new GenericData.Record(schema);
customer.put("id", nCustomers);
customer.put("name", name);
customer.put("email", email);
// 创建ProducerRecord对象
ProducerRecord<String, GenericRecord> data = new ProducerRecord<String,
GenericRecord>("customerAvro", name, customer);
try {
// 发送消息,并使用回调函数
producer.send(data, sMCallback.new DemoProducerCallback(nCustomers));
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private class DemoProducerCallback implements Callback {
Integer id = null;
public DemoProducerCallback(Integer id) {
this.id = id;
}
@Override
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
// TODO Auto-generated method stub
if (e != null) {
// 如果消息发送失败,打印异常
e.printStackTrace();
} else {
System.out.println("Success send! Message ID: " + id);
}
}
}
}
运行结果:
查看主题的消息:
参考:https://www.jianshu.com/p/cd6f413d35b0