首先,我们需要配置好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的简单用例。