使用 AsynchronousSocketChannel 响应数据
tio-boot
的 HTTP 响应是异步的。如果你想在业务处理完发送 HTTP 响应后执行一些额外的操作,可以参考本节内容。
tio-boot 异步发送 HTTP 响应
1. 获取 ChannelContext
首先,从 HttpRequest
中获取 ChannelContext
,它包含了当前连接的状态和配置信息。
ChannelContext channelContext = request.getChannelContext();
2. 准备发送的数据
创建一个包含响应数据的 Map
,此处只是添加一个简单的键值对。
Map<String, Object> data = new HashMap<>();
data.put("code", 1);
3. 封装为 HttpResponse
使用 TioControllerContext.getResponse()
获取 HttpResponse
对象,然后用 Resps.json
方法将数据封装成 JSON 格式的响应。
HttpResponse httpResponse = TioControllerContext.getResponse();
httpResponse = Resps.json(httpResponse, data);
4. 调用 Handler 进行编码
获取 AioHandler
处理器,该处理器用于对数据进行编码和解码。调用 encode
方法将 httpResponse
编码为可以通过网络发送的字节数据。
TioConfig tioConfig = channelContext.getTioConfig();
AioHandler aioHandler = tioConfig.getAioHandler();
ByteBuffer writeBuffer = aioHandler.encode(httpResponse, tioConfig, channelContext);
5. 异步写入数据到客户端
使用 channelContext.asynchronousSocketChannel.write
异步地将编码后的数据写入客户端。同时传入 request
作为 attachment
,并定义 CompletionHandler
来处理写操作的结果。
channelContext.asynchronousSocketChannel.write(writeBuffer, request, new CompletionHandler<Integer, HttpRequest>() {
...
});
6. 处理写操作的结果
completed
方法:当写操作成功完成时调用此方法,记录操作结果,并关闭 AsynchronousSocketChannel
。
public void completed(Integer result, HttpRequest attachment) {
log.info("result:{},attachment:{}", result, attachment);
try {
channelContext.asynchronousSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
failed
方法:当写操作失败时调用此方法,尝试关闭 AsynchronousSocketChannel
。
public void failed(Throwable exc, HttpRequest attachment) {
try {
channelContext.asynchronousSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
这种方法展示了如何异步发送 HTTP 响应,并通过 CompletionHandler
处理操作结果。通过这种方式,服务器可以在不阻塞当前线程的情况下处理多个连接和请求,从而提高性能。
AsynchronousSocketChannel 的 write 方法
AsynchronousSocketChannel
的 write
方法用于异步写操作,它接收三个参数:
ByteBuffer src:
ByteBuffer
对象,包含要写入通道的数据。它提供了高效的数据读写接口,常用于 NIO 的数据输入输出。A attachment:
附件对象,可以是任何类型的数据,会被传递给CompletionHandler
的completed
或failed
方法。CompletionHandler<Integer,? super A> handler:
处理器接口。当异步操作完成时,无论成功还是失败,都会调用处理器的方法。completed
方法表示操作成功,failed
方法表示操作失败。
示例代码
下面是一个完整的示例代码,展示如何在 tio-boot
中异步发送 HTTP 响应。
package com.litongjava.tio.web.hello.controller;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.CompletionHandler;
import java.util.HashMap;
import java.util.Map;
import com.litongjava.tio.boot.http.TioControllerContext;
import com.litongjava.tio.core.ChannelContext;
import com.litongjava.tio.core.TioConfig;
import com.litongjava.tio.core.intf.AioHandler;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import com.litongjava.annotation.RequestPath;
import com.litongjava.tio.http.server.util.Resps;
import lombok.extern.slf4j.Slf4j;
@RequestPath("/async")
@Slf4j
public class AsyncController {
public Map<String, Object> index(HttpRequest request) {
ChannelContext channelContext = request.getChannelContext();
TioConfig tioConfig = channelContext.getTioConfig();
AioHandler aioHandler = tioConfig.getAioHandler();
String token = channelContext.getToken();
Map<String, Object> data = new HashMap<>();
data.put("channelContext", channelContext.toString());
data.put("tioConfig", tioConfig.toString());
data.put("userId", channelContext.userid);
data.put("token", token);
data.put("asynchronousSocketChannel", channelContext.asynchronousSocketChannel.toString());
data.put("aioHandler", aioHandler.toString());
log.info("data:{}", data);
return data;
}
public String writeHttpResponse(HttpRequest request) {
ChannelContext channelContext = request.getChannelContext();
// 准备发送的数据
Map<String, Object> data = new HashMap<>();
data.put("code", 1);
// 封装为 HttpResponse
HttpResponse httpResponse = TioControllerContext.getResponse();
httpResponse = Resps.json(httpResponse, data);
// 调用 Handler 进行编码
TioConfig tioConfig = channelContext.getTioConfig();
AioHandler aioHandler = tioConfig.getAioHandler();
ByteBuffer writeBuffer = aioHandler.encode(httpResponse, tioConfig, channelContext);
// 异步写入数据到客户端
channelContext.asynchronousSocketChannel.write(writeBuffer);
return null;
}
public String writeHttpResponseWithCompletionHandler(HttpRequest request) {
ChannelContext channelContext = request.getChannelContext();
// 准备发送的数据
Map<String, Object> data = new HashMap<>();
data.put("code", 1);
// 封装为 HttpResponse
HttpResponse httpResponse = TioControllerContext.getResponse();
httpResponse = Resps.json(httpResponse, data);
// 调用 Handler 进行编码
TioConfig tioConfig = channelContext.getTioConfig();
AioHandler aioHandler = tioConfig.getAioHandler();
ByteBuffer writeBuffer = aioHandler.encode(httpResponse, tioConfig, channelContext);
// 异步写入数据到客户端
channelContext.asynchronousSocketChannel.write(writeBuffer, request, new CompletionHandler<Integer, HttpRequest>() {
@Override
public void completed(Integer result, HttpRequest attachment) {
// 进行其他操作
log.info("result:{},attachment:{}", result, attachment);
// 手动关闭连接
try {
channelContext.asynchronousSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, HttpRequest attachment) {
try {
channelContext.asynchronousSocketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
return null;
}
}
访问测试
- 访问
http://localhost/async/index
:
{
"aioHandler": "com.litongjava.tio.boot.server.TioBootServerHandler@7a041eef",
"tioConfig": "ServerTioConfig [name=tio-boot]",
"channelContext": "server:0.0.0.0:80, client:0:0:0:0:0:0:0:1:4986",
"asynchronousSocketChannel": "sun.nio.ch.WindowsAsynchronousSocketChannelImpl[connected local=/0:0:0:0:0:0:0:1:80 remote=/0:0:0:0:0:0:0:1:4986]"
}
- 访问
http://localhost/async/writeHttpResponse
:
{ "code": 1 }
- 访问
http://localhost/async/writeHttpResponseWithCompletionHandler
:
{ "code": 1 }
以上代码展示了如何使用 AsynchronousSocketChannel
进行异步 HTTP 响应,并在发送响应后执行额外的业务逻辑。这种非阻塞式编程可以显著提升服务器的性能。