将消息发布到 GCP pubSub 主题失败

Posted

技术标签:

【中文标题】将消息发布到 GCP pubSub 主题失败【英文标题】:publishing messages to GCP pubSub topic fails 【发布时间】:2018-08-13 23:52:41 【问题描述】:

我有一个 Spring Boot 应用程序,它尝试将消息发布到 Cloud Pub/Sub 主题。当我在本地机器上运行应用程序时,它成功地将消息发布到主题中。但在将其部署到 App Engine 后,它会失败并出现以下异常

com.google.api.gax.rpc.UnavailableException: io.grpc.StatusRuntimeException: UNAVAILABLE: Credentials failed to obtain metadata
    at com.google.api.gax.rpc.ApiExceptionFactory.createException(ApiExceptionFactory.java:69) ~[gax-1.16.0.jar:1.16.0]
    at com.google.cloud.pubsub.v1.Publisher$4.onFailure(Publisher.java:373) ~[google-cloud-pubsub-0.33.0-beta.jar:0.33.0-beta]
    at com.google.common.util.concurrent.Futures$4.run(Futures.java:1123) [guava-20.0.jar:na]
    at com.google.common.util.concurrent.MoreExecutors$DirectExecutor.execute(MoreExecutors.java:435) [guava-20.0.jar:na]
    at com.google.common.util.concurrent.AbstractFuture.executeListener(AbstractFuture.java:900) [guava-20.0.jar:na]
    at com.google.common.util.concurrent.AbstractFuture.complete(AbstractFuture.java:811) [guava-20.0.jar:na]
    at com.google.common.util.concurrent.AbstractFuture.setException(AbstractFuture.java:675) [guava-20.0.jar:na]
    at io.grpc.stub.ClientCalls$GrpcFuture.setException(ClientCalls.java:492) [grpc-stub-1.9.0.jar:1.9.0]
    at io.grpc.stub.ClientCalls$UnaryStreamToFuture.onClose(ClientCalls.java:467) [grpc-stub-1.9.0.jar:1.9.0]
    at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:41) [grpc-core-1.9.0.jar:1.9.0]
    at io.grpc.internal.CensusStatsModule$StatsClientInterceptor$1$1.onClose(CensusStatsModule.java:684) [grpc-core-1.9.0.jar:1.9.0]
    at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:41) [grpc-core-1.9.0.jar:1.9.0]
    at io.grpc.internal.CensusTracingModule$TracingClientInterceptor$1$1.onClose(CensusTracingModule.java:392) [grpc-core-1.9.0.jar:1.9.0]
    at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:475) [grpc-core-1.9.0.jar:1.9.0]
    at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java:63) [grpc-core-1.9.0.jar:1.9.0]
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.close(ClientCallImpl.java:557) [grpc-core-1.9.0.jar:1.9.0]
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.access$600(ClientCallImpl.java:478) [grpc-core-1.9.0.jar:1.9.0]
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:590) [grpc-core-1.9.0.jar:1.9.0]
    at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37) [grpc-core-1.9.0.jar:1.9.0]
    at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123) [grpc-core-1.9.0.jar:1.9.0]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_112-google-v7]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_112-google-v7]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_112-google-v7]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:295) [na:1.8.0_112-google-v7]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_112-google-v7]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_112-google-v7]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_112-google-v7]
Caused by: io.grpc.StatusRuntimeException: UNAVAILABLE: Credentials failed to obtain metadata
    at io.grpc.Status.asRuntimeException(Status.java:526) ~[grpc-core-1.9.0.jar:1.9.0]
    ... 19 common frames omitted
Caused by: java.io.IOException: Could not get the access token.
    at com.google.auth.oauth2.AppEngineCredentials.refreshAccessToken(AppEngineCredentials.java:136) ~[google-auth-library-oauth2-http-0.7.1.jar:na]
    at com.google.auth.oauth2.OAuth2Credentials.refresh(OAuth2Credentials.java:149) ~[google-auth-library-oauth2-http-0.7.1.jar:na]
    at com.google.auth.oauth2.OAuth2Credentials.getRequestMetadata(OAuth2Credentials.java:135) ~[google-auth-library-oauth2-http-0.7.1.jar:na]
    at com.google.auth.Credentials.blockingGetToCallback(Credentials.java:103) ~[google-auth-library-credentials-0.9.0.jar:na]
    at com.google.auth.Credentials$1.run(Credentials.java:92) ~[google-auth-library-credentials-0.9.0.jar:na]
    ... 7 common frames omitted
Caused by: java.lang.reflect.InvocationTargetException: null
    at sun.reflect.GeneratedMethodAccessor21.invoke(Unknown Source) ~[na:na]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_112-google-v7]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_112-google-v7]
    at com.google.auth.oauth2.AppEngineCredentials.refreshAccessToken(AppEngineCredentials.java:131) ~[google-auth-library-oauth2-http-0.7.1.jar:na]
    ... 11 common frames omitted
Caused by: com.google.apphosting.api.ApiProxy$CallNotFoundException: Can't make API call memcache.Get in a thread that is neither the original request thread nor a thread created by ThreadManager
    at com.google.apphosting.api.ApiProxy$CallNotFoundException.foreignThread(ApiProxy.java:844) ~[runtime-shared.jar:na]
    at com.google.apphosting.api.ApiProxy$1.get(ApiProxy.java:183) ~[runtime-shared.jar:na]
    at com.google.apphosting.api.ApiProxy$1.get(ApiProxy.java:180) ~[runtime-shared.jar:na]
    at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:93) ~[appengine-api.jar:na]
    at com.google.appengine.api.memcache.MemcacheServiceImpl.quietGet(MemcacheServiceImpl.java:28) ~[appengine-api.jar:na]
    at com.google.appengine.api.memcache.MemcacheServiceImpl.get(MemcacheServiceImpl.java:51) ~[appengine-api.jar:na]
    at com.google.appengine.api.appidentity.AppIdentityServiceImpl.getAccessToken(AppIdentityServiceImpl.java:302) ~[appengine-api.jar:na]
    ... 15 common frames omitted

代码复制自this Spring guide。

【问题讨论】:

【参考方案1】:

您分享的错误消息明确指出UNAVAILABLE: Credentials failed to obtain metadata,因此该错误看起来与 App Engine 中的凭据有关。显然,根据您对问题的描述,您的应用程序在本地开发服务器中运行时工作正常,但是一旦您将其部署到您的 App Engine 标准应用程序,它就会失败。这一切都表明您可能有错误的凭据配置。

如the Authentication section in the guide you shared 和App Engine's documentation 中所述,App Engine Standard 使用应用程序默认凭据进行身份验证。为此,它会检查环境变量 GOOGLE_APPLICATION_CREDENTIALS 中的凭据,如果缺少凭据,它将使用 App Engine 应用程序中的默认服务帐户。

知道,您可能遇到的问题是您指向的凭据文件在远程 (App Engine) 环境中不可用。因此,我的建议是,如果您认为可以使用默认 App Engine 服务帐户,只需将 GOOGLE_APPLICATION_CREDENTIALS 环境变量和 spring.cloud.gcp.credentials.location 属性留空,以便 App Engine 从其自己的服务帐户获取默认凭据.请记住,它应该具有与 Pub/Sub 交互的权限才能使其正常工作,因此如果需要,您可能需要 grant roles to the service account。

【讨论】:

以上是关于将消息发布到 GCP pubSub 主题失败的主要内容,如果未能解决你的问题,请参考以下文章

GCP - 如何添加关于发送到 pubsub 死信队列的消息数量的警报?

GCP Pubsub 主题持续时间中存在的消息数

如何在 apache camel 中执行 gcp pubsub 消息的并行处理

GCP PubSub 主题推送问题

如何从 Cloudflare 工作人员内部发布到 GCP PubSub 主题

GCP PubSub:通过 CURL 类型的请求发布消息