目的
在本地电脑虚拟机上装了三个kafka容器,然后通过frp映射,别的电脑可以通过访问云服务器端口来访问虚拟机,从而连接kafka
基础信息
云服务器公网IP:119.XXX.XXX.113
虚拟机hostname:ubuntu
三台容器的hostname:k01,k02,k03
云服务器涉及到的端口最好都开放入规则和出规则(没有试验是否有些可以不开放)
9092 19092 9093 19093 9094 19094
云服务器端口与虚拟机端口通过frp的映射关系
9092->9092,9093->9093,9094->9094 19092->19092,19093->19093,19094->19094
这样其他电脑访问云服务器特定端口就相当于访问虚拟机特定端口
docker容器与虚拟机的端口映射:
9092->k01:9092,19092->k01:19092
9093->k02:9092,19093->k02:19093
9094->k03:9092,19094->k03:19094
前提
最起码在这之前要在虚拟机里能跑通消费者和生产者程序,,,,
比如在虚拟机上同时启动
sudo docker exec -it k01 bin/kafka-console-producer.sh --bootstrap-server k01:9092 --topic test
和
sudo dockerr exec -it k01 bin/kafka-console-consumer.sh --bootstrap-server k01:9092 --topic test
然后生产者发消息看消费者能不能接受到
listener
要实现内外网分流实际上就是看三个kafka容器的config/server.properties里怎么配置不同的listener相关的配置
listener用来规定此kafka容器要监听哪个网卡的哪个端口,.0.0.0.0:9092默认监听所有网卡的9092端口(当然还有其他的知识,比如listener如果要设置0.0.0.0,则advertised.listener必须要写,但是这些知识与我要做的操作无关,如果需要了解,请访问别的帖子,比如Kafka配置内外网分流以及常见通信配置详解 - 知乎)
advertised.listeners:这个kafka容器要对外发布的监听器地址,因为kafka本身不知道兄弟节点地址,比如java程序里参数写:prop.setProperty("bootstrap.servers", "k01:9092"),那程序会连接k01,但是k01不知道k02和k03的ip和对应的开放端口,这时候需要知道k02和k03的ip:端口(因为数据在三个容器里,如果消费者要消费,必然是需要知道所有broker的地址的),此时k01会去zookeeper里寻找三个容器的地址(每个上线的kafka都会在/brokers/ids/序列号下注册临时节点,get /brokers/ids/数字可以得到信息),然后得到的每个容器的ip:port其实是每个容器config/server.properties里配置的advertised.listeners参数里的ip:port,即本参数,然后根据得到的每个容器的地址去连接每个容器
还有两个参数,在下面用到的时候会提
分析
只有内网
先分析一个容器k02且只在内网即虚拟机上进行生产消费的完善配置,
此时k02容器需要监听的端口只有9092,所以listener可写(不填ip也相当于监控所有网卡)
listeners=INTERNAL://:9092
即知道最终访问k02的9092端口就可以连接kafka(注意,这里不是填ubuntu映射的端口9093,因为ubuntu端口其实也只是一系列映射端口中的一个,不是最终的容器端口)
INTERNAL为INTERNAL://:9092这个监听器的名称,下面有用
然后我们在虚拟机上连接k02的broker的途径就是,连接ubuntu:9093,然后ubuntu:9093和k02:9092是映射关系,所以最终访问k02的9092端口,然后配置的listener就可以监控到数据了或者sudo docker exec -it k01 bin/kafka-console-producer.sh --bootstrap-server k02:9092 --topic test直接在容器里执行了
如果是sudo docker ....这种方式,那从zookeeper里取过来的发布的地址要为k02:9092,然后程序连接k02:9092从而成功访问kafka,如果是第一种方式,那zookeeper里发布的地址要为ubuntu:9093,从而程序接下来连接ubuntu:9093从而访问k02:9092,成功连接kafka
sudo docker...方式(第一种方式类似,没有写的必要,因为最终配置没用到)的advertised.listeners配置(监听器名字和listeners里的要相同,不相同有什么问题那就没有实验过了,直觉来说肯定有问题)
advertised.listeners=INTERNAL://k02:9092
如果是默认监听器不需要声明,非默认的(比如上面写的INTERNAL)需要声明
listener.security.protocol.map=INTERNAL:PLAINTEXT
PLAINTEXT是一种加密方式,想了解见上面引用的那个帖子
此时k02配置添加
listeners=INTERNAL://:9092
advertised.listeners=INTERNAL://k02:9092
listener.security.protocol.map=INTERNAL:PLAINTEXT
k01,k03类似配置
# k01
listeners=INTERNAL://:9092
advertised.listeners=INTERNAL://k01:9092
listener.security.protocol.map=INTERNAL:PLAINTEXT
# k03
listeners=INTERNAL://:9092
advertised.listeners=INTERNAL://k03:9092
listener.security.protocol.map=INTERNAL:PLAINTEXT
外网
我们先分析一个外网访问kafka容器k01的途径
任意电脑先链接云服务器公开端口119.XXX.XXX.113:19092,然后通过frp映射到ubuntu:19092,然后ubuntu:19092通过docker映射到容器k01的k01:19092,
即访问119.XXX.XXX.113:19092最终访问k01:19092,那对容器k01来说,要做的事只是监控19092端口(因为最后要有两个监听器,所以要起不同的名字,而且别的帖子说不同监听器监听的端口要不同,这个没试验过)
listeners=EXTERNAL://:19092
外网能且只能通过119.XXX.XXX.113:19092访问k01:19092,那k01发布的地址就要是外网能访问的
即
advertised.listeners=EXTERNAL://119.XXX.XXX.113:19092
同时自定义监听器需声明
listener.security.protocol.map=EXTERNAL:PLAINTEXT
即
k01配置
listener.security.protocol.map=EXTERNAL:PLAINTEXT
listeners=EXTERNAL://:19092
advertised.listeners=EXTERNAL://119.XXX.XXX.113:19092
k02,k03配置分别为
listener.security.protocol.map=EXTERNAL:PLAINTEXT
listeners=EXTERNAL://:19093
advertised.listeners=EXTERNAL://119.XXX.XXX.113:19093
listener.security.protocol.map=EXTERNAL:PLAINTEXT
listeners=EXTERNAL://:19094
advertised.listeners=EXTERNAL://119.XXX.XXX.113:19094
其实如果把这两种监听器合并即可得到最终配置,不过两个监听器,broker之间相同通信用哪个呢,肯定是要用内网那个,因为可能其他情形下内网不能连接外网,所以有个参数可配置broker使用的监听器
inter.broker.listener.name=INTERNAL
最终配置
# k01/server.properties下
listener.security.protocol.map=INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
listeners=INTERNAL://:9092,EXTERNAL://:19092
advertised.listeners=EXTERNAL://119.XXX.XXX.113:19092,INTERNAL://k01:9092
inter.broker.listener.name=INTERNAL
# k02/server.properties下
listener.security.protocol.map=INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
listeners=INTERNAL://:9092,EXTERNAL://:19093
advertised.listeners=EXTERNAL://119.XXX.XXX.113:19093,INTERNAL://k02:9092
inter.broker.listener.name=INTERNAL
# k03/server.properties下
listener.security.protocol.map=INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT
listeners=INTERNAL://:9092,EXTERNAL://:19094
advertised.listeners=EXTERNAL://119.XXX.XXX.113:19094,INTERNAL://k03:9092
inter.broker.listener.name=INTERNAL
此时有个疑问,zookeeper里暴露的每个容器都有两个地址,它怎么知道选哪个呢,我猜是根据你请求的端口来提供对应的地址
配置kafka重启之后,最好看下zookeeper的/brokers/ids/0 /brokers/ids/1 /brokers/ids/2里的值里的地址是不是你配置的
此时就有两种连接kafka的途径了,第一种在容器里用k01:9092或k02:9092或k03:9092(不要搞些花里胡哨的,比如localhost:9092或者k01的ip,192.168.0.11:9092这种,严格按照advertised.listeners写的东西来)连接,比如
sudo docker exec -it k01 bin/kafka-console-producer.sh --bootstrap-server k01:9092 --topic test
第二种外网访问(frp中转这种情况与在阿里云里搭建docker容器,docker里搭建kafka容器没有区别,无论中间经过多少端口映射,只要listeners里监控的有最终访问的kafka容器端口,advertised.listeners有你能访问的ip地址,比如云服务器公网ip,都可以成功,中间映射的若干端口没有用)
比如用java走云服务器公网ip连接(还是那句话,别花里胡哨的写)
properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "119.X.XXX.113:19092");
至此,配置就结束了