当 Spring Boot 容器尝试连接 kafka 容器时出现“Broker 可能不可用”错误

Posted

技术标签:

【中文标题】当 Spring Boot 容器尝试连接 kafka 容器时出现“Broker 可能不可用”错误【英文标题】:Getting "Broker may not be available" error when spring boot container tries to connect kafka container 【发布时间】:2021-02-15 22:22:05 【问题描述】:

我在使用 spring boot kafka docker 时遇到问题,尝试了以下链接中提到的所有方法,但问题仍然存在。

Spring Boot containers can not connect to the Kafka container

附加我的 docker-compose.yml

version: '2'
services:
  zookeeper:
    image: wurstmeister/zookeeper
    container_name: zookeeper
    ports:
    - "2181:2181"
  kafka:
    image: wurstmeister/kafka
    container_name: kafka
    ports:
    - "9092:9092"
    environment:
      KAFKA_ADVERTISED_HOST_NAME: kafka
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
  my-app:
    image: my-app
    container_name: my-app
    ports:
      - "8081:8081"
    depends_on:
      - zookeeper
      - kafka

错误日志

-1 (localhost/127.0.0.1:9092) could not be established. Broker may not be available.
my-app     | 2020-11-03 08:10:21.444  WARN 1 --- [| adminclient-1] org.apache.kafka.clients.NetworkClient   : [AdminClient clientId=adminclient-1] Connection to node -1 (localhost/127.0.0.1:9092) could not be established. Broker may not be available.

我正在运行 wrustmiester/kafka 和 wrustmiester/zookeeper。 如果我在本地运行 spring boot 应用程序,一切运行顺利。 当我尝试构建我的 Spring Boot 应用程序的映像并运行该映像时,问题就出现了。然后错误是 Broker 可能不可用。 请哪位大神指导一下。

添加我的 application.yml

spring:
  kafka:
    consumer:
      bootstrap-servers: localhost:9092
      group-id: group_id
      auto-offset-reset: earliest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      #org.apache.kafka.common.serialization.StringDeserializer
      properties.spring.json.trusted.packages: com.myapp.pojo

    producer:
      bootstrap-servers: localhost:9092
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
      #org.apache.kafka.common.serialization.StringSerializer

将我的 docker-compose.yml 更新为

version: '3'
services:
  zookeeper:
    image: wurstmeister/zookeeper
    container_name: zookeeper
    network_mode: bridge
    ports:
    - "2181:2181"
  kafka:
    image: wurstmeister/kafka
    container_name: kafka
    network_mode: bridge
    ports:
    - "9092:9092"
    hostname: kafka
    environment:
      KAFKA_ADVERTISED_HOST_NAME: kafka
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
     # KAFKA_LISTENERS: INSIDE://:9092,OUTSIDE://:9094
     # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
     # KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE
      KAFKA_ADVERTISED_PORT: 9092
    links:
      - zookeeper
  my-app:
    image: my-app
    container_name: my-app
    network_mode: bridge
    ports:
      - "8081:8081"
    depends_on:
      - zookeeper
      - kafka

将我的 application.yml 更新为

spring:
  kafka:
    consumer:
      bootstrap-servers: kafka:9092
      group-id: group_id
      auto-offset-reset: earliest
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
      #org.apache.kafka.common.serialization.StringDeserializer
      properties.spring.json.trusted.packages: com.myapp.pojo

    producer:
      bootstrap-servers: kafka:9092
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
      #org.apache.kafka.common.serialization.StringSerializer

错误日志相同

WARN 1 --- [| adminclient-1] org.apache.kafka.clients.NetworkClient   : [AdminClient clientId=adminclient-1] Connection to node -1 (localhost/127.0.0.1:9092) could not be established. Broker may not be available.
my-app     | 2020-11-03 17:05:39.585  WARN 1 --- [| adminclient-1] org.apache.kafka.clients.NetworkClient   : [AdminClient clientId=adminclient-1] Connection to node -1 (localhost/127.0.0.1:9092) could not be established. Broker may not be available.

【问题讨论】:

见confluent.io/blog/… 只提供链接无济于事,请告诉我哪里出错了 博文中提到,连接localhost是不正确的。在应用容器内,它指的是应用容器,而不是代理。您的生产者和消费者无法连接到应用容器中的任何内容 【参考方案1】:

我能够解决我的问题。

这是我更新后的 docker-compose.yml ,其余配置与上面相同

version: '2'
services:
  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000

  kafka:
    image: confluentinc/cp-kafka:latest
    depends_on:
      - zookeeper
    ports:
      - 9092:9092
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

  my-app:
    image: my-app
    container_name: my-app
    environment:
      SPRING_KAFKA_BOOTSTRAPSERVERS: kafka:29092
    ports:
      - "8081:8081"
    depends_on:
      - kafka

所做的更改是

    我已经使用了 confluent Kafka 和 zookeeper,但即使是上面带有 wrustmeister Kafka 和 zookeeper 的 docker-compose.yml 也可以。

    最重要的属性

    环境: SPRING_KAFKA_BOOTSTRAPSERVERS: kafka:29092

    也如@OneCricketeer 所建议的那样

或者,如果所有三个客户端都在与相同的通信 集群,从生产者和消费者中删除引导服务器,以及 使用 spring.kafka.bootstrap-servers 全局设置它

我觉得即使这样也应该有效。 也许我可以稍后再试一下,然后更新我的答案。

【讨论】:

【参考方案2】:

我以几乎相同的方式做了同样的事情(差别很小),看看下面的 compose-file,如果这对你有用

version: '3'

services:
  zookeeper:
    image: wurstmeister/zookeeper
    ports:
      - 2181:2181
  kafka:
    image: wurstmeister/kafka
    ports:
      - 9092:9092
    hostname: kafka
    environment:
      - KAFKA_ADVERTISED_HOST_NAME=kafka
      - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
      - KAFKA_ADVERTISED_PORT=9092
    links:
      - zookeeper

【讨论】:

我是否应该在 application.yml 中为引导服务器提及主机名:localhost:9092,而不是 localhost -> kafka 不,在这种情况下你需要使用服务或容器名称访问 kafka,所以基本上你应该在 application.yml 文件中使用 kafka:9092 是的,我已将 localhost:9092 替换为 kafka:9092。但问题仍然存在 @Ajinkya 然后编辑您的问题以反映更改和新的错误消息 @OneCricketeer,我已经更新了问题,错误依旧。【参考方案3】:

您的错误来自 AdminClient,因此,如果您没有在代码中手动创建该实例,则需要为 spring.kafka.admin.* 添加一个配置部分,并在那里设置引导服务器

或者,如果所有三个客户端都在与同一个集群进行通信,则从生产者和消费者中删除引导服务器,并将其设置为全局spring.kafka.bootstrap-servers

由于您使用的是 Docker 网络,因此地址需要是 Kafka 容器的主机名。另请注意,links 是已弃用的 Compose 功能,不应依赖

【讨论】:

你提到的所有细节都无法帮助我解决问题。您能否分享一些代码 sn-ps 并指出我在配置中做错了什么。此外,没有3个客户。我有 1 个与 Apache Kafka 通信的 Spring Boot Rest 应用程序 "您的错误来自 AdminClient,因此,如果您没有在代码中手动创建该实例,则需要使用 bootstrap 为 spring.kafka.admin.* 添加一个配置部分-servers 也设置在那里”-> 当我在本地运行 spring boot 应用程序并在 docker 上运行 Kafka 和 zookeeper 图像时,相同的代码可以工作,所以我认为创建 Admin 实例没有任何问题。请阅读我的问题,如果我遗漏了任何其他细节,请告诉我。 我确实阅读了您的问题。您无法将“在本地运行”与在 Docker 中运行所有内容进行比较。就这么简单——如果你试图在其他任何地方部署你的容器(使用 Docker 的要点),它也不会工作。该错误清楚地表明 localhost 仍在某处的代码中使用,并且实际上没有看到您的完整 Spring 应用程序,很难说那会是哪里,因为您的配置文件没有显示(除非管理属性默认为 localhost,而且你没有提到它的部分)。如果您可以在本地运行所有内容,请不要使用 Docker 在本地运行意味着 Spring boot 在本地运行,但 Kafka 和 zookeeper 在 docker 上。所以基本上我的 spring-boot 应用程序与 docker 图像通信。此外,如果您需要任何其他详细信息或配置文件,我已准备好发布它,请告诉我。另外,如果需要,我可以分享我的项目的 GitHub 链接。 我的意思是,Kafka 通常位于远程集群环境中。 (例如,亚马逊 MSK)。在这种情况下,您的代码会出现相同的错误。但是,是的,您的完整堆栈跟踪和创建/使用 AdminClient 的代码是必要的【参考方案4】:

你可以试试这个吗

version: '3'
services:
  zookeeper:
    image: wurstmeister/zookeeper
    container_name: zookeeper
    ports:
    - "2181:2181"
  kafka:
    image: wurstmeister/kafka
    container_name: kafka
    ports:
    - "9092:9092"
    hostname: kafka
    environment:
      KAFKA_ADVERTISED_HOST_NAME: kafka
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
     # KAFKA_LISTENERS: INSIDE://:9092,OUTSIDE://:9094
     # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
     # KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE
      KAFKA_ADVERTISED_PORT: 9092
    links:
      - zookeeper
  my-app:
    image: my-app
    container_name: my-app
    ports:
      - "8081:8081"
    depends_on:
      - zookeeper
      - kafka
networks:
  default:
    external:
      name: bridge

【讨论】:

【参考方案5】:

我通过在我的 Spring-Boot 应用程序中配置一个 KafkaAdmin bean 并传递 bootstrap-servers 属性来修复:

@SpringBootApplication
public class CpoExecutorApplication 

    @Value("$spring.kafka.template.default-topic")
    private String topicName;

    @Value("$spring.kafka.bootstrap-servers")
    private String bootstrapAddress;

    @Bean
    public KafkaAdmin kafkaAdmin() 
        Map<String, Object> configs = new HashMap<>();
        configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);
        return new KafkaAdmin(configs);
    

    @Bean
    public NewTopic processTopic() 
        return TopicBuilder.name(topicName).partitions(2).build();
    
....

application.yml

server:
  port: 8000
spring:
  kafka:
    bootstrap-servers: localhost:9094
    consumer:
      auto-offset-reset: false
    template:
      default-topic: process-topic
.....

这样我就不需要在 docker-compose.yml 中传递 SPRING_KAFKA_BOOTSTRAPSERVERS 环境变量了。

【讨论】:

以上是关于当 Spring Boot 容器尝试连接 kafka 容器时出现“Broker 可能不可用”错误的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot,Java,Docker Compose,尝试在两个容器(mysql,my-api)之间建立连接时出现“连接被拒绝”

Docker:无法连接 Spring Boot 和 MYSQL

无法从 Spring Boot Docker 容器连接 mysql Docker 容器

使用 docker-compose 无法在同一网络中连接 mysql 和 spring boot

Spring Boot 内嵌容器Undertow参数设置

Spring Boot 内嵌容器Undertow参数设置