为什么在首次启动时无法使用 spring-boot-starter-data-cassandra 从 Spring Boot 实例连接到在 Docker 上运行的 Cassandra?

问题描述 投票:0回答:1

我有以下内容

  cassandra:
    image: cassandra:latest
    ports:
      - 9042:9042
    volumes:
      - ./cassandra/image:/var/lib/cassandra
    environment:
      - CASSANDRA_AUTHENTICATOR=AllowAllAuthenticator
      - CASSANDRA_AUTHORIZER=AllowAllAuthorizer

cassandra 实例网络看起来像这样......

"Networks": {
            "cbusha-infra_default": {
                "IPAMConfig": null,
                "Links": null,
                "Aliases": [
                    "cbusha-infra-cassandra-1",
                    "cassandra",
                    "572f0770b41e"
                ],
                "MacAddress": "02:42:ac:1a:00:04",
                "NetworkID": "b44e49f0f195651a259b7b859fcadda128d359db18de4ab0a4e8b3efa4ed0e35",
                "EndpointID": "6d5fb1b98d2c427a760030a4804db29798893517b48616409575babe0f0f9ae8",
                "Gateway": "172.26.0.1",
                "IPAddress": "172.26.0.4",
                "IPPrefixLen": 16,
                "IPv6Gateway": "",
                "GlobalIPv6Address": "",
                "GlobalIPv6PrefixLen": 0,
                "DriverOpts": null,
                "DNSNames": [
                    "cbusha-infra-cassandra-1",
                    "cassandra",
                    "572f0770b41e"
                ]
            }
        }

我确认我可以使用 IJ 连接管理器进行本地连接

我现在正在尝试连接,因此我的 Spring 应用程序有以下配置

spring:
  cassandra:
    contact-points: cassandra
    port: 9042
    keyspace-name: cbusha
    local-datacenter: datacenter1
    schema-action: CREATE_IF_NOT_EXISTS
    connect-timeout-millis: 30000 # 30 seconds
    read-timeout-millis: 30000 # 30 seconds

我什至使用一个函数来确保它首先启动(我还添加了逻辑来测试键空间是否可用)...

@SpringBootApplication
public class BackendApplication {

    private static final Logger log = LoggerFactory.getLogger(BackendApplication.class);

    public static void main(String[] args) {
        waitForCassandra();
        SpringApplication sa = new SpringApplication(BackendApplication.class);
        sa.addBootstrapRegistryInitializer(new MyBootstrapInitializer());
        sa.run(args);
    }

    private static void waitForCassandra() {
        CqlSessionBuilder builder = CqlSession.builder();
        AtomicInteger attempts = new AtomicInteger();
        try (ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor()) {
            executor.scheduleAtFixedRate(() -> {
                try (CqlSession session = builder.build()) {
                    ResultSet rs = session.execute("SELECT keyspace_name FROM system_schema.keyspaces WHERE keyspace_name = 'cbusha';");
                    if (rs.one() != null) {
                        executor.shutdown();
                    } else {
                        if (attempts.incrementAndGet() >= 12) { // 12 attempts * 10 seconds sleep = 2 minutes
                            log.error("Keyspace cbusha does not exist - exiting after 2 minutes");
                            System.exit(1);
                        }
                        log.debug("Keyspace cbusha does not exist - sleeping");
                    }
                } catch (Exception e) {
                    if (attempts.incrementAndGet() >= 12) { // 12 attempts * 10 seconds sleep = 2 minutes
                        log.error("Cassandra is unavailable - exiting after 2 minutes");
                        System.exit(1);
                    }
                    log.debug("Cassandra is unavailable - sleeping");
                }
            }, 0, 10, TimeUnit.SECONDS);
        }
        log.info("Cassandra is up - executing command");
    }
}

但是当我尝试在 docker 上启动时,我看到连接可以访问

2024-03-24 13:33:45 17:33:45.055 [main] INFO com.cbusha.be.BackendApplication -- Cassandra is up - executing command

但是,当它尝试创建通道时,我会收到this gist中的错误。

我看到它正在使用正确的配置文件,并且 docker dns 正在此处解析

endPoint=cassandra/172.26.0.4:9042

如果我稍后重新启动容器,它就会起作用。

这是为什么?有没有办法在开始之前确认通道是否正常工作(类似于连接)?

spring-boot docker cassandra
1个回答
0
投票

我怀疑这是因为您在单独的服务中进行了初始化。在连接到 Cassandra 之前,您的代码不会等待初始化完成。

您的代码需要等待 Cassandra 可用才能连接。请参阅您之前的问题此处,其中初始化脚本也需要等待建立连接。

├── docker-compose.yml
├── Dockerfile
├── pom.xml
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── example
│       │           └── demo
│       │               ├── config
│       │               │   └── CassandraConnectionCheck.java
│       │               └── DemoApplication.java
│       └── resources
│           └── application.properties
└── wait-for-cassandra.sh

🗎

CassandraConnectionCheck.java

package com.example.demo.config;

import com.datastax.oss.driver.api.core.CqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class CassandraConnectionCheck implements CommandLineRunner {
    private static final Logger logger = LoggerFactory.getLogger(CassandraConnectionCheck.class);

    @Value("${spring.data.cassandra.contact-points}")
    private String cassandraHost;
    @Value("${spring.data.cassandra.port}")
    private int cassandraPort;

    private final CqlSession cqlSession;
    public CassandraConnectionCheck(CqlSession cqlSession) {
        this.cqlSession = cqlSession;
    }

    @Override
    public void run(String... args) {
        logger.info("🔵 Making connection to Cassandra at {}:{}.", cassandraHost, cassandraPort);

        if (!cqlSession.getMetadata().getNodes().isEmpty()) {
            logger.info("🟢 Connected to Cassandra at {}:{}.", cassandraHost, cassandraPort);
        } else {
            logger.error("🔴 Failed to connect to Cassandra at {}:{}.", cassandraHost, cassandraPort);
        }
    }
}

🗎

application.properties

spring.data.cassandra.keyspace-name=mykeyspace
spring.data.cassandra.contact-points=cassandra
spring.data.cassandra.port=9042
spring.data.cassandra.schema-action=none
spring.data.cassandra.local-datacenter=datacenter1

🗎

Dockerfile

FROM maven:3.6.3-openjdk-11-slim AS build

WORKDIR /home/app

COPY src src
COPY pom.xml /home/app

RUN mvn clean package -DskipTests

FROM openjdk:11-jre-slim

RUN apt-get update && apt-get install -y netcat && apt-get clean

COPY --from=build /home/app/target/spring-boot-app-0.0.1-SNAPSHOT.jar /usr/local/lib/spring-boot-app.jar

EXPOSE 9000

COPY wait-for-cassandra.sh .
RUN chmod +x wait-for-cassandra.sh

ENTRYPOINT ["./wait-for-cassandra.sh"]

🗎

wait-for-cassandra.sh
(这会等待 Cassandra 可用,然后创建密钥空间,最后运行 Spring Boot 应用程序。)

#!/bin/bash

CASSANDRA_HOST=cassandra
CASSANDRA_PORT=9042
MAX_RETRIES=10
RETRY_INTERVAL=10

echo "Waiting for Cassandra at ${CASSANDRA_HOST}:${CASSANDRA_PORT} to be ready..."

success=false
for ((i=1;i<=MAX_RETRIES;i++)); do
    if nc -z $CASSANDRA_HOST $CASSANDRA_PORT; then
        echo "Cassandra is ready."
        success=true
        break
    else
        echo "Cassandra is not ready yet. Attempt ${i}/${MAX_RETRIES}."
        sleep $RETRY_INTERVAL
    fi
done

if [ "$success" = true ] ; then
    echo "Create keyspace..."
    cqlsh -e "CREATE KEYSPACE IF NOT EXISTS mykeyspace WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'};" cassandra 9042
    echo "Done!"

    echo "Start Spring Boot application."
    exec java -jar /usr/local/lib/spring-boot-app.jar
else
    echo "Failed to connect to Cassandra after ${MAX_RETRIES} attempts."
fi

© www.soinside.com 2019 - 2024. All rights reserved.