尝试使用 Ktor/Java8 读取在 App Engine Standard 上运行的数据时,Firestore 客户端挂起

Posted

技术标签:

【中文标题】尝试使用 Ktor/Java8 读取在 App Engine Standard 上运行的数据时,Firestore 客户端挂起【英文标题】:Firestore client hangs when attempting to read data running on App Engine Standard with Ktor/Java8 【发布时间】:2019-07-28 04:19:02 【问题描述】:

我在 Google App Engine 标准环境(使用 Java8、Kotlin 和 Ktor)中与 Firestore 集成时遇到问题。当我尝试执行任何操作时,Firestore 客户端挂起。在开发应用程序服务器中运行时它可以正常工作,但在生产中运行时则不行。

我在跑步:

Ktor 1.1.2 Kotlin 1.3.21 com.google.cloud:google-cloud-firestore(0.81.0-beta 版) 原生模式下的 Firestore

我已经用这条简单的路线进行了测试:

get("/test") 
    try 
        val firestore = FirestoreOptions.getDefaultInstance().service
        val user = firestore.collection("user").document("testusername").get().get()
        call.respond(user.getString("name")!!)
     catch (ex: Exception) 
        call.respond("Exception: " + ex)
    

在网络浏览器中访问/test 会导致网站在返回 500 错误之前挂起一分钟。

Google Cloud Platform 中记录了以下异常:

[e~website/20190306t125554.416577836925362605].<stdout>: 2019-03-06 13:01:31.306 [RequestEDA5B9B5] ERROR Application - Unhandled: GET - /test
java.lang.InterruptedException: null
    at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:506)
    at com.google.common.util.concurrent.AbstractFuture$TrustedFuture.get(AbstractFuture.java:83)
    at com.google.common.util.concurrent.ForwardingFuture.get(ForwardingFuture.java:62)
    at com.example.MainApplicationKt$run$4$2.invokeSuspend(MainApplication.kt:93)
    at com.example.MainApplicationKt$run$4$2.invoke(MainApplication.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:278)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:63)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:137)
    at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:157)
    at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:23)
    at io.ktor.routing.Routing.executeResult(Routing.kt:148)
    at io.ktor.routing.Routing.interceptor(Routing.kt:29)
    at io.ktor.routing.Routing$Feature$install$1.invokeSuspend(Routing.kt:93)
    at io.ktor.routing.Routing$Feature$install$1.invoke(Routing.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:278)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:63)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:137)
    at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:63)
    at io.ktor.features.ContentNegotiation$Feature$install$1.invoke(ContentNegotiation.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:278)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:63)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:137)
    at io.ktor.features.CallLogging$Feature$install$2.invokeSuspend(CallLogging.kt:130)
    at io.ktor.features.CallLogging$Feature$install$2.invoke(CallLogging.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:278)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:63)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:137)
    at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:157)
    at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:23)
    at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invokeSuspend(DefaultEnginePipeline.kt:106)
    at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invoke(DefaultEnginePipeline.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:278)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:63)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:137)
    at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:157)
    at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:23)
    at io.ktor.server.servlet.KtorServlet$blockingService$1.invokeSuspend(KtorServlet.kt:125)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
    at kotlinx.coroutines.DispatchedKt.resumeCancellable(Dispatched.kt:410)
    at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:25)
    at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
    at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:160)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:52)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at io.ktor.server.servlet.KtorServlet.blockingService(KtorServlet.kt:96)
    at io.ktor.server.servlet.KtorServlet.service(KtorServlet.kt:57)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:848)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1772)
    at com.google.apphosting.utils.servlet.JdbcmysqlConnectionCleanupFilter.doFilter(JdbcMySqlConnectionCleanupFilter.java:60)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:582)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:524)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:226)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
    at com.google.apphosting.runtime.jetty9.ParseBlobUploadHandler.handle(ParseBlobUploadHandler.java:119)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1182)
    at com.google.apphosting.runtime.jetty9.AppEngineWebAppContext.doHandle(AppEngineWebAppContext.java:183)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:512)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at com.google.apphosting.runtime.jetty9.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:296)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
    at org.eclipse.jetty.server.Server.handle(Server.java:539)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:333)
    at com.google.apphosting.runtime.jetty9.RpcConnection.handle(RpcConnection.java:202)
    at com.google.apphosting.runtime.jetty9.RpcConnector.serviceRequest(RpcConnector.java:81)
    at com.google.apphosting.runtime.jetty9.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:123)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchServletRequest(JavaRuntime.java:695)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchRequest(JavaRuntime.java:658)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:628)
    at com.google.apphosting.runtime.JavaRuntime$NullSandboxRequestRunnable.run(JavaRuntime.java:820)
    at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:269)
    at java.lang.Thread.run(Thread.java:748)

这是我的 application.conf 文件的内容:

ktor 
    application 
        modules = [com.example.MainApplicationKt.main]
    

我已经测试过了:

启用结算 从头开始创建新项目 改用 com.google.firebase:firebase-admin 包(版本 6.7.0)(结果相同) 改用 Datastore(这似乎工作正常,但从 Firestore 切换需要我移动整个项目并重写大量代码)

【问题讨论】:

你能发布 application.conf 文件吗? @Moinkhan,当然。我已经用我的 application.conf 文件的内容更新了帖子。 可能是您的代码抛出异常...您可以用 try catch 包装...并在 catch 子句中用空字符串响应.. @Moinkhan,我刚刚尝试捕获测试代码的所有异常,但得到的结果与以前相同。 【参考方案1】:

我在 App Engine Flex 环境中运行时遇到了类似的问题,最终发现 Flex 提供的默认实例大小 (0.6 GB) 太小而无法支持 Firestore API。因此,在 Flex 案例中,我需要调整我的 app.yaml 文件以包含一个“资源”部分,如下所示:

resources:
  cpu: 2
  memory_gb: 2.3
  disk_size_gb: 10

App Engine 灵活部署中的资源设置参考:https://cloud.google.com/appengine/docs/flexible/custom-runtimes/configuring-your-app-with-app-yaml#resource-settings

对于 App Engine Standard,我希望您可能需要通过调整 app-engine.xml 文件中的“instance-class”来执行类似的操作。 “F1”是 App Engine Standard 中的默认实例类,Java 8 运行时限制为 128 MB,Java 11 运行时限制为 256 MB。因此,您可能想尝试切换到“F2”或“F4”。

在 App Engine Standard java 部署中配置实例类的参考:https://cloud.google.com/appengine/docs/standard/java/config/appref#syntax

App Engine 实例类参考:https://cloud.google.com/appengine/docs/standard/#instance_classes

【讨论】:

以上是关于尝试使用 Ktor/Java8 读取在 App Engine Standard 上运行的数据时,Firestore 客户端挂起的主要内容,如果未能解决你的问题,请参考以下文章

递归读取目录文件下的所有文件

以编程方式读取 Adob​​e 游戏变量/与游戏/游戏机器人交互

E/dalvikvm: JNI ERROR (app bug): local reference table overflow (max=512)

尝试从 create-react-app 中的 .json 文件中读取图像

如何在 app.config 中提供 xml 文件源并从 winform 中写入/读取这些 xml 文件?

从 app.config 文件中读取