Servlet 规范里「一个实例,多线程」「请求对象只在一次请求内有效」「init 失败怎么办」这些说法,在真实容器里具体长什么样?这篇用嵌入式 Jetty 逐项实测。
[order]
启动阶段(loadOnStartup:order0 = 0,order1 = 1,order2 = 2,lazy 不设置):
[ 200 ms][main] server.start() 之前
[ 218 ms][main] ServletContextListener.contextInitialized
[ 220 ms][main] S-lazy.init()
[ 220 ms][main] S-order0.init()
[ 220 ms][main] S-order1.init()
[ 220 ms][main] S-order2.init()
[ 227 ms][main] server.start() 返回
第 1 次请求 /lazy(servlet 实例注册):200 S-lazy ok
第 1 次请求 /bycls(按类注册,没设 loadOnStartup):200 LazyCls ok
第 2 次请求 /bycls:200 LazyCls ok
请求阶段:
[ 277 ms][qtp1295083508-N] S-lazy.service(GET /lazy)
[ 285 ms][qtp1295083508-N] LazyCls.init()
请求不存在的路径 /nope:404 <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 404 Not Found</title> </head> <body> <h2>HTTP ERROR 404 Not Found</h2> <table> <tr><th>URI:</th><td>http://127.0.0.1:57096/nope</td></tr> <tr><th>STATUS:</th><td>404</td></tr> <tr><th>MESSAGE:</th><td>Not Found</td></tr> </table> <hr/><a href="https://jetty.org/">Powered by Jetty:// 12.0.16</a><hr/> </body> </html>
停止阶段:
[ 291 ms][main] server.stop() 之前
[ 293 ms][main] S-order0.destroy()
[ 293 ms][main] S-lazy.destroy()
[ 293 ms][main] S-order1.destroy()
[ 293 ms][main] S-order2.destroy()
[ 293 ms][main] LazyCls.destroy()
[ 294 ms][main] ServletContextListener.contextDestroyed
[ 294 ms][main] server.stop() 返回 [shared]
64 个并发请求,每个把 4 个计数器各加 2000 次(期望值 128000),成功 64 个
非同步 int:127814(丢失 186);volatile int:127807(丢失 193);synchronized int:128000;AtomicInteger:128000
servlet 实例数(identityHashCode 的种类):1;init() 调用次数:1;处理请求的线程名种类:[qtpN-N] [stale]
请求 /first?k=A:200 uri=/first param=A
请求完成之后,通过保存的引用读 getRequestURI():NullPointerException: Cannot invoke "org.eclipse.jetty.server.Request.getHttpURI()" because the return value of "org.eclipse.jetty.ee10.servlet.ServletApiRequest.getRequest()" is null
读 getParameter("k"):A
读 getMethod():NullPointerException: Cannot invoke "org.eclipse.jetty.server.Request.getMethod()" because the return value of "org.eclipse.jetty.ee10.servlet.ServletApiRequest.getRequest()" is null
往保存的 response 里写:没有抛异常;isCommitted = IllegalStateException: Request/Response does not exist (likely recycled)
再请求 /second?k=B:200 uri=/second param=B
此时再读保存的第一个请求引用的 getRequestURI():NullPointerException: Cannot invoke "org.eclipse.jetty.server.Request.getHttpURI()" because the return value of "org.eclipse.jetty.ee10.servlet.ServletApiRequest.getRequest()" is null [initfail]
/fail 第 1 次请求(init 抛 ServletException):500 <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 500 jakarta.servlet.ServletException: init ??</title> </head> <body> <h2>HTTP ERROR 500 jakarta.servlet.ServletException: init ??</h2> <table> <tr><th>URI:</th><td>http://127.0.0.1:57216/fail</td></tr> <tr><th>STATUS:</th><td>500</td></tr> <tr><th>MESSAGE:</th><td>jakarta.servlet.ServletException: init ??</td></tr> </table> <hr/><a href="https://jetty.org/">Powered by Jetty:// 12.0.16</a><hr/> </body> </html>
/fail 第 2 次请求(init 抛 ServletException):404 <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 404 Not Found</title> </head> <body> <h2>HTTP ERROR 404 Not Found</h2> <table> <tr><th>URI:</th><td>http://127.0.0.1:57216/fail</td></tr> <tr><th>STATUS:</th><td>404</td></tr> <tr><th>MESSAGE:</th><td>Not Found</td></tr> </table> <hr/><a href="https://jetty.org/">Powered by Jetty:// 12.0.16</a><hr/> </body> </html>
/fail 第 3 次请求(init 抛 ServletException):404 <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 404 Not Found</title> </head> <body> <h2>HTTP ERROR 404 Not Found</h2> <table> <tr><th>URI:</th><td>http://127.0.0.1:57216/fail</td></tr> <tr><th>STATUS:</th><td>404</td></tr> <tr><th>MESSAGE:</th><td>Not Found</td></tr> </table> <hr/><a href="https://jetty.org/">Powered by Jetty:// 12.0.16</a><hr/> </body> </html>
init 日志:
[ 276 ms][qtp1295083508-N] FailLazy.init() 第 1 次
/temp(第 1 次 init 抛 UnavailableException,暂时 1 秒):503 <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 503 Service Unavailable</title> </head> <body> <h2>HTTP ERROR 503 Service Unavailable</h2> <table> <tr><th>URI:</th><td>http://127.0.0.1:57216/temp</td></tr> <tr><th>STATUS:</th><td>503</td></tr> <tr><th>MESSAGE:</th><td>Service Unavailable</td></tr> </table> <hr/><a href="https://jetty.org/">Powered by Jetty:// 12.0.16</a><hr/> </body> </html>
立即再请求:503 <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 503 Service Unavailable</title> </head> <body> <h2>HTTP ERROR 503 Service Unavailable</h2> <table> <tr><th>URI:</th><td>http://127.0.0.1:57216/temp</td></tr> <tr><th>STATUS:</th><td>503</td></tr> <tr><th>MESSAGE:</th><td>Service Unavailable</td></tr> </table> <hr/><a href="https://jetty.org/">Powered by Jetty:// 12.0.16</a><hr/> </body> </html>
1.3 秒之后再请求:200 temp ok
init 日志:
[ 291 ms][qtp1295083508-N] TempLazy.init() 第 1 次
/perm 第 1 次请求(UnavailableException 永久):404 <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 404 Not Found</title> </head> <body> <h2>HTTP ERROR 404 Not Found</h2> <table> <tr><th>URI:</th><td>http://127.0.0.1:57216/perm</td></tr> <tr><th>STATUS:</th><td>404</td></tr> <tr><th>MESSAGE:</th><td>Not Found</td></tr> </table> <hr/><a href="https://jetty.org/">Powered by Jetty:// 12.0.16</a><hr/> </body> </html>
/perm 第 2 次请求(UnavailableException 永久):404 <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 404 Not Found</title> </head> <body> <h2>HTTP ERROR 404 Not Found</h2> <table> <tr><th>URI:</th><td>http://127.0.0.1:57216/perm</td></tr> <tr><th>STATUS:</th><td>404</td></tr> <tr><th>MESSAGE:</th><td>Not Found</td></tr> </table> <hr/><a href="https://jetty.org/">Powered by Jetty:// 12.0.16</a><hr/> </body> </html>
init 日志:
[ 1614 ms][qtp1295083508-N] PermLazy.init() 第 1 次
另起一个服务器:loadOnStartup = 1 的 servlet 在 init 里抛 ServletException,server.start():失败 ServletException: 启动时失败
日志:
[ 1629 ms][main] S-other.init()
[ 1629 ms][main] eager.init()(loadOnStartup = 1)
server.isStarted() = false;isFailed() = true [stop]
客户端收到:ConnectException: null
事件顺序:
[ 276 ms][qtp1295083508-N] slow.service 开始
[ 538 ms][main] server.stop() 调用(stopTimeout = 500 ms,请求还要约 1.7 s)
[ 1061 ms][main] slow.destroy()
[ 1573 ms][qtp1295083508-N] slow.service 被中断
[ 1573 ms][qtp1295083508-N] slow.service 结束
[ 1584 ms][main] server.stop() 抛 TimeoutException
[ 1584 ms][main] server.stop() 返回(耗时 1045 ms)
客户端收到:200 slow2 done
stopTimeout 足够长时的事件顺序:
[ 1591 ms][qtp20049680-N] slow2.service 开始
[ 1899 ms][main] server.stop() 调用(stopTimeout = 5000 ms,请求还要约 0.7 s)
[ 2591 ms][qtp20049680-N] slow2.service 结束
[ 2599 ms][main] slow2.destroy()
[ 2600 ms][main] server.stop() 返回(耗时 700 ms) [async]
线程池最多 5 个线程(其中一部分被 acceptor / selector 占用),8 个并发请求,每个耗时 500 ms,走 异步(startAsync):成功 8 个,总耗时 601 ms
线程池最多 5 个线程(其中一部分被 acceptor / selector 占用),8 个并发请求,每个耗时 500 ms,走 同步(sleep):成功 8 个,总耗时 2041 ms
单个异步请求的事件顺序:200 async done
[ 2873 ms][qtp242355057-N] service 开始(startAsync)
[ 2874 ms][qtp242355057-N] service 返回(请求线程释放)
[ 3382 ms][my-async-pool] complete()