首先,我们需要配置好kafka的依赖及客户端必要参数(有关服务器的配置,我会在另一篇博客里介绍)。
1.加入kafka依赖
//kafka compile ('org.springframework.kafka:spring-kafka')
2.配置kafka的相关参数
/*kafka配置*/ @Configuration @EnableKafka public class KafkaConf { @Value("${kafka.bootstrap-servers}") private String bootstrapServers;//服务器的地址,例如,192.168.31.136:9092 /*消息生产者*/ @Bean public Map<String, Object> producerConfigs() { Map<String, Object> props = new HashMap<>(); props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); props.put(ProducerConfig.RETRIES_CONFIG, 0); props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384); props.put(ProducerConfig.LINGER_MS_CONFIG, 1); props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432); props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, LongSerializer.class);//这个是kafka自带的Long型数据序列化类 props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, DataSerializer.class);//自定义的序列化类 return props; } @Bean public ProducerFactory<Long, Message> producerFactory() { return new DefaultKafkaProducerFactory<>(producerConfigs()); } @Bean public KafkaTemplate<Long, Message> kafkaTemplate() { return new KafkaTemplate<>(producerFactory()); } /*消息的消费者,通常,生产者和消费者不在同一个项目中*/ @Bean public Map<String, Object> consumerConfigs() { Map<String, Object> props = new HashMap<>(); props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, LongDeserializer.class);//kafka自带的Long型数据反序列化类 props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);//kafka自带的byte数组反序列化类 props.put(ConsumerConfig.GROUP_ID_CONFIG, "test");//默认客户组 return props; } @Bean public ConsumerFactory<Long, byte[]> consumerFactory() { return new DefaultKafkaConsumerFactory<>(consumerConfigs()); } /*监听器配置*/ @Bean public ConcurrentKafkaListenerContainerFactory<Long, byte[]> kafkaListenerContainerFactory() { ConcurrentKafkaListenerContainerFactory<Long, byte[]> factory = new ConcurrentKafkaListenerContainerFactory<>(); factory.setConsumerFactory(consumerFactory()); return factory; } }
给kafka发送的数据必须是经过序列化的,自定义的序列化类需要实现kafka的Serializer接口,反序列化需要实现Deserializer接口,例如上面的DataSerializer类
public class DataSerializer<T extends Message> implements Serializer<T> { @Override public void configure(Map<String, ?> configs, boolean isKey) { //do nothing } @Override public byte[] serialize(final String topic, final T data) { return data.toByteArray(); } @Override public void close() { //do nothing } }
configure()和close()方法都可以使用默认的配置处理,这里只需要把serialize方法处理好就行
3.生成特定的bean
以上是项目基本配置,但是,光有以上配置是不够的,通常,我们希望能直接交给生产者一个我们自定义的javabean,由它来处理具体的toByte过程,这时,就需要使用特定的bean。
例如,我们有一个Test类,包含两个参数:id和text
public class Test { private long id; private String text; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getText() { return text; } public void setText(String text) { this.text = text; } }
但是,这个类kafka是无法直接使用的,即便是使用了之前的自定义序列化方法。于是,我们引入一个插件protobuf-gradle-plugin,这个插件可以将.proto文件转化为kafka能够使用的类,这个类的调用方法又和我们熟悉的javabean相似,这正是我们所需要的。
配置脚本依赖包
classpath('com.google.protobuf:protobuf-gradle-plugin:0.8.1')
这个配置加在buildscript的dependencies中,这是给IDE使用的插件
之后,在build.gradle中加入
//protobuf code generation sourceSets { main { proto { srcDir 'src/main/resources/proto'//.proto文件所在目录 include 'test.proto'//我们定义的自定义类 } } } protobuf { generateProtoTasks { all().each { task -> task.builtins { java {} } } } generatedFilesBaseDir = "$projectDir/src"//项目主目录 }
之后我们就能在脚本任务中找到generateProto任务了(通常在Tasks的other中),执行这个任务,就能用我们配置好的test.proto生成kafka所需的类了。
test.proto配置如下
syntax = "proto3"; option java_package = "com.test.study.aboutkafka.model";//类输出位置 option java_outer_classname = "Test";//输出的类的名字 message test { int64 id = 1;//1表示第一个属性,int64对应java的Long string text = 2;//第二个属性 }
输出的类为这种格式
public final class Test { private Test() {} public static void registerAllExtensions( com.google.protobuf.ExtensionRegistryLite registry) { } public static void registerAllExtensions( com.google.protobuf.ExtensionRegistry registry) { registerAllExtensions( (com.google.protobuf.ExtensionRegistryLite) registry); } public interface testOrBuilder extends // @@protoc_insertion_point(interface_extends:test) com.google.protobuf.MessageOrBuilder {
很长,就不全贴了,两个属性在这里
private long id_ ; /** * <code>int64 id = 1;</code> */ public long getId() { return id_; } /** * <code>int64 id = 1;</code> */ public Builder setId(long value) { id_ = value; onChanged(); return this; }
private java.lang.Object text_ = ""; /** * <code>string text = 2;</code> */ public java.lang.String getText() { java.lang.Object ref = text_; if (!(ref instanceof java.lang.String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; java.lang.String s = bs.toStringUtf8(); text_ = s; return s; } else { return (java.lang.String) ref; } }
/** * <code>string text = 2;</code> */ public Builder setText( java.lang.String value) { if (value == null) { throw new NullPointerException(); } text_ = value; onChanged(); return this; }
对于string类型参数text,这里有个非空校验,如果不想要可以删掉。
4.使用kafka
先是生产者
@Component public class TestProducer { @Autowired public TestProducer(KafkaTemplate<Long, Message> kafkaTemplate) { this.kafkaTemplate = kafkaTemplate; } public void testsend() { Test.test.Builder builder = Test.test.newBuilder(); builder.setId(1); builder.setText("hey man"); Test.test msg = builder.build(); kafkaTemplate.send("test1" ,msg ); System.out.println("send"); } private final KafkaTemplate<Long, Message> kafkaTemplate; }
首先注入我们配置好的kafkaTemplate实例,接着构造bean,完成后发送
public ListenableFuture<SendResult<K, V>> send(String topic, V data) { ProducerRecord<K, V> producerRecord = new ProducerRecord<>(topic, data); return doSend(producerRecord); }
send有两个参数,第一个是topic主题,只有订阅这个主题的消费者才能消费这条消息,第二个是data,消息主体。
接下来是消费者
@Component public class TestConsumer { @KafkaListener( topics = "test1" , group = "chicken") public void listen1(byte[] buf) throws InvalidProtocolBufferException { Test.test msg = Test.test.parseFrom(buf); System.out.println("msg is = " + msg); } }
消费者需要配置自己的消费主题topics,和自己所在用户组,组如果不配,则会使用我们默认的test组。一个主题下的一条消息,在同一个用户组中,只会被消费一次。
比如,生产者往test1发送一条“hey man”,现在有10个消费者,其中6个所在组是chicken,另外4个所在组是dog,那么chicken和dog组中分别只有一个用户能监听到并消费这条消息。
以上就是kafka的简单用例。