timeout

你提出了一个非常好的问题!方案1确实有问题,让我分析一下:

方案1的问题

在新线程中执行会导致:

  1. ThreadLocal 数据丢失:很多框架依赖 ThreadLocal 存储请求上下文

  2. 事务管理问题:数据库事务通常绑定在线程上

  3. 请求/响应对象线程安全性:Servlet 规范不保证 Request/Response 对象的线程安全

  4. 额外的线程切换开销

更好的方案

让我重新实现一个在当前线程执行,只用超时检测的版本:你说得对!我重新提供了4个方案,每个都有不同的权衡:

方案对比

方案

执行线程

能否中断

适用场景

方案1:Servlet异步

新线程

✅ 可以

最标准,Jetty原生支持

方案2:监控线程

当前线程

⚠️ 只能设置响应

需要保持ThreadLocal

方案3:简单检查

当前线程

❌ 不能

只记录超时,不中断

方案4:ThreadLocal

当前线程

✅ 业务代码配合

需要业务代码主动检查

推荐选择

如果需要保持在当前线程执行(保留ThreadLocal、事务等),推荐方案2(监控线程)

如果可以接受新线程,推荐方案1(Servlet异步)

你的框架是怎样的架构?是否依赖ThreadLocal存储上下文?这样我可以给出更具体的建议。


import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicBoolean
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

/**
 * 方案1:基于 Servlet 异步支持(推荐)
 * 在当前线程执行业务逻辑,使用 Servlet 原生超时机制
 */
class AsyncTimeoutMiddleware(
    private val timeoutMillis: Long = 30000L
) : Middleware {
    
    override fun handle(ctx: Context, next: Next) {
        val request = ctx.request
        val response = ctx.response
        
        // 启动异步处理
        val asyncContext = request.startAsync()
        asyncContext.timeout = timeoutMillis
        
        val completed = AtomicBoolean(false)
        
        // 设置超时监听器
        asyncContext.addListener(object : javax.servlet.AsyncListener {
            override fun onTimeout(event: javax.servlet.AsyncEvent) {
                if (completed.compareAndSet(false, true)) {
                    handleTimeout(response)
                    asyncContext.complete()
                }
            }
            
            override fun onComplete(event: javax.servlet.AsyncEvent) {}
            override fun onError(event: javax.servlet.AsyncEvent) {
                completed.set(true)
            }
            override fun onStartAsync(event: javax.servlet.AsyncEvent) {}
        })
        
        // 在新线程中执行(保持独立的执行上下文)
        asyncContext.start {
            try {
                next.invoke()
                if (completed.compareAndSet(false, true)) {
                    asyncContext.complete()
                }
            } catch (e: Exception) {
                if (completed.compareAndSet(false, true)) {
                    handleError(response, e)
                    asyncContext.complete()
                }
            }
        }
    }
    
    private fun handleTimeout(response: HttpServletResponse) {
        if (!response.isCommitted) {
            response.status = 408
            response.contentType = "application/json; charset=utf-8"
            response.writer.write("""{"error":"Request Timeout","message":"请求处理超时"}""")
        }
    }
    
    private fun handleError(response: HttpServletResponse, e: Exception) {
        if (!response.isCommitted) {
            response.status = 500
            response.contentType = "application/json; charset=utf-8"
            response.writer.write("""{"error":"Internal Server Error"}""")
        }
    }
}

/**
 * 方案2:监控线程方案
 * 业务逻辑在当前线程执行,另起监控线程检测超时
 */
class MonitorTimeoutMiddleware(
    private val timeoutMillis: Long = 30000L
) : Middleware {
    
    private val scheduler = Executors.newScheduledThreadPool(1)
    
    override fun handle(ctx: Context, next: Next) {
        val completed = AtomicBoolean(false)
        val response = ctx.response
        
        // 启动超时监控任务
        val timeoutFuture = scheduler.schedule({
            if (completed.compareAndSet(false, true)) {
                // 超时了,尝试设置响应
                if (!response.isCommitted) {
                    try {
                        response.status = 408
                        response.contentType = "application/json; charset=utf-8"
                        response.writer.write("""{"error":"Request Timeout"}""")
                    } catch (e: Exception) {
                        // 响应可能已经开始写入,忽略异常
                    }
                }
            }
        }, timeoutMillis, TimeUnit.MILLISECONDS)
        
        try {
            // 在当前线程执行业务逻辑
            next.invoke()
            
            // 正常完成,取消超时任务
            if (completed.compareAndSet(false, true)) {
                timeoutFuture.cancel(false)
            }
        } catch (e: Exception) {
            completed.set(true)
            timeoutFuture.cancel(false)
            throw e
        }
    }
    
    fun shutdown() {
        scheduler.shutdown()
    }
}

/**
 * 方案3:基于时间检查的简单方案
 * 只在请求前后检查时间,不能中断正在执行的请求
 */
class SimpleTimeoutMiddleware(
    private val timeoutMillis: Long = 30000L
) : Middleware {
    
    override fun handle(ctx: Context, next: Next) {
        val startTime = System.currentTimeMillis()
        
        try {
            next.invoke()
            
            val duration = System.currentTimeMillis() - startTime
            if (duration > timeoutMillis && !ctx.response.isCommitted) {
                // 虽然完成了,但超时了
                ctx.response.setHeader("X-Request-Timeout", "true")
                ctx.response.setHeader("X-Request-Duration", duration.toString())
            }
        } catch (e: Exception) {
            val duration = System.currentTimeMillis() - startTime
            if (duration > timeoutMillis) {
                // 可能是超时导致的异常
                if (!ctx.response.isCommitted) {
                    ctx.response.status = 408
                    ctx.response.contentType = "application/json; charset=utf-8"
                    ctx.json(mapOf(
                        "error" to "Request Timeout",
                        "duration" to duration
                    ))
                }
                return
            }
            throw e
        }
    }
}

/**
 * 方案4:ThreadLocal + 定时检查(适合同步执行)
 * 在当前线程执行,但允许业务代码自行检查超时状态
 */
class ThreadLocalTimeoutMiddleware(
    private val timeoutMillis: Long = 30000L
) : Middleware {
    
    companion object {
        private val timeoutContext = ThreadLocal<TimeoutContext>()
        
        // 业务代码可以调用此方法检查是否超时
        fun checkTimeout() {
            val ctx = timeoutContext.get()
            if (ctx != null && ctx.isTimeout()) {
                throw TimeoutException("Request timeout")
            }
        }
    }
    
    private data class TimeoutContext(
        val startTime: Long,
        val timeoutMillis: Long
    ) {
        fun isTimeout(): Boolean {
            return System.currentTimeMillis() - startTime > timeoutMillis
        }
    }
    
    override fun handle(ctx: Context, next: Next) {
        val context = TimeoutContext(System.currentTimeMillis(), timeoutMillis)
        timeoutContext.set(context)
        
        try {
            next.invoke()
            
            if (context.isTimeout() && !ctx.response.isCommitted) {
                ctx.response.status = 408
                ctx.response.contentType = "application/json; charset=utf-8"
                ctx.json(mapOf("error" to "Request Timeout"))
            }
        } catch (e: TimeoutException) {
            if (!ctx.response.isCommitted) {
                ctx.response.status = 408
                ctx.response.contentType = "application/json; charset=utf-8"
                ctx.json(mapOf("error" to "Request Timeout"))
            }
        } finally {
            timeoutContext.remove()
        }
    }
}

// ============ 使用示例 ============

fun example1() {
    val app = Application()
    
    // 推荐:使用 Servlet 异步支持
    app.use(AsyncTimeoutMiddleware(30000))
    
    app.get("/api/test") { ctx ->
        // 业务逻辑在独立线程中执行
        Thread.sleep(5000)
        ctx.json(mapOf("message" to "success"))
    }
}

fun example2() {
    val app = Application()
    
    // 监控线程方案:业务逻辑在当前线程
    app.use(MonitorTimeoutMiddleware(30000))
    
    app.get("/api/test") { ctx ->
        // 业务逻辑在当前请求线程中执行
        // 可以安全使用 ThreadLocal、事务等
        Thread.sleep(5000)
        ctx.json(mapOf("message" to "success"))
    }
}

fun example3() {
    val app = Application()
    
    // 简单方案:只记录超时
    app.use(SimpleTimeoutMiddleware(30000))
    
    app.get("/api/test") { ctx ->
        Thread.sleep(5000)
        ctx.json(mapOf("message" to "success"))
    }
}

fun example4() {
    val app = Application()
    
    // ThreadLocal 方案:业务代码可主动检查超时
    app.use(ThreadLocalTimeoutMiddleware(30000))
    
    app.get("/api/process") { ctx ->
        for (i in 1..100) {
            // 处理每个项目前检查超时
            ThreadLocalTimeoutMiddleware.checkTimeout()
            
            processItem(i)
        }
        ctx.json(mapOf("message" to "completed"))
    }
}

fun processItem(i: Int) {
    Thread.sleep(100)
}

// ============ 基础类型定义 ============

interface Middleware {
    fun handle(ctx: Context, next: Next)
}

typealias Next = () -> Unit

interface Context {
    val request: HttpServletRequest
    val response: HttpServletResponse
    
    fun json(data: Any)
}

class Application {
    fun use(middleware: Middleware) {}
    fun get(path: String, handler: (Context) -> Unit) {}
}