首先就是去阿里云仓库apache-kafka安装包下载_开源镜像站-阿里云 (aliyun.com)下载Kafka
然后将压缩包放到linux服务器上:推荐Irzsz插件上传,直接rz;
使用 tar -zxcvf 解压打包文件到/usr/local目录下,并重命名为kafka;
mv 你的kafka解压文件 /usr/local/kafka
接下来全局直接安装jdk 11(因为kafka和zookeeper基于java环境启动的);
sudo apt install openjdk-11-jdk -y
之后就是添加服务注册和管理xinetd(主要是为了方便启动和关闭,为后续测试和修改做准备)
编辑zookeeper.service和kafka.service两个文件如下:
sudo vim /etc/systemd/system/zookeeper.service
[Unit]
Description=Apache Zookeeper server
Documentation=http://zookeeper.apache.org
Requires=network.target remote-fs.target
After=network.target remote-fs.target
[Service]
Type=simple
ExecStart=/usr/local/kafka/bin/zookeeper-server-start.sh /usr/local/kafka/config/zookeeper.properties
ExecStop=/usr/local/kafka/bin/zookeeper-server-stop.sh
Restart=on-abnormal
[Install]
WantedBy=multi-user.target
sudo vim /etc/systemd/system/kafka.service
[Unit]
Description=Apache Kafka Server
Documentation=http://kafka.apache.org/documentation.html
Requires=zookeeper.service
[Service]
Type=simple
Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64"
ExecStart=/usr/local/kafka/bin/kafka-server-start.sh /usr/local/kafka/config/server.properties
ExecStop=/usr/local/kafka/bin/kafka-server-stop.sh
[Install]
WantedBy=multi-user.target
这两个文件编辑完之后执行以下命令
sudo systemctl daemon-reload
sudo systemctl start zookeeper
sudo systemctl start kafka
#注意的是一定要先等zookeeper先启动成功再启动kafka,restart重启也是如此;因为zookeeper提供#注册服务
sudo systemctl status zookeeper
sudo systemctl status kafka
#启动成功为active(running)
#或者直接用netstat -ntlp查看是否存在端口2181和9092
接着创建topic,首先进入/usr/local/kafka目录(记得前面要重命名解压文件)执行以下脚本创建
bin/kafka-topics.sh --create --bootstrap-server 公网ip(localhost估计也行):9092 --replication-factor 1 --partitions 1 --topic sampleTopic
查看topic命令,看是否存在sampleTopic
bin/kafka-topics.sh --list --bootstrap-server localhost:9092
接下来就是测试是否能产生消息和消费消息了
#注意,这里的公网ip是你服务器的ip,括号里面的localhost是本地地址,两者是二选一关系。
#产生消息,输入命令后会让你输入消息,Ctr+c退出;
bin/kafka-console-producer.sh --broker-list 公网ip(localhost):9092 --topic sampleTopic
#从头开始消费消息
bin/kafka-console-consumer.sh --bootstrap-server 公网ip(localhost):9092 --topic sampleTopic --from-beginning
如果上面能实现产生和消费消息的话,那么说明本地kafka运行是没有问题的。
接下来就是跟你的windows/mac项目进行一个远程连接生产和消费了。
首先是打开安全组端口2181和9092,防火墙也可以放开(不知道有没有影响,我是开了的)
sudo firewall-cmd --zone=public --add-port=2181/tcp --permanent
sudo firewall-cmd --zone=public --add-port=9092/tcp --permanent
#永久的可以查看其他教程
然后修改配置文件(就在bin目录下执行该命令):
vim config/server.properties
接下来就是照着修改就行,你可以直接加上或者去取消#注释更改也行
# 允许外部端口连接
listeners=PLAINTEXT://0.0.0.0:9092
# 外部代理地址
advertised.listeners=PLAINTEXT://公网ip:9092
使用命令重启zookeeper和kafka
sudo systemctl restart zookeeper
#间隔一下,大概30秒左右,等zookeeper启动成功
sudo systemctl restart kafka
现在就可以去编写springboot项目了
先编写yml文件,添加kafka配置:
kafka:
bootstrap-servers: 公网ip:9092
producer:
# 发生错误后,消息重发的次数。
retries: 1
#当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。
batch-size: 16384
# 设置生产者内存缓冲区的大小。
buffer-memory: 33554432
# 键的序列化方式
key-serializer: org.apache.kafka.common.serialization.StringSerializer
# 值的序列化方式
value-serializer: org.apache.kafka.common.serialization.StringSerializer
# acks=0 : 生产者在成功写入消息之前不会等待任何来自服务器的响应。
# acks=1 : 只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应。
# acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。
acks: 1
client-id: 200
consumer:
# 自动提交的时间间隔 在spring boot 2.X 版本中这里采用的是值的类型为Duration 需要符合特定的格式,如1S,1M,2H,5D
auto-commit-interval: 1S
# 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理:
# latest(默认值)在偏移量无效的情况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录)
# earliest :在偏移量无效的情况下,消费者将从起始位置读取分区的记录
auto-offset-reset: earliest
# 是否自动提交偏移量,默认值是true,为了避免出现重复数据和数据丢失,可以把它设置为false,然后手动提交偏移量
enable-auto-commit: false
# 键的反序列化方式
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
# 值的反序列化方式
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
client-id: 200
listener:
# 在侦听器容器中运行的线程数。
concurrency: 5
#listner负责ack,每调用一次,就立即commit
ack-mode: manual_immediate
missing-topics-fatal: false
然后就是引入Kafka插件(pom.xml):
<!-- kafka插件 -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>3.1.0</version>
</dependency>
接着就是编写例子了(网上有很多样例,可以借鉴一下)
package com.example.learn.util;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.kafka.support.SendResult;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
public class KafkaUtil {
private static final Logger logger = LoggerFactory.getLogger(KafkaUtil.class);
@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;
public void sendMessage(String topic, Object o) {
CompletableFuture<SendResult<String, Object>> future = kafkaTemplate.send(topic, o);
future.thenAccept(result -> logger.info("生产者成功发送消息到topic:{} partition:{}的消息", result.getRecordMetadata().topic(), result.getRecordMetadata().partition()))
.exceptionally(ex -> {
logger.error("生产者发送消失败,原因:{}", ex.getMessage());
return null;
});
}
public void sendMessage(String topic, String key, Object o) {
CompletableFuture<SendResult<String, Object>> future = kafkaTemplate.send(topic, key, o);
future.thenAccept(result -> logger.info("生产者成功发送消息到topic:{} partition:{}的消息", result.getRecordMetadata().topic(), result.getRecordMetadata().partition()))
.exceptionally(ex -> {
logger.error("生产者发送消失败,原因:{}", ex.getMessage());
return null;
});
}
@KafkaListener(topics = "${kafka.topic.my-topic}", groupId = "${kafka.consumer.group-id}")
public void consumeMessage(ConsumerRecord<?, ?> record) {
Optional message = Optional.ofNullable(record.value());
if (message.isPresent()) {
Object msg = message.get();
logger.info("Message:" + msg);
}}
}
消费者暂时没测试,因为传入的ConsumerRecord还没弄懂,有空再弄(本来弄Kafka就是为了加深八股文印象,没想到弄了一天,太亏了,被某guide害的)
ConsumerRecord<?, ?> record =new ConsumerRecord<>("sampleTopic", 0, 0, "key", "value");
然后你随便编写几个接口在apifox里面测试一下就行,如下所示(生成消息是没问题的,消费消息在刚连接时候显示也是没问题的):