首页 > 解决方案 > Spring Boot 应用在部署到 Google App Engine 时无法连接到 Google Cloud SQL (PostgreSQL)

问题描述

我有一个基于 Gradle 的 Spring Boot(版本 2.1.6)应用程序,我正在部署到 Google App Engine。我创建了一个我正在尝试连接的 Google Cloud SQL (PostgreSQL 11) 数据库实例。

我已按照此示例中的所有步骤操作:https ://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-sql-postgres -样本

当我在本地运行应用程序时,一切正常。但是当我将应用程序部署到 App Engine 时,它​​无法连接到数据库。这些是我得到的错误:

ERROR: (gcloud.app.deploy) Error Response: [9] 
Application startup error:
    at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40) ~[google-api-client-1.28.0.jar!/:1.28.0]
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:417) ~[google-api-client-1.28.0.jar!/:1.28.0]
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1089) ~[google-http-client-1.30.1.jar!/:na]
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:515) ~[google-api-client-1.28.0.jar!/:1.28.0]
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:448) ~[google-api-client-1.28.0.jar!/:1.28.0]
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:565) ~[google-api-client-1.28.0.jar!/:1.28.0]
    at com.google.cloud.sql.core.CloudSqlInstance.fetchEphemeralCertificate(CloudSqlInstance.java:327) ~[jdbc-socket-factory-core-1.0.14.jar!/:na]
    ... 10 common frames omitted

2019-10-22 20:49:13.206  INFO 1 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
    name: default
    ...]
2019-10-22 20:49:13.474  INFO 1 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core {5.3.10.Final}
2019-10-22 20:49:13.485  INFO 1 --- [           main] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2019-10-22 20:49:14.420  INFO 1 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.0.4.Final}
2019-10-22 20:49:15.392 ERROR 1 --- [           main] o.s.b.web.embedded.tomcat.TomcatStarter  : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'authenticationTokenFilter': Unsatisfied dependency expressed through field 'apiKeysService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'apiKeysServiceImpl' defined in URL [jar:file:/app.jar!/BOOT-INF/classes!/nl/storegear/sgapi/services/servicesimpl/ApiKeysServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'apiKeyRepository': Cannot create inner bean '(inner bean)#72e34f77' of type [org.springframework.orm.jpa.SharedEntityManagerCreator] while setting bean property 'entityManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '(inner bean)#72e34f77': Cannot resolve reference to bean 'entityManagerFactory' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
2019-10-22 20:49:15.466  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2019-10-22 20:49:15.473  WARN 1 --- [           main] o.a.c.loader.WebappClassLoaderBase       : The web application [ROOT] appears to have started a thread named [pool-1-thread-1] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 sun.misc.Unsafe.park(Native Method)
 java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
 java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1088)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
 java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
 java.lang.Thread.run(Thread.java:748)
2019-10-22 20:49:15.478  WARN 1 --- [           main] o.a.c.loader.WebappClassLoaderBase       : The web application [ROOT] appears to have started a thread named [pool-1-thread-2] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
...

这是我的application.properties文件的样子:

...

spring.datasource.hikari.connectionTimeout=20000
spring.datasource.hikari.maximumPoolSize=5

spring.datasource.username={REDACTED}
spring.datasource.password={REDACTED}
spring.cloud.gcp.sql.database-name={REDACTED}
spring.cloud.gcp.sql.instance-connection-name={REDACTED}
spring.datasource.continue-on-error=true
spring.datasource.initialization-mode=always
spring.cloud.gcp.credentials.location=file:src/main/resources/{REDACTED}.json
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false
spring.jpa.show-sql=false

...

我认为这可能与 gcp 凭据 json 文件有关,但我不知道接下来要尝试什么。有任何想法吗?

标签: javapostgresqlspring-bootjdbcgoogle-cloud-platform

解决方案


这里可能有两个问题。

1-

在本地测试应用程序时,它能够连接并获得身份验证,因为“application.properties”文件已正确配置,私钥的路径在“..credentials.location”属性中指定。部署 App Engine 时,尝试连接的不是应用程序,而是为应用程序提供服务的App Engine 实例本身。我怀疑在这种情况下会发生什么,App Engine 没有被告知去哪里获取驻留在“application.properties”Spring Boot 文件中的正确凭据。

在本教程中,在 App Engine 上运行的 Spring Boot 应用程序连接到 Cloud SQL 实例时完成了类似的集成。用于将 App Engine 指向凭据的方法是通过保存凭据的Spring Profiles以及“<a href="https://cloud.google.com/appengine/docs/standard/java11/ config/appref" rel="nofollow noreferrer">app.yaml” 文件,其中配置文件通过“spring_profiles_active”环境变量激活。

或者,可以在 Cloud SDK 中完成身份验证,如您引用的示例的第 5 步中所述。这是用于通过此方法获取凭据的命令,这是来自完整教程的实现示例。

2-

Spring Boot 启动器使用Tomcat 作为默认的 servlet 容器。App Engine 使用Jetty 作为 servlet 容器。在部署 App Engine 的时候,如果不忽略 Tomcat 的依赖,就会和 Jetty 的发生冲突。这可能是在执行 App Engine 部署时引发“osbweb.embedded.tomcat.TomcatStarter : Error started Tomcat context”错误时发生的情况。

这是有关如何排除这些依赖项的官方 Spring 文档,这里是集成相同组件的完整教程中的实现示例


推荐阅读