timeout
你提出了一个非常好的问题!方案1确实有问题,让我分析一下:
方案1的问题
在新线程中执行会导致:
ThreadLocal 数据丢失:很多框架依赖 ThreadLocal 存储请求上下文
事务管理问题:数据库事务通常绑定在线程上
请求/响应对象线程安全性:Servlet 规范不保证 Request/Response 对象的线程安全
额外的线程切换开销
更好的方案
让我重新实现一个在当前线程执行,只用超时检测的版本:你说得对!我重新提供了4个方案,每个都有不同的权衡:
方案对比
方案
执行线程
能否中断
适用场景
方案1:Servlet异步
新线程
✅ 可以
最标准,Jetty原生支持
方案2:监控线程
当前线程
⚠️ 只能设置响应
需要保持ThreadLocal
方案3:简单检查
当前线程
❌ 不能
只记录超时,不中断
方案4:ThreadLocal
当前线程
✅ 业务代码配合
需要业务代码主动检查
推荐选择
如果需要保持在当前线程执行(保留ThreadLocal、事务等),推荐方案2(监控线程):
业务逻辑在请求线程执行
监控线程只负责超时检测
超时后尝试设置响应(如果还没提交)
如果可以接受新线程,推荐方案1(Servlet异步):
这是Servlet标准做法
Jetty完美支持
性能最好
你的框架是怎样的架构?是否依赖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) {}
}