异步处理请求
This content is not available in your language yet.
如果你的请求里会做数据库查询、远程 HTTP 调用、文件处理或任何明显耗时的工作,就不应该一直把这些操作压在主处理流程里。
这篇文档讲的就是:什么时候需要异步,以及在 Feat 里应该怎么写。
先判断:你真的需要异步吗
Section titled “先判断:你真的需要异步吗”如果你的请求处理只是:
- 读取几个参数
- 拼一段字符串
- 返回一个很快就能得到的结果
那通常没必要上异步。
但如果你的处理链路里有这些动作,就值得认真看完这页:
- 数据库查询
- 第三方 API 调用
- 文件上传或转码
- 明显会阻塞的计算或等待
Feat 里的异步处理模型
Section titled “Feat 里的异步处理模型”Feat 的服务端异步处理不是魔法,它依赖的是 HttpHandler 里的另一个 handle(...) 入口:
void handle(HttpRequest request, CompletableFuture<Void> future)你要做的事只有两件:
- 把耗时逻辑挪到自己的线程池里执行
- 完成后记得调用
future.complete(null)
如果第二步忘了做,请求就会一直挂着。
一个最小但真实的异步示例
Section titled “一个最小但真实的异步示例”下面这个例子几乎就是仓库示例的核心写法:
import tech.smartboot.feat.core.server.HttpHandler;import tech.smartboot.feat.core.server.HttpRequest;import tech.smartboot.feat.core.server.HttpServer;
import java.io.IOException;import java.util.Date;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;
public class AsyncHttpDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors() );
HttpServer server = new HttpServer(); server.options().debug(true);
server.httpHandler(new HttpHandler() { @Override public void handle(HttpRequest request, CompletableFuture<Void> future) throws IOException { executorService.execute(() -> { try { Thread.sleep(1000); request.getResponse().write( ("currentThread: " + Thread.currentThread() + " at " + new Date()).getBytes() ); } catch (Exception e) { e.printStackTrace(); } finally { future.complete(null); } }); }
@Override public void handle(HttpRequest request) { // 异步模式下这里通常不再承担主逻辑 } });
server.listen(8080); }}这个例子里真正重要的部分
Section titled “这个例子里真正重要的部分”很多人会把注意力放在 Thread.sleep(1000) 这种演示代码上,但真正重要的是这三行:
executorService.execute(() -> { // 耗时工作 future.complete(null);});它们分别表达了:
- “主线程不要做这件事”
- “把工作交给另外的执行器”
- “做完后显式通知 Feat 这次请求已经可以结束”
运行之后会看到什么
Section titled “运行之后会看到什么”启动服务后访问 http://localhost:8080,你会在大约 1 秒后拿到响应。
响应内容里会带上执行线程信息,说明这段逻辑不是在主请求线程里同步跑完的。
这正是异步处理的价值:
- 请求入口更快释放
- 主处理线程不用被单个慢请求拖住
- 你可以用自己的线程池管理耗时任务
一个更接近真实项目的写法
Section titled “一个更接近真实项目的写法”如果你已经在使用 Feat.httpServer(...),也可以直接把异步处理器挂进去:
Feat.httpServer(options -> options.debug(true)) .httpHandler(new HttpHandler() { @Override public void handle(HttpRequest request, CompletableFuture<Void> future) { businessExecutor.execute(() -> { try { String result = callSlowService(); request.getResponse().write(result); } finally { future.complete(null); } }); }
@Override public void handle(HttpRequest request) { } }) .listen();这里最需要控制的是 businessExecutor,而不是 Feat 本身。
换句话说,异步处理真正的核心问题通常是你的业务线程池策略。
最容易犯的错误
Section titled “最容易犯的错误”1. 忘记 future.complete(null)
Section titled “1. 忘记 future.complete(null)”这是最典型的问题。
结果通常是:服务端看起来没报错,但客户端请求一直不结束。
2. 在异步线程里抛异常,却没有兜底
Section titled “2. 在异步线程里抛异常,却没有兜底”至少应该确保无论成功还是失败,都能结束 future:
executorService.execute(() -> { try { doSomething(request); } catch (Throwable e) { e.printStackTrace(); } finally { future.complete(null); }});3. 把所有异步任务都丢给无限增长的线程池
Section titled “3. 把所有异步任务都丢给无限增长的线程池”异步不是“越多线程越好”。
如果线程池不受控,最终只是把阻塞从主线程搬到了另一堆线程上。
什么时候应该继续往下看
Section titled “什么时候应该继续往下看”如果你已经确认自己需要异步,接下来通常会遇到两类问题:
- 路由开始复杂起来:去看 Router 路由组件
- 线程数、超时、缓冲区需要一起调:去看 ServerOptions 配置指南
如果你已经打算切到注解式开发模型,再回去看 Feat Cloud 导览 会更合适。