如何构建原生非阻塞Servlet及实现响应式服务相关问题咨询
问题1 认知确认与响应式服务底层要求
首先你的部分认知存在偏差:
- Servlet 3.1规范确实引入了非阻塞IO的支持,但不是所有Servlet默认就具备非阻塞处理能力,需要手动开启异步支持、按非阻塞模式编码才能生效
- 不需要必须部署在Netty上,Tomcat 8+、Jetty 9+等主流Servlet容器都已经支持Servlet 3.1非阻塞特性;Netty本身不是标准Servlet容器,它是响应式框架常用的底层异步运行时
- 要实现真正的响应式服务,必须使用非阻塞的请求处理运行时(不管是Servlet容器还是Netty这类异步运行时):响应式的核心优势是用少量线程承载高并发,要是容器层本身是阻塞模型(每请求绑定一个工作线程,请求处理完才释放线程),哪怕上层用了响应式编程框架,线程还是会被阻塞占用,完全发挥不出响应式的优势。同时要注意全链路不能有阻塞操作(比如同步JDBC调用、同步HTTP调用),否则还是会阻塞线程。
问题2 非阻塞Servlet与普通Servlet的区别及实现
核心区别
普通HttpServlet是阻塞IO模型:容器为每个请求分配一个独立的工作线程,从请求进入到响应返回的整个周期,该线程都会被占用,哪怕业务逻辑在等待IO(数据库查询、第三方接口调用),线程也无法释放处理其他请求,并发上限受线程池大小限制。
非阻塞Servlet基于Servlet 3.1的AsyncContext、ReadListener、WriteListener实现:请求进入后拿到异步上下文就可以释放容器工作线程,请求体读取、响应体写入、业务处理都通过回调方式执行,线程不会被空占,少量线程就能支撑大量并发请求。
具体实现形式
非阻塞Servlet不需要额外继承特殊类,还是可以继承HttpServlet,核心是开启异步支持+按回调模式编写读写逻辑,最简示例如下:
import javax.servlet.*; import javax.servlet.annotation.WebServlet; import javax.servlet.http.*; import java.io.IOException; import java.nio.charset.StandardCharsets; @WebServlet(urlPatterns = "/non-blocking-demo", asyncSupported = true) public class NonBlockingDemoServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 开启异步上下文,当前容器工作线程即可释放回线程池 AsyncContext asyncContext = request.startAsync(); // 设置异步请求超时时间 asyncContext.setTimeout(10000); ServletInputStream inputStream = request.getInputStream(); // 注册读监听器,非阻塞读取请求数据 inputStream.setReadListener(new ReadListener() { @Override public void onDataAvailable() throws IOException { // 仅当有可用数据时触发,读取过程不会阻塞 byte[] buf = new byte[256]; while (inputStream.isReady() && !inputStream.isFinished()) { inputStream.read(buf); // 自行处理读取到的请求数据 } } @Override public void onAllDataRead() throws IOException { // 请求数据全部读取完成后执行业务逻辑 response.setContentType("text/plain;charset=utf-8"); ServletOutputStream outputStream = response.getOutputStream(); // 注册写监听器,非阻塞写回响应 outputStream.setWriteListener(new WriteListener() { @Override public void onWritePossible() throws IOException { while (outputStream.isReady()) { outputStream.write("非阻塞Servlet请求处理完成".getBytes(StandardCharsets.UTF_8)); // 标记请求处理完成 asyncContext.complete(); break; } } @Override public void onError(Throwable throwable) { asyncContext.complete(); throwable.printStackTrace(); } }); } @Override public void onError(Throwable throwable) { asyncContext.complete(); throwable.printStackTrace(); } }); } }
常用框架实现参考
- Spring栈:如果要基于Servlet栈做异步处理,可以使用Spring MVC提供的
DeferredResult、Callable异步返回值;要做全栈响应式可以直接使用Spring WebFlux,默认运行在Netty上,也支持部署在符合Servlet 3.1规范的容器中。 - MicroProfile栈:Quarkus的响应式能力基于Vert.x实现,可以直接使用
@Route注解编写响应式接口,配合Reactive REST Client、Reactive Hibernate实现全链路非阻塞;KumuluzEE、OpenLiberty等实现也都支持MicroProfile Reactive相关规范,按官方规范编码即可。
内容的提问来源于stack exchange,提问作者Jordi
相关产品推荐
相关产品推荐

