2015-01-16 13 views
5

Tôi có một ứng dụng khởi động mùa xuân đơn giản. Nó có một điểm cuối duy nhất lấy một đối tượng từ thân yêu cầu và không làm gì:Tránh nạn đói do khách hàng chậm trong tomcat/spring-boot

@Controller 
class FooController { 
    @RequestMapping(method=RequestMethod.POST, value="/foo") 
    public void postFoo(@RequestBody Foo foo) { 
    } 
} 

Những thứ khá đơn giản.

Sau đó tôi kết nối qua telnet và gửi qua các tiêu đề thích hợp như thể tôi sắp gửi một đối tượng mã hóa json, nhưng không bao giờ gửi yêu cầu - tôi chỉ để kết nối treo.

Chạy jstack, tôi có thể thấy rằng tomcat đã gửi yêu cầu đến mùa xuân. Mùa xuân đã gửi nó đến jackson. Jackson bị chặn trên NIO đang chờ nhiều dữ liệu hơn.

Thread 12128: (state = BLOCKED) 
- sun.misc.Unsafe.park(boolean, long) @bci=0 (Compiled frame; information may be imprecise) 
- java.util.concurrent.locks.LockSupport.parkNanos(java.lang.Object, long) @bci=20, line=226 (Compiled frame) 
... 
- org.apache.tomcat.util.net.NioEndpoint$KeyAttachment.awaitLatch(java.util.concurrent.CountDownLatch, long, java.util.concurrent.TimeUnit) @bci=18, line=1582 (Compiled frame) 
... 
- org.apache.tomcat.util.net.NioSelectorPool.read(java.nio.ByteBuffer, org.apache.tomcat.util.net.NioChannel, java.nio.channels.Selector, long) @bci=7, line=227 (Compiled frame) 
- org.apache.coyote.http11.InternalNioInputBuffer.readSocket(boolean, boolean) @bci=103, line=427 (Compiled frame) 
... 
- org.apache.catalina.connector.CoyoteInputStream.read(byte[], int, int) @bci=76, line=200 (Compiled frame) 
- com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper.ensureLoaded(int) @bci=49, line=503 (Compiled frame) 
... 
- com.fasterxml.jackson.databind.ObjectMapper.readValue(java.io.InputStream, com.fasterxml.jackson.databind.JavaType) @bci=6, line=2158 (Compiled frame) 
- org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(com.fasterxml.jackson.databind.JavaType, org.springframework.http.HttpInputMessage) @bci=11, line=225 (Compiled frame) 
... 
- org.springframework.web.servlet.FrameworkServlet.doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @bci=3, line=863 (Compiled frame) 
- javax.servlet.http.HttpServlet.service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @bci=149, line=646 (Compiled frame) 
- org.springframework.web.servlet.FrameworkServlet.service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @bci=32, line=837 (Compiled frame) 
- javax.servlet.http.HttpServlet.service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) @bci=30, line=727 (Compiled frame) 
- org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) @bci=446, line=303 (Compiled frame) 
... 
- org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(java.nio.channels.SelectionKey, org.apache.tomcat.util.net.NioEndpoint$KeyAttachment) @bci=140, line=1736 (Interpreted frame) 
- org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run() @bci=94, line=1695 (Compiled frame) 
- java.util.concurrent.ThreadPoolExecutor.runWorker(java.util.concurrent.ThreadPoolExecutor$Worker) @bci=95, line=1145 (Compiled frame) 
- java.util.concurrent.ThreadPoolExecutor$Worker.run() @bci=5, line=615 (Interpreted frame) 
- org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run() @bci=4, line=61 (Interpreted frame) 
- java.lang.Thread.run() @bci=11, line=745 (Interpreted frame) 

Vấn đề của tôi là nếu 200 người làm điều đó, thì hồ bơi của tôi bị bỏ đói và yêu cầu hợp pháp không thể đến. Có vẻ như đây là một cuộc tấn công DOS khá đơn giản chống lại bất cứ thứ gì chạy trên tomcat.

Tôi đoán có một cách để giải quyết vấn đề này bằng cách nhận Trình kết nối HTTP NIO để đọc trước bộ đệm của nó. Nếu vậy, làm thế nào tôi sẽ đi về việc thiết lập này?

Thậm chí sau đó, có vẻ như một tác nhân độc hại có thể dễ dàng mang dịch vụ xuống bằng cách chỉ gửi các đối tượng lớn đến đó. Làm thế nào để mọi người bình thường ngăn chặn đói đói khi khách hàng chậm, lỗi hoặc độc hại đang kết nối?

+0

Martin, tôi cần bạn đăng thêm ngăn xếp. Tôi có cảm giác như sợi chỉ bị khóa trên CountdownLatch đó hai lần. Tất cả các hoạt động đọc/ghi từ các ổ cắm không chặn là ... không chặn. Có nghĩa là một read() sẽ trả về dữ liệu hiện có sẵn và không chờ đợi nữa. –

+0

@Martin: Bạn đã nhận được giải pháp chưa? Vui lòng cung cấp giải pháp nếu có ai đó có thể giải quyết. –

Trả lời

0

Khám phá Stuck Thread Detection Valve trong Tomcat.

Từ các tài liệu:

van này cho phép phát hiện các yêu cầu mà phải mất một thời gian dài để xử lý, mà có thể chỉ ra rằng các chủ đề đó là xử lý nó gặp trở ngại. Ngoài ra, nó có thể tùy ý làm gián đoạn các chủ đề đó để thử và bỏ chặn chúng.

Khi yêu cầu như vậy được phát hiện, dấu vết ngăn xếp hiện tại của chủ đề được ghi vào nhật ký Tomcat với mức WARN.

Bạn có thể chỉ định số giây bạn muốn yêu cầu, tối đa (mặc định 10 phút!) Và van này sẽ tiêu diệt chúng nếu chúng vượt quá mức đó.

+0

Đó sẽ là một sự xấu hổ nếu đó là cách tốt nhất - nó có vẻ khá mong manh. Tôi đã thực sự hy vọng sẽ có một cách để đẩy một loạt các IO off vào thread chọn NIO. – Martin

Các vấn đề liên quan