Jetty在上传多部分文件和连接中断时占用100%的CPU
我们目前正在使用嵌入式Jetty Server + Spring MVC上传文件。 当我使用curl手动上传文件并使用Ctrl ^ C故意破坏它时,CPU在服务器上达到100%并且仅重新启动应用程序将完成此问题。
curl -i -X POST -H“Content-Type:multipart / form-data”-F“param1 = bla”-F“file = @ a_file”http://myserver/bla/submit
我已经检查过这个问题的其他帖子,但通常它会通过更新Jetty API版本和Java 8来解决。我已经这样做了,但同样的问题正在发生。可能与春天有关吗?
的API:
以下是该方法的代码段:
@RequestMapping(
value = MY_PATH,
method = RequestMethod.POST)
public
@ResponseBody
void submitFile(
@NotNull @RequestParam("param1") String param1,
@NotNull @RequestParam("file") MultipartFile file) {
//No code gets executed
}
我已经使用Java Visual VM监视并找到导致问题的那个的转储:
"qtp529864074-68" - Thread t@68
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.FileDispatcherImpl.writev0(Native Method)
at sun.nio.ch.SocketDispatcher.writev(SocketDispatcher.java:51)
at sun.nio.ch.IOUtil.write(IOUtil.java:148)
at sun.nio.ch.SocketChannelImpl.write(SocketChannelImpl.java:504)
- locked <6ddac168> (a java.lang.Object)
at org.eclipse.jetty.io.ChannelEndPoint.flush(ChannelEndPoint.java:177)
at org.eclipse.jetty.io.WriteFlusher.flush(WriteFlusher.java:419)
at org.eclipse.jetty.io.WriteFlusher.write(WriteFlusher.java:313)
at org.eclipse.jetty.io.AbstractEndPoint.write(AbstractEndPoint.java:141)
at org.eclipse.jetty.server.HttpConnection$SendCallback.process(HttpConnection.java:735)
at org.eclipse.jetty.util.IteratingCallback.processing(IteratingCallback.java:241)
at org.eclipse.jetty.util.IteratingCallback.iterate(IteratingCallback.java:224)
at org.eclipse.jetty.server.HttpConnection.send(HttpConnection.java:509)
at org.eclipse.jetty.server.HttpChannel.sendResponse(HttpChannel.java:668)
at org.eclipse.jetty.server.HttpChannel.write(HttpChannel.java:722)
at org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:177)
at org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:163)
at org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:441)
at org.eclipse.jetty.util.ByteArrayISO8859Writer.writeTo(ByteArrayISO8859Writer.java:109)
at org.eclipse.jetty.server.Response.sendError(Response.java:606)
at org.eclipse.jetty.server.Response.sendError(Response.java:512)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:650)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1160)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1090)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213)
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:109)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:119)
at org.eclipse.jetty.server.Server.handle(Server.java:517)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:308)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:242)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:261)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)
at org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:75)
at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceAndRun(ExecuteProduceConsume.java:213)
at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:147)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:654)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:572)
at java.lang.Thread.run(Thread.java:745)
答案 0 :(得分:0)
查看密码转换文件,您会看到来自.flush()
电话的Response.sendError()
来电。这可能意味着您的POST失败(无论出于何种原因)并且尝试发送错误响应,但要么尚未完成,要么客户端尚未完成读取错误。
它甚至可能是sendError循环的结果(查找错误页面,使用/分派错误页面,并且错误页面本身有错误导致另一个sendError触发)
至于Multipart方面,您的SpringMVC设置似乎存在问题,因为Jetty在处理Multipart上传内容方面没有问题。
在Github上为这个问题创建了这个答案的示例项目。
使用Servlet API在Jetty中执行此操作的示例。
@MultipartConfig(location="/tmp/upload",
fileSizeThreshold=1024*1024,
maxFileSize=1024*1024*50)
@WebServlet(urlPatterns={"/upload"}, name="upload")
public class UploadServlet extends HttpServlet
{
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setContentType("text/plain");
PrintWriter out = resp.getWriter();
int i=0;
for(Part part: req.getParts())
{
out.printf("Got part: name=%s, size=%d%n",part.getName(), part.getSize());
part.write(String.format("part-%02d.dat",i++));
}
}
}
如果我们在Jetty发行版中部署的war文件中运行它,然后使用Curl测试它,您将看到以下内容......
$ curl -i -X POST -H "Content-Type: multipart/form-data" \
-F "param1=bla" -F "file=@big.tar.gz" \
http://localhost:8080/upload
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
Content-Type: text/plain;charset=iso-8859-1
Content-Length: 65
Server: Jetty(9.3.6.v20151106)
Got part: name=file, size=23597555
Got part: name=param1, size=3
$ ls -la /tmp/upload/
total 23052
drwxrwxr-x. 2 joakim joakim 80 Jan 26 14:19 .
drwxrwxrwt. 20 root root 500 Jan 26 14:19 ..
-rw-rw-r--. 1 joakim joakim 23597555 Jan 26 14:19 part-00.dat
-rw-rw-r--. 1 joakim joakim 3 Jan 26 14:19 part-01.dat
$ sha1sum /tmp/upload/part-00.dat big.tar.gz
13be13872d0203bae2f8a83ad457f8a0d259a2db /tmp/upload/part-00.dat
13be13872d0203bae2f8a83ad457f8a0d259a2db big.tar.gz
$ file /tmp/upload/part-01.dat
/tmp/upload/part-01.dat: ASCII text, with no line terminators
$ cat /tmp/upload/part-01.dat
bla