Java Google App Engine Standard 在随机时间段出现内存缓存服务错误

Service error in memcache with Java Google App Engine Standard at random time periods

在过去的一个月里,我们的 Java Google App Engine 标准 Web 应用开始在看似随机的时间出现奇怪的错误(请参阅下面的堆栈跟踪)。大约在这个时候,我们进行了以下更改:

  1. 从 Java7 运行时切换到 Java8/Jetty9 运行时(这让我们更灵活地链接到第 3 方支付库)。

  2. 切换到使用 Google Cloud SDK 部署,而不是单独的 Google App Engine SDK。

昨天我们经历了 3 个错误时期。其中一次发生在 2 月 28 日太平洋标准时间 0530 点到太平洋标准时间 0600 点左右。突然,所有从数据库加载的尝试都开始失败:

com.google.appengine.api.memcache.LogAndContinueErrorHandler handleServiceError: Service error in memcache (LogAndContinueErrorHandler.java:50)
com.google.appengine.api.memcache.MemcacheServiceException: Memcache getIdentifiables: exception getting multiple keys
    at com.google.appengine.api.memcache.MemcacheServiceApiHelper$RpcResponseHandler.handleApiProxyException(MemcacheServiceApiHelper.java:77)
    at com.google.appengine.api.memcache.MemcacheServiceApiHelper.absorbParentException(MemcacheServiceApiHelper.java:122)
    at com.google.appengine.api.utils.FutureWrapper.handleParentException(FutureWrapper.java:56)
    at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:95)
    at com.google.appengine.api.memcache.MemcacheServiceImpl.quietGet(MemcacheServiceImpl.java:28)
    at com.google.appengine.api.memcache.MemcacheServiceImpl.getIdentifiables(MemcacheServiceImpl.java:61)
    at com.googlecode.objectify.cache.EntityMemcache.getAll(EntityMemcache.java:215)
    at com.googlecode.objectify.cache.CachingAsyncDatastoreService.get(CachingAsyncDatastoreService.java:253)
    at com.googlecode.objectify.impl.engine.LoadEngine$Round.fetchPending(LoadEngine.java:172)
    at com.googlecode.objectify.impl.engine.LoadEngine$Round.execute(LoadEngine.java:118)
    at com.googlecode.objectify.impl.engine.LoadEngine.execute(LoadEngine.java:258)
    at com.googlecode.objectify.impl.cmd.LoaderImpl.refs(LoaderImpl.java:110)
    at com.googlecode.objectify.impl.cmd.LoaderImpl.refs(LoaderImpl.java:98)
    at com.googlecode.objectify.impl.cmd.LoaderImpl.ref(LoaderImpl.java:88)
    at com.googlecode.objectify.impl.cmd.LoadTypeImpl.refOf(LoadTypeImpl.java:92)
    at com.googlecode.objectify.impl.cmd.LoadTypeImpl.id(LoadTypeImpl.java:86)
    at com.codeavengers.LoginServlet.doPost(LoginServlet.java:62)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
    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.codeavengers.UrlFilter.doFilter(UrlFilter.java:143)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at com.codeavengers.payments.PermissionsFilter.doFilter(PermissionsFilter.java:114)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at com.googlecode.objectify.cache.AsyncCacheFilter.doFilter(AsyncCacheFilter.java:59)
    at com.googlecode.objectify.ObjectifyFilter.doFilter(ObjectifyFilter.java:49)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:125)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at com.google.apphosting.runtime.jetty9.SaveSessionFilter.doFilter(SaveSessionFilter.java:43)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at com.google.apphosting.utils.servlet.JdbcMySqlConnectionCleanupFilter.doFilter(JdbcMySqlConnectionCleanupFilter.java:60)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1759)
    at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:48)
    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.ContextHandler.doHandle(ContextHandler.java:1180)
    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:297)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)
    at org.eclipse.jetty.server.Server.handle(Server.java:534)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:320)
    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:108)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchServletRequest(JavaRuntime.java:680)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchRequest(JavaRuntime.java:642)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:612)
    at com.google.apphosting.runtime.JavaRuntime$NullSandboxRequestRunnable.run(JavaRuntime.java:806)
    at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:274)
    at java.lang.Thread.run(Thread.java:745)
Caused by: com.google.apphosting.api.ApiProxy$UnknownException: An error occurred for the API request memcache.Get().
    at com.google.apphosting.utils.runtime.ApiProxyUtils.statusException(ApiProxyUtils.java:207)
    at com.google.apphosting.utils.runtime.ApiProxyUtils.getRpcError(ApiProxyUtils.java:128)
    at com.google.apphosting.runtime.ApiProxyImpl$AsyncApiFuture.setRpcError(ApiProxyImpl.java:601)
    at com.google.apphosting.runtime.ApiProxyImpl$AsyncApiFuture.failure(ApiProxyImpl.java:589)
    at com.google.apphosting.runtime.http.HttpApiHostClient.communicationFailure(HttpApiHostClient.java:170)
    at com.google.apphosting.runtime.http.JettyHttpApiHostClient$Listener.onComplete(JettyHttpApiHostClient.java:141)
    at org.eclipse.jetty.client.ResponseNotifier.notifyComplete(ResponseNotifier.java:193)
    at org.eclipse.jetty.client.ResponseNotifier.notifyComplete(ResponseNotifier.java:185)
    at org.eclipse.jetty.client.HttpExchange.notifyFailureComplete(HttpExchange.java:269)
    at org.eclipse.jetty.client.HttpExchange.abort(HttpExchange.java:240)
    at org.eclipse.jetty.client.HttpConversation.abort(HttpConversation.java:141)
    at org.eclipse.jetty.client.HttpRequest.abort(HttpRequest.java:735)
    at org.eclipse.jetty.client.HttpDestination.abort(HttpDestination.java:267)
    at org.eclipse.jetty.client.PoolingHttpDestination.failed(PoolingHttpDestination.java:90)
    at org.eclipse.jetty.client.DuplexConnectionPool.failed(DuplexConnectionPool.java:159)
    at org.eclipse.jetty.util.Promise$Wrapper.failed(Promise.java:84)
    at org.eclipse.jetty.client.HttpClient.failed(HttpClient.java:587)
    at org.eclipse.jetty.client.AbstractHttpClientTransport.connectFailed(AbstractHttpClientTransport.java:152)
    at org.eclipse.jetty.client.AbstractHttpClientTransport.connect(AbstractHttpClientTransport.java:141)
    at org.eclipse.jetty.client.HttpClient.connect(HttpClient.java:592)
    at org.eclipse.jetty.client.HttpClient.succeeded(HttpClient.java:569)
    at org.eclipse.jetty.client.HttpClient.succeeded(HttpClient.java:562)
    at org.eclipse.jetty.util.SocketAddressResolver$Async.lambda$resolve(SocketAddressResolver.java:181)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.run(QueuedThreadPool.java:589)
    ... 1 more

根据我目前所做的调查,我认为这可能与我们使用的 Objectify 版本有关。我们在 objectify-4.0b1.jar。 我尝试升级到 objectify 4.1,并且该站点版本同时工作,而 4.0 版本不工作。所以也许这与它有关?

我对将使用 objectify 4.1 的版本切换到生产版本犹豫不决,因为一旦您这样做,我们就无法切换回该站点的旧稳定版本。所以我想确保升级能真正解决我们的问题。

更新 1

应用程序正常运行 22 小时后,应用程序再次出现故障并出现更多 Memcache 错误。我们尝试清除 Memcache,但没有做任何事情。 24 小时后,该版本的应用程序仍然无法运行。

我们通过使用完全相同的代码重新部署新版本解决了这个问题,它又开始正常工作了。 24 小时后该版本仍然运行良好。

更新 2

我们切换到使用 Objectify 4.1.3 的版本,24 小时后该版本运行良好。

有关相关问题的额外信息

自从切换到 Java8+CloudSDK 以来,过去一个月我们遇到了其他奇怪的问题。

例如,在随机时间(主要是太平洋标准时间周五下午),该站点会在某些计算机上停止正常工作。然后在 30 分钟左右后它会再次工作。然后一周后又出现了同样的问题

仔细阅读 Google Cloud SDK 的文档后,它说他们更改了 Cloud SDK 的默认暂存选项,因为 delete-jsps 是 true,而 App Engine SDK 是 false。我们的服务器正在从几个 JSP 文件中读取数据...因此此更改破坏了我们的网站。

事实是,它在本地服务器上仍然运行良好,在实时服务器上它也有 99% 的时间运行良好。就好像他们是 AB 测试在随机时间更改该默认参数对一定百分比的实例的影响。这看起来很疯狂,因为他们可以检查我们的数据日志并看到我们的网站正在从 jsp 个文件中加载和读取值。

一旦我们从 JSP 文件中删除该数据并将其放入单独的配置文件中,该问题就消失了。

似乎 Google 又一次尝试随机对 Memcache 进行一些更改,这在使用 Objectify 4.0b2 时完全破坏了我们的站点?可行吗?

IIRC 在 4.0b2 和 4.1 之间,Objectify 使用内存缓存的方式发生了一些变化。虽然这似乎不太可能,但可能使用模式可能会引发内存缓存中的某种潜在问题。

4.0b2 真的 很老了 - 超过 4 年 - 从那时起已经修复了大量问题。迁移到当前版本是明智的。

由于我们今天遇到过此类问题(没有明显原因的内存缓存错误),因此在出现问题的版本上重新部署完全相同的代码也解决了我们的问题(至少目前是这样)。我们正在使用 Objectify 5.1.24。