记录一次配置过程
安装docker
参加下面链接的第一部分
Ubuntu20.04使用docker安装kafka服务-CSDN博客
安装zookeeper
docker run -d --name zookeeper -p 2181:2181 -v /etc/localtime:/etc/localtime wurstmeister/zookeeper
安装kafka服务
docker run -d --name kafka -p 9092:9092 -e KAFKA_BROKER_ID=0 -e KAFKA_ZOOKEEPER_CONNECT=[你的IP地址]:2181 -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://[你的IP地址]:9092 -e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 -t wurstmeister/kafka
改成我自己的本机的就是:
docker run -d --name kafka -p 9092:9092 -e KAFKA_BROKER_ID=0 -e KAFKA_ZOOKEEPER_CONNECT=192.168.147.131:2181 -e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.147.131:9092 -e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 -t wurstmeister/kafka
进入docker
执行docker ps -a命令,查看结果如下:
注意上述结果显示kafka和zookeeper都是已关闭状态,需要开启,使用下面的命令:docker start 容器id,即
docker start a01c
docker start c0c7
注意要先启动父容器(或者说是依赖项zookeeper),再启动子容器kafka,容器id输入前几位有区分即可,不必全部输入,也不必补全(tab不会给你补全)
执行docker ps -a命令,再次查看结果如下:
ok这就启动成功了
然后执行命令进入docker
docker exec -it [container-id] /bin/sh #container-id就是上图中c0c7这一串数字,执行命令时只需要输入前几个数字/字母即可
cd /opt/kafka/bin #跳转到kafka目录下的bin目录,这下面有一些可执行脚本
换成我的本机就是执行下面的命令:
docker exec -it c0c7 /bin/sh #container-id就是上图中41b8e4db352f这一串数字,执行命令时只需要输入前几个数字/字母即可
cd /opt/kafka/bin #跳转到kafka目录下的bin目录,这下面有一些可执行脚本
得到下面的结果:
生产者消费者模式初体验
启动生产者
注意下面的ip:192.168.147.131要和你本机ip一致,使用ifconfig命令查看
./kafka-console-producer.sh --broker-list 192.168.147.131:9092 --topic wxj
打开另一个终端,启动消费者
注意ip要一致,topic要一致
docker exec -it c0c7 /bin/sh
cd /opt/kafka/bin
./kafka-console-consumer.sh --bootstrap-server 192.168.147.131:9092 --topic wxj
运行效果如下:
其他一点别的,就是常识啦,比如如何退出生产者消费者的环境,ctrl+c,ctrl+d都试试,如何退出容器,可以试试输入exit命令
ok 完事了,还是去探索腾讯云的kafka服务吧
常用命令行命令
首先进入kafka容器的环境中
docker exec -it c0c7 /bin/sh
cd /opt/kafka/bin
kafka-topics.sh
对主题topic进行增删改查的工具
常用选项如下:
--bootstrap-server:kafka服务器ip:port,需要查询指定的topic或者topic列表,必须指定这个量
--create:创建主题
--delete:删除主题
--describe:描述主题
--list:查看主题列表
--alter:修改主题的 partitions等
--topic <String: topic>:主题名
--topic-id <String: topic-id>:主题id
--partitions <Integer: # of partitions>:主题的partition
新增
命令
kafka-topics.sh --bootstrap-server=127.0.0.1:9092 --create --topic wxj2
效果
查看topic列表
命令
kafka-topics.sh --bootstrap-server=127.0.0.1:9092 --list
效果
查看topic详情(描述)
命令
kafka-topics.sh --bootstrap-server=127.0.0.1:9092 --describe --topic wxj2
效果
修改
命令
以修改主题partiion数量为例
kafka-topics.sh --bootstrap-server=127.0.0.1:9092 --alter --topic wxj2 --partitions 3
再次查看topic的描述发现分区完成
删除
命令
kafka-topics.sh --bootstrap-server=127.0.0.1:9092 --delete --topic wxj2
kafka-console-producer.sh
标准输入读数据,发送到Kafka的工具
常用选项如下:
--bootstrap-server:kafka服务器ip:port,必须的
--topic <String: topic> :Kafka主题,必须的
--sync:同步发送
--compression-codec [String: compression-codec] :压缩方式,‘none’,‘gzip’, ‘snappy’, ‘lz4’, , ‘zstd’,默认gzip.
使用示例
kafka-topics.sh --bootstrap-server=127.0.0.1:9092 --create --topic demo
kafka-console-producer.sh --bootstrap-server=127.0.0.1:9092 --topic demo
kafka-console-consumer.sh
常用选项如下:
--bootstrap-server:kafka服务器ip:port,必须的
--topic <String: topic> :Kafka主题,必须的
--group <String: consumer group id>:消费者组id
--key-deserializer <String: deserializer for key>:key反序列化,默认是org.apache.kafka.common.serialization.StringDeserializer
--value-deserializer <String: deserializer for values>:value反序列化,默认是org.apache.kafka.common.serialization.StringDeserializer
--offset <String: consume offset>:消费的offset
--partition <Integer: partition>:消费的分区
使用示例
kafka-console-consumer.sh --bootstrap-server=127.0.0.1:9092 --topic demo
kafka-console-consumer.sh --bootstrap-server=127.0.0.1:9092 --topic demo --partition 0 --offset 2
上述命令的使用方法是进入到了docker容器kafka中了,要是不想进入环境直接运行命令的示例是
docker exec -it c0c7 /opt/kafka/bin/kafka-topics.sh --bootstrap-server=127.0.0.1:9092 --create --topic demo
效果:
Kafka+IDEA+Maven
具体详细情况参见我自己写的kafka进阶博客
此处连接的是上述虚拟机中安装的kafka,故使用前需要开启服务
生产者
普通消费者
package com.tjut.kafka.producer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class CustomProducer {
public static void main(String[] args) {
//创建kafka生产者对象
//"" "hello"
//属性配置
Properties properties = new Properties();
//连接集群
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.147.131:9092");
//指定对应的key和value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
//发送数据
for (int i=0;i<5;i++) {
kafkaProducer.send(new ProducerRecord<>("first", "wxj"+i));
}
//关闭资源
kafkaProducer.close();
}
}
带有返回值的生产者(CallBack)
package com.tjut.kafka.producer;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class CustomProducerCallBack {
public static void main(String[] args) {
//创建kafka生产者对象
//"" "hello"
//属性配置
Properties properties = new Properties();
//连接集群
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.147.131:9092");
//指定对应的key和value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
//发送数据
for (int i=10;i<15;i++) {
kafkaProducer.send(new ProducerRecord<>("first", "wxj" + i), new Callback() {
@Override
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
if(e==null){
System.out.println("主题:"+recordMetadata.topic());
System.out.println("分区:"+recordMetadata.partition());
}
}
});
}
//关闭资源
kafkaProducer.close();
}
}
同步方式(多了get)
package com.tjut.kafka.producer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
public class CustomProducerSync {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建kafka生产者对象
//"" "hello"
//属性配置
Properties properties = new Properties();
//连接集群
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.147.131:9092");
//指定对应的key和value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
//发送数据
for (int i=0;i<5;i++) {
//多了get
kafkaProducer.send(new ProducerRecord<>("first", "wxj"+i)).get();
}
//关闭资源
kafkaProducer.close();
}
}
使用事务的方式
package com.tjut.kafka.producer;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class CustomProducerTransaction {
public static void main(String[] args) {
//创建kafka生产者对象
//"" "hello"
//属性配置
Properties properties = new Properties();
//连接集群
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.147.131:9092");
//指定对应的key和value的序列化类型
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
//设置事务id
properties.put(ProducerConfig.TRANSACTIONAL_ID_CONFIG,"transaction_id_0");
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
// 初始化事务
kafkaProducer.initTransactions();
// 开启事务
kafkaProducer.beginTransaction();
try {
//发送数据
for (int i=0;i<5;i++) {
kafkaProducer.send(new ProducerRecord<>("first", "wxj"+i));
}
// int i = 1 / 0;
//提交事务
kafkaProducer.commitTransaction();
}catch (Exception e){
System.out.println("事务终止!!!");
//终止事务
kafkaProducer.abortTransaction();
}finally {
//关闭资源
kafkaProducer.close();
}
}
}
消费者
package com.tjut.kafka.consumer;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Properties;
public class CustomerConsumer {
public static void main(String[] args) {
//创建kafka生产者对象
//"" "hello"
//属性配置
Properties properties = new Properties();
//连接集群,必须
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.147.131:9092");
//指定对应的key和value的序列化类型,必须
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
// 配置消费者组(组名任意起名) 必须
properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);
// 消费某个主题的某个分区数据,assign方式
ArrayList<TopicPartition> topicPartitions = new ArrayList<TopicPartition>();
topicPartitions.add(new TopicPartition("first", 0));
kafkaConsumer.assign(topicPartitions);
// 注册要消费的主题(可以消费多个主题),subscribe方式
// ArrayList<String> topics = new ArrayList<String>();
// topics.add("first");
// // 消息订阅
// kafkaConsumer.subscribe(topics);
// 拉取数据打印
while (true) {
// 设置 1s 中消费一批数据
ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));
// 打印消费到的数据
for (ConsumerRecord<String, String> consumerRecord :consumerRecords) {
System.out.println(consumerRecord);
}
}
// //关闭资源
// kafkaConsumer.close();
}
}
注意,集合订阅的方式subscribe和指定分区的订阅方式assign分表代表了两种不同的订阅状态。然而这两种状态是互斥的, 在一个消费者中只能使用其中的一种,否则会报出IllegalStateException 异常:
java.lang.IllegalStateException : Subscription to topics , partitions and patter are mutually exclusive.
参考解读:Kafka-消费者订阅主题和取消订阅_consumer is not subscribed to any topics or assign-CSDN博客