选择自定义安装模式
选择带有GUI的server
修改ifcfg-ens33配置文件
service network restart
一般自带
yum --version // 查看有无安装
rpm -qa | grep java
(openjdk不全,需要删除)
su yum romove jdk*
oracle下载jdk8
mkdir /usr/local/java
mv jdk_path /usr/local/java
tar -zxvf jdk8.tar.gz
source /etc/ profile
安装jdk8后,先拍摄快照
基于快照拍摄3台虚拟机
kafkaServer1 kafkaServer2 kafkaServer3
修改ifcfg-ens33配置文件
systemctl status firewalld (有的说zookeeper集群要关闭防火墙)
当我执行这个命令时, 此时显示activate
下载的kafka版本是2.4.1, 里面zookeeper是3.5.7
tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz
mv apache-zookeeper-3.5.7-bin zookeeper3.5.7
echo "1" > ./zkData/myid
echo "2" > ./zkData/myid
echo "3" > ./zkData/myid
在搭建之前, 一个思考题:需要关闭防火墙吗?
1.创建logs, data文件夹
2.myid(添加myid文件,myid文件要在dataDir下)
3.path (添加到系统路径, 执行脚本时可以不在bin目录下)
4.zoo.cfg(修改zoo.cfg配置文件 重要)
# scp zoo.cfg [email protected]:/usr/local/zookeeper/zookeeper3.5.7/conf(涉及到公钥密钥) 可以不需要
zkServer.sh start
zkServer.sh status
Client port found: 2181. Client address: localhost.
Error contacting service. It is probably not running.
报了这个错误,解决方式是关闭防火墙或者给防火墙添加入站规则
改过之后,这个错误解决,出现leader, follower
[root@localhost zookeeper3.5.7]# zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/zookeeper3.5.7/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader
systemctl status firewalld
systemctl stop firewalld
systemctl start firewalld
systemctl disable firewalld
systemctl enable firewalld
zkServer.sh stop
zkServer.sh status
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zookeeper/zookeeper3.5.7/zkData
dataLogDir=/usr/local/zookeeper/zookeeper3.5.7/zkLogs
clientPort=2181
server.1=192.168.129.11:2888:3888
server.2=192.168.129.12:2888:3888
server.3=192.168.129.13:2888:3888
这个地方需要3台服务器中任意两台能够ssh随意登录
ssh-keygen -t rsa
# 此时在/root/.ssh下生成私钥和公钥
# 这个命令执行会遇到:输入, 直接enter,不需要密码锁
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 22 [email protected]
# 将公钥拷贝到目标服务器上的authorized_keys文件下
#!/bin/bash
node1=192.168.129.11
node2=192.168.129.12
node3=192.168.129.13
case $1 in
"start"){
for i in $node1 $node2 $node3
do
echo -------------------------------- $i zookeeper 启动 ---------------------------
ssh $i "/usr/local/zookeeper/zookeeper3.5.7/bin/zkServer.sh start"
done
}
;;
"stop"){
for i in $node1 $node2 $node3
do
echo -------------------------------- $i zookeeper 停止 ---------------------------
ssh $i "/usr/local/zookeeper/zookeeper3.5.7/bin/zkServer.sh stop"
done
}
;;
"status"){
for i in $node1 $node2 $node3
do
echo -------------------------------- $i zookeeper 状态 ---------------------------
ssh $i "/usr/local/zookeeper/zookeeper3.5.7/bin/zkServer.sh status"
done
}
;;
esac
# 无需担心退出登录问题, ssh接命令,是一种伪终端登录,命令执行完后就会退出
执行脚本命令时发现
bash myZk.sh status
-------------------------------- 192.168.129.11 zookeeper 状态 ---------------------------
Error: JAVA_HOME is not set and java could not be found in PATH.
解决措施:
在zookeeper目录下的bin目录下,有一个zkEnv.sh文件,在文件代码部分的 前面 加上自己的JAVA_HOME路径即可。
export JAVA_HOME=/usr/local/java/jdk1.8.0_391
ZOOBINDIR="${ZOOBINDIR:-/usr/bin}"
ZOOKEEPER_PREFIX="${ZOOBINDIR}/.."
下载kafka2.4.1
1 tar -xzvf kafka-2.4.1.tar.gz
2 mkdir ./kafka-2.4.1/kafka-logs
cd ./config
3 修改server.properties, 其他节点也要修改
修改内容如下
broker.id=0
listeners=PLAINTEXT://192.168.129.11:9092
log.dirs=/usr/local/kafka/kafka_2.12-2.4.1/kafka-logs
zookeeper.connect=192.168.129.11:2181,192.168.129.12:2181,192.168.129.13:2181
4 编写kafka启动和停止脚本
5 bash myKafka.sh start
报错
kafka集群启动 nohup: failed to run command ‘java’: No such file or directory
解决方法 启动脚本里加入 source /etc/profile
6 测试
# 查看主题列表
bash ./bin/kafka-topics.sh --list --bootstrap-server 192.168.129.12:9092
bash ./bin/kafka-topics.sh --list --zookeeper 192.168.129.11:2181
# 创建主题
bash ./bin/kafka-topics.sh --create --bootstrap-server 192.168.129.12:9092 --replication-factor 3 --partitions 1 --topic test
bash ./bin/kafka-topics.sh --create --bootstrap-server 192.168.129.13:9092 --replication-factor 3 --partitions 3 --topic shopping
bin/kafka-topics.sh --create --zookeeper 192.168.129.11:2181,192.168.129.12:2181,192.168.129.13:2181 --replication-factor 3 --partitions 3 --topic test
执行这个命令,客户端只会和其中一个zookeeper服务器建立连接。写多个目的是保证高可用和负载均衡
带有--zookeeper的写法是老版,带有--bootstap-server是新版。 推荐新版
这里zookeeper服务器一定是要leader? 答:不需要。读命令直接读,写命令转交给leader
# 再次 查看主题列表
bash ./bin/kafka-topics.sh --list --bootstrap-server 192.168.129.12:9092
# 向主题中生产消息,程序不会自动退出
bash ./bin/kafka-console-producer.sh --broker-list 192.168.129.12:9092 --topic test
bash ./bin/kafka-console-producer.sh --broker-list 192.168.129.12:9092,192.168.129.11:9092,192.168.129.13:9092 --topic shopping
只会和其中一个broker建立连接,多个broker为了保证高可用和负载均衡
两个命令执行的效果是一样的
# 从主题中消费消息, 程序不会自动退出
bash ./bin/kafka-console-consumer.sh --bootstrap-server 192.168.129.12:9092 --topic test --from-beginning
# 从主题中消费消息,如果超过5s都没有消息,程序就会自动退出
bash ./bin/kafka-console-consumer.sh --bootstrap-server 192.168.129.12:9092 --topic test --from-beginning --timeout-ms 5000
bash ./bin/kafka-console-consumer.sh --bootstrap-server 192.168.129.12:9092,192.168.129.11:9092,192.168.129.13:9092 --topic test --from-beginning --timeout-ms 5000
bash ./bin/kafka-console-consumer.sh --bootstrap-server 192.168.129.12:9092,192.168.129.11:9092,192.168.129.13:9092 --topic shopping --from-beginning --timeout-ms 5000
从主题消费消息时,--bootstrap-server也可以接受多个broker地址
# q1
bash ./bin/kafka-topics.sh --list --bootstrap-server 192.168.129.13:9092
bash ./bin/kafka-topics.sh --list --zookeeper 192.168.129.11:2181
这两个命令的区别?第一个是新版,第二个是老版
一个是直接与kafka broker建立连接获取主题列表信息
一个是与zookeeper建立连接获取主题列表信息。
并且,这两个命令不需要接所有的服务器,只需要其中一个就可以了。
因为这个与客户端建立连接的broker会和其他broker进行通信,获取到主题列表信息
笔者试过修改zkSever和broker的服务器的地址,发现拿到的结果都是一样的。
如果写上所有的broker地址,那么客户端是和所有的broker建立连接吗?
你在 --broker-list 参数中指定多个 Kafka broker 地址时,客户端实际上只会与其中的一个 broker 建立连接。指定多个 broker 地址的作用是提供高可用性和负载均衡。
#!/bin/bash
node1=192.168.129.11
node2=192.168.129.12
node3=192.168.129.13
case $1 in
"start"){
for i in $node1 $node2 $node3
do
echo -------------------------------- $i kafka 启动 ---------------------------
ssh $i "source /etc/profile;/usr/local/kafka/kafka_2.12-2.4.1/bin/kafka-server-start.sh -daemon /usr/local/kafka/kafka_2.12-2.4.1/config/server.properties"
done
}
;;
"stop"){
for i in $node1 $node2 $node3
do
echo -------------------------------- $i kafka 停止 ---------------------------
ssh $i "/usr/local/kafka/kafka_2.12-2.4.1/bin/kafka-server-stop.sh"
done
}
;;
esac
package p7.producer;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class MyProducer {
public static void main(String[] args) {
Properties properties = new Properties();
// 1.配置生产者启动的关键属性参数
// 1.1 BOOTSTRAP_SERVERS_CONFIG:连接kafka集群的服务列表,如果有多个,使用"逗号"进行分隔
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.129.11:9092,192.168.129.12:9092,192.168.129.13:9092");
properties.put(ProducerConfig.CLIENT_ID_CONFIG, "shopping-producer");
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
KafkaProducer<String, String> myProducer = new KafkaProducer<>(properties);
try{
for (int i = 0; i < 10; i++) {
String msg = "user"+i+": i buy an huawei phone";
ProducerRecord<String, String> kafkaMsg = new ProducerRecord<>("shopping", msg);
RecordMetadata metadata = myProducer.send(kafkaMsg).get();
System.out.println("同步方式发送消息结果:" + "topic-" + metadata.topic() + "|partition-"
+ metadata.partition() + "|offset-" + metadata.offset());
}
}catch (Exception e){
e.printStackTrace();
}finally {
myProducer.close();
}
}
}
package p7.producer;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
public class MyAsyncProducer {
public static void main(String[] args) {
Properties properties = new Properties();
// 1.配置生产者启动的关键属性参数
// 1.1 BOOTSTRAP_SERVERS_CONFIG:连接kafka集群的服务列表,如果有多个,使用"逗号"进行分隔
properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.129.11:9092,192.168.129.12:9092,192.168.129.13:9092");
properties.put(ProducerConfig.CLIENT_ID_CONFIG, "shopping-producer");
properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
KafkaProducer<String, String> myProducer = new KafkaProducer<>(properties);
try{
for (int i = 0; i < 10; i++) {
String msg = "user"+i+": i buy an huawei phone";
ProducerRecord<String, String> kafkaMsg = new ProducerRecord<>("shopping", msg);
myProducer.send(kafkaMsg, new Callback() {
@Override
public void onCompletion(RecordMetadata metadata, Exception e) {
if(e != null){
System.out.println("发送消息失败:" + e.getStackTrace());
}
if(metadata != null){
System.out.println("异步方式发送消息结果:" + "topic-"+
metadata.topic() + "|partition-"+
metadata.partition() + "|offset-" + metadata.offset());
}
}
});
Thread.sleep(2000); // 这个线程做了别的事情
}
}catch (Exception e){
e.printStackTrace();
}finally {
myProducer.close();
}
}
}
package p7.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.*;
public class MyConsumer {
private static final long MAX_IDLE_CNT=20;
public static void main(String[] args) {
// 1. 配置属性参数
Properties properties = new Properties();
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.129.11:9092,192.168.129.12:9092,192.168.129.13:9092");
// org.apache.kafka.common.serialization.StringDeserializer
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, "shopping-group");
// 常规属性:会话连接超时时间
properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, 10000);
// 消费者提交offset: 自动提交 & 手工提交,默认是自动提交
properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 5000);
// 2. 创建消费者对象
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(properties);
// 3. 订阅你感兴趣的主题:quick_start
consumer.subscribe(Collections.singletonList("shopping"));
System.err.println("shopping consumer started...");
try {
int cur_idle_cnt=0;
// 4.采用拉取消息的方式消费数据
while (true) {
// 等待多久拉取一次消息
// 拉取TOPIC_QUICKSTART主题里面所有的消息
// topic 和 partition是 一对多的关系,一个topic可以有多个partition
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
if(records.isEmpty()){
++cur_idle_cnt;
}else{
cur_idle_cnt=0;
}
if(cur_idle_cnt>=MAX_IDLE_CNT){
System.out.println("已经有20次拉取的消息为空");
break;
}
// 因为消息是在partition中存储的,所以需要遍历partition集合
for (TopicPartition topicPartition : records.partitions()) {
// 通过TopicPartition获取指定的消息集合,获取到的就是当前topicPartition下面所有的消息
List<ConsumerRecord<String, String>> partitionRecords = records.records(topicPartition);
// 获取TopicPartition对应的主题名称
String topic = topicPartition.topic();
// 获取当前topicPartition下的消息条数
int size = partitionRecords.size();
System.err.println(String.format("--- 获取topic: %s, 分区位置:%s, 消息总数: %s",
topic,
topicPartition.partition(),
size));
for (int i = 0; i < size; i++) {
ConsumerRecord<String, String> consumerRecord = partitionRecords.get(i);
// 实际的数据内容
String value = consumerRecord.value();
// 当前获取的消息偏移量
long offset = consumerRecord.offset();
// ISR : High Watermark, 如果要提交的话,比如提交当前消息的offset+1
// 表示下一次从什么位置(offset)拉取消息
long commitOffser = offset + 1;
System.err.println(String.format("获取实际消息 value:%s, 消息offset: %s, 提交offset: %s",
value, offset, commitOffser));
}
}
}
} finally {
consumer.close();
}
}
}
1. 生产者向shopping主题下发了100条消息。之后消费者第一次或者再次启动,消费者能消费到100条消息吗?
这里需要实践。我实践了一次。当时第一次启动的情况不会消费到。但是再次启动的情况就可以消费到。
服务器A上ssh登录远程服务器B, 为什么公钥放在B上而不是A上?
答: 这个要了解ssh客户端登录远程服务器的过程,才能解决这个问题。
这个使用公钥和私钥登录的详细过程说一下,拿服务器A和B举例?
答:当使用公钥和私钥进行身份验证登录时,以下是服务器A和B之间的详细过程:
ssh-keygen -t rsa -b 4096 -C "[email protected]"
这将生成一个 RSA 类型的密钥对,长度为 4096 位,并将其关联到你提供的电子邮件地址。
ssh-copy-id user@serverB_address
将 user 替换为你要登录的用户名,serverB_address 替换为服务器B的 IP 地址或域名。系统会要求你输入服务器B的密码。
这将自动将你的公钥添加到服务器B上的 ~/.ssh/authorized_keys 文件中,以授权你使用私钥进行身份验证。
ssh user@serverB_address
系统会提示你输入私钥的密码(如果你在第一步中设置了密码)。如果私钥没有密码,系统会直接连接到服务器B。
此时,SSH 客户端会自动使用本地的私钥与服务器B进行身份验证。如果私钥与公钥匹配,你将成功登录到服务器B。
通过这些步骤,你现在应该能够使用生成的公钥和私钥进行 SSH 身份验证,并通过服务器A登录到服务器B。
请确保在操作之前已经安装了 OpenSSH,并具备足够的权限来执行这些命令。如果你遇到问题或需要更详细的指导,
请参考相关操作系统和 SSH 客户端的文档,或向系统管理员寻求帮助。
并要求你提供与公钥对应的私钥进行身份验证。 这个私钥进行身份验证是在A还是B?
答:在使用 SSH 公钥/私钥进行身份验证时,私钥用于在本地计算机(服务器 A)上进行身份验证。在连接到远程服务器(服务器 B)时,SSH 客户端会将本地计算机上的私钥发送到服务器 B 进行身份验证。
具体来说,当你尝试连接到服务器 B 时,SSH 客户端将会向服务器 B 发送你的公钥。服务器 B 将检查该公钥是否与用户帐户
下 ~/.ssh/authorized_keys 文件中存储的公钥匹配。如果匹配,则服务器 B 将会向 SSH 客户端发送一个挑战文本。SSH 客户
端使用本地计算机上的私钥生成响应文本,并将其发送回服务器 B。如果服务器 B 能够使用公钥验证响应文本,则 SSH 客户
端被授权访问服务器 B。
因此,在这个过程中,私钥和公钥都不会离开各自的计算机。私钥用于本地计算机上的签名和身份验证,而公钥用于远程服务
器上的身份验证和加密。