You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google App Engine标准环境升级Java21后Jetty12闲置超时限制30秒

GAE Java 21运行时Jetty 30秒空闲超时问题

我在Google App Engine标准环境使用Java 8运行时多年,2024年初在其停止支持后成功迁移至Java 21运行时。升级后,当请求处理耗时超过30秒时,会收到如下500错误:

HTTP ERROR 500 java.util.concurrent.TimeoutException: Idle timeout expired: 30001/30000 ms
URI: http://XXXXX.appspot.com/servlet
STATUS: 500
MESSAGE: java.util.concurrent.TimeoutException: Idle timeout expired: 30001/30000 ms
CAUSED BY: java.util.concurrent.TimeoutException: Idle timeout expired: 30001/30000 ms
Caused by:
java.util.concurrent.TimeoutException: Idle timeout expired: 30001/30000 ms
at org.eclipse.jetty.io.IdleTimeout.checkIdleTimeout(IdleTimeout.java:167)
at org.eclipse.jetty.io.IdleTimeout.idleCheck(IdleTimeout.java:113)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)

从Java 8迁移的优势之一是HTTP请求超时从1分钟延长到了10分钟,但这个30秒超时显然来自Jetty 12层。实际测试发现,处理流程能正常完成(耗时超1分钟),但错误并非在30秒后立即出现,而是在处理完成后才弹出——哪怕耗时5分钟,错误信息仍显示“30000 ms timeout”。重新加载页面就能看到处理完成后的结果。

我推测错误发生在Jetty层,但无法直接访问或修改该超时参数,且该错误未出现在Google Cloud的Log Explorer中。我尝试了以下配置均无效:

appengine-web.xml配置

<system-properties>
  <property name="appengine.api.urlfetch.defaultDeadline" value="10"/>
  <property name="org.eclipse.jetty.io.IdleTimeout._idleTimeout" value="50000"/>
  <property name="JAVA_USER_OPTS" value="-Dorg.eclipse.jetty.io.IdleTimeout._idleTimeout=50000"/>
</system-properties>
  
<env-variables>
  <env-var name="JETTY_PROPERTIES" value="-Dorg.eclipse.jetty.io.IdleTimeout._idleTimeout=50000"/>
  <env-var name="JETTY_PROPERTIES" value="-Djetty.http.idleTimeout=60000" />
  <env-var name="JETTY_ARGS" value="idleTimeout=36000" />
  <env-var name="JETTY_PROPERTIES" value="-Dserver.jetty.connection-idle-timout=36000" />
  <env-var name="JETTY_ARGS" value="server.jetty.connection-idle-timout=36000" />
</env-variables>

web.xml配置

<context-param>
  <param-name>org.eclipse.jetty.server.IdleTimeout</param-name>
  <param-value>35000</param-value>
</context-param>

pom.xml覆盖Jetty依赖

<!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-server -->
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>12.0.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-io -->
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-io</artifactId>
    <version>12.0.7</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.eclipse.jetty.ee10/jetty-ee10-servlet -->
<dependency>
    <groupId>org.eclipse.jetty.ee10</groupId>
    <artifactId>jetty-ee10-servlet</artifactId>
    <version>12.0.6</version>
</dependency>

我尝试通过Servlet获取Jetty服务器实例,代码如下:

public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException 
{
    java.util.Enumeration<String> attributeNames = req.getServletContext().getAttributeNames();
    while (attributeNames.hasMoreElements()) {
        String attributeName = attributeNames.nextElement();
        Object attributeValue = getServletContext().getAttribute(attributeName);
        log.warning(attributeName + ": " + attributeValue);
            
        if (attributeName.equals("org.eclipse.jetty.server.Executor"))
        {
            // Get all fields of the object
            Field[] fields = attributeValue.getClass().getDeclaredFields();

            // Print the properties
            for (Field field : fields) {
                field.setAccessible(true); // Make the field accessible if it's private
                try {
                    Object value = field.get(attributeValue);
                    log.warning(field.getName() + ": " + value);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Log Explorer中的输出:

com.google.apphosting.runtime.jetty.APP_VERSION_CONTEXT_ATTR: com.google.apphosting.runtime.AppVersion@6f2b44ba
org.eclipse.jetty.tlds: [jar:file:///workspace/WEB-INF/lib/org.glassfish.web.jakarta.servlet.jsp.jstl-3.0.1.jar!/META-INF/fmt.tld, 
org.eclipse.jetty.util.DecoratedObjectFactory: org.eclipse.jetty.util.DecoratedObjectFactory[decorators=2]
org.eclipse.jetty.originAttribute: origin
org.apache.tomcat.InstanceManager: org.apache.tomcat.SimpleInstanceManager@XXXXXX
org.eclipse.jetty.quickstart.xml: /workspace/WEB-INF/quickstart-web.xml
org.eclipse.jetty.server.Executor: QueuedThreadPool[qtp1105322512]@XXXXX{STARTED,0<=0<=100,i=0,r=-1,t=-46773ms,q=0}[ReservedThreadExecutor@XXXXXXX{reserved=0/1,pending=0}]
jakarta.servlet.context.tempdir: /tmp/jetty-0_0_0_0-0-workspace-_-any-
org.eclipse.jetty.ee10.quickstart.QuickStartDescriptorProcessor: org.eclipse.jetty.ee10.quickstart.QuickStartDescriptorProcessor@XXXXX
org.apache.jasper.compiler.TldCache: org.apache.jasper.compiler.TldCache@XXXXX
org.apache.jasper.runtime.JspApplicationContextImpl: org.apache.jasper.runtime.JspApplicationContextImpl@XXXXX

org.eclipse.jetty.server.Executor实例的属性:

LOG: org.slf4j.jul.JDK14LoggerAdapter@693f642d
NOOP: org.eclipse.jetty.util.thread.QueuedThreadPool$$Lambda/0x00003e2948164ff8@5c84e324
_counts: 0|0
_evictThreshold: 4624122529
_threads: []
_joinLock: org.eclipse.jetty.util.thread.AutoLock$WithCondition@436a9f55
_jobs: []
_threadGroup: null
_threadFactory: QueuedThreadPool[qtp1105322512]@41e1e210{STARTED,0<=0<=100,i=0,r=-1,t=-46777ms,q=0}[ReservedThreadExecutor@17bd3b7c{reserved=0/1,pending=0}]
_name: qtp1105322512
**_idleTimeout: 60000**
_maxThreads: 100
_minThreads: 0
_reservedThreads: -1
_tryExecutor: ReservedThreadExecutor@17bd3b7c{reserved=0/1,pending=0}
_priority: 5
_daemon: false
_detailedDump: false
_lowThreadsThreshold: 1
_budget: org.eclipse.jetty.util.thread.ThreadPoolBudget@31ae5249
_stopTimeout: 5000
_virtualThreadsExecutor: null
_maxEvictCount: 1
_runnable: org.eclipse.jetty.util.thread.QueuedThreadPool$Runner@772be7f0

可以看到_idleTimeout属性值为60000毫秒,猜测修改该值也无法解决问题。


内容的提问来源于stack exchange,提问作者Isaac Meany

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 03:27:33