将 gcloud VM 上运行的 Spring Boot 连接到云 SQL 实例

Posted

技术标签:

【中文标题】将 gcloud VM 上运行的 Spring Boot 连接到云 SQL 实例【英文标题】:Connecting Spring Boot running on gcloud VM to cloud SQL instance 【发布时间】:2020-09-03 16:32:35 【问题描述】:

我正在尝试使用在 google cloud vm 上运行的 spring boot 连接到 cloud mysql

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-gcp-starter-sql-mysql</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

这是堆栈跟踪的结尾,我不知道如何在浏览器中通过 SSH 复制整个堆栈跟踪

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1287) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        ... 47 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultMySqlJdbcInfoProvider' defined in class path resource [org/springframework/cloud/gcp/autoconfigure/sql/GcpCloudSqlAutoConfiguration$MySqlJdbcInfoProviderConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cloud.gcp.autoconfigure.sql.CloudSqlJdbcInfoProvider]: Factory method 'defaultMySqlJdbcInfoProvider' threw exception; nested exception is java.lang.IllegalArgumentException: A database name must be provided.
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:656) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1287) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        ... 61 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cloud.gcp.autoconfigure.sql.CloudSqlJdbcInfoProvider]: Factory method 'defaultMySqlJdbcInfoProvider' threw exception; nested exception is java.lang.IllegalArgumentException: A database name must be provided.
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        ... 75 common frames omitted
Caused by: java.lang.IllegalArgumentException: A database name must be provided.
        at org.springframework.util.Assert.hasText(Assert.java:284) ~[spring-core-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        at org.springframework.cloud.gcp.autoconfigure.sql.DefaultCloudSqlJdbcInfoProvider.<init>(DefaultCloudSqlJdbcInfoProvider.java:39) ~[spring-cloud-gcp-autoconfigure-1.2.2.RELEASE.jar:1.2.2.RELEASE]
        at org.springframework.cloud.gcp.autoconfigure.sql.GcpCloudSqlAutoConfiguration$MySqlJdbcInfoProviderConfiguration.defaultMySqlJdbcInfoProvider(GcpCloudSqlAutoConfiguration.java:87) ~[spring-cloud-gcp-autoconfigure-1.2.2.RELEASE.jar:1.2.2.RELEASE]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
        at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.3.RELEASE.jar:5.2.3.RELEASE]
        ... 76 common frames omitted

我在 *** 上找到了一些关于此的文章和其他答案,确保我在 application.properties 中的设置正确但仍然出现相同的错误。不知道下一步该去哪里搜索。

spring.datasource.driverClassName=com.mysql.jdbc.GoogleDriver
spring.jpa.hibernate.ddl-auto=none
spring.cloud.gcp.sql.database-name=teamplanner
spring.datasource.url=jdbc:mysql://34.107.103.23:3306/teamplanner
spring.datasource.username=teamplanner
spring.datasource.password=xxxxxxxxxx
spring.datasource.initialization-mode=always
server.port = 8080
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.cloud.gcp.sql.instance-connection-name=teamplanner-springboot-rest:europe-west3:teamplanner-springboot-rest-mysql

请帮忙

-----------------编辑 这是数据库名称、用户和连接属性

依赖版本:

这是云 gcp 的父依赖,它定义了 spring-cloud-gcp-starter-sql-mysql 依赖的版本。在我自己的电脑上开发时,我连接到我的电脑上安装的 MySql,我没有spring-cloud-gcp-dependenciesspring-cloud-gcp-starter-sql-mysql,只有mysql-connector-java,一切都运行良好。

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-gcp-dependencies</artifactId>
                <version>1.2.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
</dependencyManagement>

这是用于 Spring Boot 启动器依赖项

<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>

我可以使用这个命令从我的云虚拟机 SSH 成功连接到 mysql mysql --host=34.107.103.23 --user=teamplanner --password 然后它提示我输入密码,我输入 xxxxxxxxx 并连接。

【问题讨论】:

能否分享一下你的云SQL配置(尤其是数据库页面)和依赖的版本? 添加了一些信息 我打赌资源定义有冲突。你能试着评论这行spring.datasource.url=jdbc:mysql://34.107.103.23:3306/teamplanner吗? 我注释掉了整行,还尝试更改为jdbc:mysql://34.107.103.23:3306。同样的错误 - A database name must be provided. 我正在尝试简单地部署我的 rest api,以便我可以将项目添加到 CV,并且员工可以根据需要检查它。如果我们不能在谷歌云上修复这个错误,也许你会推荐一个我可以免费托管的地方?我查看了 heroku,但它具有睡眠功能,您的应用程序将在 30 分钟没有传入请求后进入睡眠状态。雇主可能不会等待它在睡眠后启动并认为它不起作用。 相反,您是否也尝试评论spring.cloud.gcp.sql.*行? 【参考方案1】:

我在使用 PostgreSQL spring boot starter 时看到了同样的问题,但版本稍新。我正在使用 2.0.5 版的 spring-cloud-gcp 依赖项和 2.5.5 版的 spring boot。

奇怪的是,我想我注意到属性文件本身中的属性顺序实际上可能是初学者的问题。如果我的属性文件如下所示:

spring.datasource.username=postgres
spring.datasource.password=$sm://projects/212050278585/secrets/postgres-user/versions/1

#name the application
spring.application.name=data-svc

spring.cloud.gcp.sql.database-name=service-db
spring.cloud.gcp.sql.instance-connection-name=gcp-project-331021:us-central1:gcp-poc

然后我在堆栈跟踪中收到错误:

java.lang.IllegalArgumentException: A database name must be provided.
    at org.springframework.util.Assert.hasText(Assert.java:289) ~[spring-core-5.3.10.jar:5.3.10]
    at com.google.cloud.spring.autoconfigure.sql.DefaultCloudSqlJdbcInfoProvider.<init>(DefaultCloudSqlJdbcInfoProvider.java:41) ~[spring-cloud-gcp-autoconfigure-2.0.4.jar:2.0.4]
    at com.google.cloud.spring.autoconfigure.sql.CloudSqlEnvironmentPostProcessor.postProcessEnvironment(CloudSqlEnvironmentPostProcessor.java:86) ~[spring-cloud-gcp-autoconfigure-2.0.4.jar:2.0.4]
    at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEnvironmentPreparedEvent(EnvironmentPostProcessorApplicationListener.java:102) ~[spring-boot-2.5.5.jar:2.5.5]
    at org.springframework.boot.env.EnvironmentPostProcessorApplicationListener.onApplicationEvent(EnvironmentPostProcessorApplicationListener.java:87) ~[spring-boot-2.5.5.jar:2.5.5]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) ~[spring-context-5.3.10.jar:5.3.10]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) ~[spring-context-5.3.10.jar:5.3.10]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) ~[spring-context-5.3.10.jar:5.3.10]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131) ~[spring-context-5.3.10.jar:5.3.10]
    at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:82) ~[spring-boot-2.5.5.jar:2.5.5]
    at org.springframework.boot.SpringApplicationRunListeners.lambda$environmentPrepared$2(SpringApplicationRunListeners.java:63) ~[spring-boot-2.5.5.jar:2.5.5]
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1541) ~[na:na]

但如果我将属性重新排序为如下所示:

spring.cloud.gcp.sql.database-name=service-db
spring.cloud.gcp.sql.instance-connection-name=ascendant-pixel-331021:us-central1:gcp-poc

spring.datasource.username=postgres
spring.datasource.password=$sm://projects/212050278585/secrets/postgres-user/versions/1

#name the application
spring.application.name=data-svc

不同之处在于我的spring.cloud.gcp.sql.* 属性首先出现在文件中。我知道这很奇怪,但它似乎对我有所帮助。

【讨论】:

优秀.. 也为我工作.. 谢谢你的发现,但你明白为什么它会以这种方式工作吗?? 我不知道除了检查 Spring 引导之外还有什么好的答案。我记得以前看到过这种类型的行为,虽然它似乎“没有充分的理由”,但我想这与设置和配置参数的优先顺序有很大关系。 是的..完全理解,理想情况下不应该是这样。【参考方案2】:

错误必须在 application.properties 类中。改成这样。

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.datasource.url=jdbc:mysql://34.107.103.23/teamplanner?useSSL=false
spring.datasource.username=teamplanner
spring.datasource.password=**********

这一定是有效的。这里发生的是您在数据库 URL 前面使用本地 MySQL 服务器端口号。无需添加端口号为 8080。如果显示错误为 8080 已在使用中,则只需添加另一个端口号。否则默认情况下它将端口作为 8080。 将此依赖项添加到 pom.xml

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

【讨论】:

那么问题一定出在数据库名上。 “teamplanner”必须是您的云数据库实例名称。您的数据库名称可能是另一个名称,请仔细检查。如果您仍然遇到问题,请将您的代码上传到 github 提供链接。这样我就可以通过它找出问题。 能否请您查看我在主帖下的最后一条评论?我在哪里谈论 @PropertySource ?我认为这可能是真正的问题,计算引擎环境的设置根本看不到/读取 application.properties 我已经用依赖项更新了我的答案,只需将其添加到 pom 文件并重新运行代码。【参考方案3】:

在我的 PC 上,当我从 spring initializr 创建项目时,我必须使用大写的 R 手动创建 Resources 文件夹。几个月来,我一直在 Windows 上从 IDE 运行项目并且没有问题。当我将项目移动到计算引擎并开始使用mvn spring-boot:run 运行它时,应用程序没有从 Resources 文件夹中读取任何内容,因为它没有按照约定命名,因为它应该是没有大写 R 的“资源”。IDE 必须做了一些事情,以便应用程序将从大写的文件夹中读取。重命名文件夹解决了这个问题。

【讨论】:

以上是关于将 gcloud VM 上运行的 Spring Boot 连接到云 SQL 实例的主要内容,如果未能解决你的问题,请参考以下文章

将 gcloud 虚拟机实例监控数据提取到 BigQuery

如何阻止某人更改 gcloud 配置文件?

与 GCLoud VM 的已打开 SSH 连接是不是可以防止其冻结/崩溃?

如何禁用`gcloud preview app run'的运行状况检查

gcloud compute 远程执行命令

Gcloud Compute - 虚拟机不断终止