5.2.3.Future & Promise
在异步处理中,通常使用两个接口
首先说明,
Netty
中的Future
和JDK
中的Future
的同名。但是两个接口,netty
的Future
是继承JDK
的future
。
Promise
又对Netty
的Future
进行了拓展
JDK
的Future
只能同步等待任务结束(成功或者失败)才能得到结果Netty
的Future
可以同步等待任务结束得到结果,也就是异步的方式获取结果,但是都要等到任务结束Netty Promise
不仅有Netty Future
的功能,而且脱离了任务独立存在
,只作为两个线程传递结果的容器
特性 | Future | Promise |
---|---|---|
定义 | java.util.concurrent.Future | Promise是一种编程模式,通常由异步库提供。 |
创建 | ExecutorService.submit(Callable) | 异步库提供Promise() 或类似函数来创建Promise对象。 |
获取结果 | Future.get() | 通过回调函数或链式调用来获取结果。 |
设置结果 | 不适用 | Promise.resolve(value) 用于设置解析结果。 |
处理异常 | 通过捕获ExecutionException 处理异常。 | Promise.catch(onRejected) 用于捕获和处理异常。 |
等待完成 | Future.isDone() | Promise对象提供链式调用,可等待异步操作完成。 |
取消任务 | Future.cancel(boolean mayInterruptIfRunning) | Promise通常不提供取消功能,一旦创建通常不可取消。 |
组合多个任务 | CompletableFuture.thenCombine() | Promise.all()或类似方法可组合多个Promise对象。 |
同步等待结果 | 不适用 | 可以使用Promise.sync() 等待结果的同步。 |
获取任务结果 | 不适用 | 可以使用getNow 获取任务结果,非阻塞,还未产生结果时,返回null |
异步等待结果 | 不适用 | 可以使用Promise.await() 异步等待结果。 |
添加监听器 | 不适用 | 可以使用Promise.addListener() 添加监听器。 |
链式调用 | 不适用 | Promise对象支持链式调用,简化异步操作处理。 |
设置成功 | 不适用 | 可以使用 setSuccess() 设置成功结果 |
设置失败 | 不适用 | 可以使用 setFailure() 设置失败结果 |
判断是否成功 | 不适用 | 可以使用 isSucess 判断任务是否成功 |
获取失败信息 | 不适用 | 可以使用cause 获取失败信息,非阻塞,如果没有失败,返回null |
5.2.3.1.JDK中的Future
public class JDKFutureTest {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Test
@DisplayName("测试JDK的Future")
public void test() throws ExecutionException, InterruptedException {
// 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 提交任务(future 主要用于在线程中传递数据)
Future<Integer> future = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
logger.error("执行计算!");
Thread.sleep(500);
return 50;
}
});
logger.error("等待结果!");
// 主线程通过 future 来获取结果
// 此时主线程会阻塞 会等待上面线程执行完 获取结果才放开
Integer i = future.get();
logger.error("获取的结果:{}", i);
}
}
5.2.3.2.Netty中的Future
public class NettyFutureTest {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static void main(String[] args) throws ExecutionException, InterruptedException {
NioEventLoopGroup group = new NioEventLoopGroup();
// 获取EventLoop
EventLoop eventLoop = group.next();
Future<Integer> future = eventLoop.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
logger.error("执行计算!");
Thread.sleep(500);
return 90;
}
});
// logger.error("等待计算!");
// Integer i = future.get();
// logger.error("获取的结果:{}", i);
// 变成异步
future.addListener(new GenericFutureListener<Future<? super Integer>>() {
@Override
public void operationComplete(Future<? super Integer> future) throws Exception {
logger.error("接收结果:{}",future.getNow());
}
});
}
}
同步
异步
5.2.3.3.Netty中的Promise
public class PromiseTest {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Test
@DisplayName("测试netty Promise")
public void test() throws ExecutionException, InterruptedException {
// 准备好eventLoop对象
NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
EventLoop eventLoop = eventExecutors.next();
// 可以主动创建Promise(实际上就是一个结果的容器)
DefaultPromise<Integer> promise = new DefaultPromise<Integer>(eventLoop);
// 模拟计算
new Thread(() -> {
logger.error("开始计算.....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 设置成功的值
promise.setSuccess(9999);
}).start();
logger.error("等待结果.....");
logger.error("获取结果.....{}", promise.get());
}
}
5.2.3.Handler & Pipeline
ChannelHandler是用来处理Channel上的各种事件
入站
,出战
,所有的ChannelHandler被连成一串就是Pipeline
- 入站的处理器通常是 ChannelnboundHandlerAdapter的子类:主要用来读取客户端数据,写回结果
- 出战的处理器通常是ChannelOutboundHandlerAdapter的子类:主要对写回的结果进行加工
服务端代码顺序
// ServerBootstrap 是一个启动 NIO 服务的辅助启动类
// 1.负责组装Netty组装组件,启动服务器
new ServerBootstrap()
.group(new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.childHandler(
new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.info("server receive msg: {}", msg);
}
});
}
}).bind(8080);
5.2.3.1.inbound-handler
入站
-
服务端
-
public class PipelineServerTest { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); public static void main(String[] args) { // ServerBootstrap 是一个启动 NIO 服务的辅助启动类 // 1.负责组装Netty组装组件,启动服务器 new ServerBootstrap().group(new NioEventLoopGroup()).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) throws Exception { // 1.通过channel 拿到 pipeline ChannelPipeline pipeline = ch.pipeline(); // 2.添加处理器 head -> h1 -> h2 -> h3 -> tail // 底层就是一个双向链表 pipeline.addLast("h1", new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { logger.error("1"); super.channelRead(ctx, msg); } }); pipeline.addLast("h2", new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { logger.error("2"); super.channelRead(ctx, msg); } }); pipeline.addLast("h3", new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { logger.error("3"); super.channelRead(ctx, msg); } }); } }).bind(8080); } }
-
-
客户端
-
public class PipelineClient { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); public static void main(String[] args) { // 1.创建启动器 try { ChannelFuture channelFuture = new Bootstrap() .group(new NioEventLoopGroup()) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) throws Exception { ch.pipeline().addLast(new StringEncoder()); } }) .connect(new InetSocketAddress("localhost", 8080)); channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { if (channelFuture.isSuccess()) { // 7.2.获取连接对象 如果没有调用sync()方法,这里的channel就会是null Channel channel = channelFuture.channel(); // 连接对象 logger.error("channel: {}", channel); // 8.向服务器发送数据 channel.writeAndFlush("hello, pipeline!"); } else { // 7.3.连接失败 Throwable cause = channelFuture.cause(); logger.error("connect failed: {}", cause); } } }); } catch (Exception e) { throw new RuntimeException(e); } } }
-
-
结果
5.2.3.1.inbound-handler
出站
-
服务端
-
注意 服务端 只有向channel写入数据才能触发write事件
-
public class PipelineServerTest { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); public static void main(String[] args) { // ServerBootstrap 是一个启动 NIO 服务的辅助启动类 // 1.负责组装Netty组装组件,启动服务器 new ServerBootstrap().group(new NioEventLoopGroup()).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) throws Exception { // 1.通过channel 拿到 pipeline ChannelPipeline pipeline = ch.pipeline(); // 2.添加处理器 head -> out-1 -> out-2 -> out-3 -> tail // 底层就是一个双向链表 (出战处理器主要重写的是write方法) // 出战处理器只有向channel写入数据 才会触发 pipeline.addLast("read-1", new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { logger.error("read-1"); super.channelRead(ctx, msg); // 向服务端中的channel写入数据 ch.write(ctx.alloc().buffer().writeBytes("hello".getBytes())); } }); pipeline.addLast("out-1", new ChannelOutboundHandlerAdapter() { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { logger.error("out-1"); super.write(ctx, msg, promise); } }); pipeline.addLast("out-2", new ChannelOutboundHandlerAdapter() { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { logger.error("out-2"); super.write(ctx, msg, promise); } }); pipeline.addLast("out-3", new ChannelOutboundHandlerAdapter() { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { logger.error("out-3"); super.write(ctx, msg, promise); } }); } }).bind(8080); } }
-
-
服务端代码一致
-
结果
扩展:handler之间传递数据
如果不调用super.channelRead() 那么链就断了 ,或者调用ctx.fireChannelRead(name) 二选一
pipeline.addLast("read-1", new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// channel中传递消息
logger.error("read-1");
ByteBuf buf = (ByteBuf) msg;
String name = buf.toString(Charset.defaultCharset());
logger.error("read-1 name:{}",name);
// 将name加点数据
name += "-add";
// 将数据传递给下一个handler
// 将数据传递给下一个handler
// 如果不调用super.channelRead() 那么链就断了
// 要么调用 ctx.fireChannelRead()
// ctx.fireChannelRead(name); // 将数据传入下一个handler
super.channelRead(ctx,name);
super.channelRead(ctx,name);
}
});
pipeline.addLast("read-2", new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.error("read-2");
logger.error("read-2 msg:{}",msg);
// channelReads(ctx,msg) 是唤醒入栈处理器
super.channelRead(ctx, msg);
// 向服务端中的channel写入数据
ch.write(ctx.alloc().buffer().writeBytes("hello".getBytes()));
}
});
5.2.3.3 ctx.writeAndFlush
注意 这里我们使用的了
head -> read-1 -> read-2 -> out-1 -> tail
ctx.writeAndFlush(ctx.alloc().buffer().writeBytes("hello".getBytes()));
写入数据ctx.writeAndFlush 从当前处理器 向前找 出战处理器 (当前是read-2)前面没有 ,所以没有找到
不是
ch.writeAndFlush(ctx.alloc().buffer().writeBytes("hello".getBytes()));
channel是从 尾巴找 从tail找
public class PipelineServerTest {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static void main(String[] args) {
// ServerBootstrap 是一个启动 NIO 服务的辅助启动类
// 1.负责组装Netty组装组件,启动服务器
new ServerBootstrap().group(new NioEventLoopGroup()).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
// 1.通过channel 拿到 pipeline
ChannelPipeline pipeline = ch.pipeline();
// 2.添加处理器 head -> out-1 -> out-2 -> out-3 -> tail
pipeline.addLast("read-1", new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// channel中传递消息
logger.error("read-1");
super.channelRead(ctx, msg);
}
});
pipeline.addLast("read-2", new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.error("read-2");
logger.error("read-2 msg:{}", msg);
// channelReads(ctx,msg) 是唤醒入栈处理器
super.channelRead(ctx, msg);
// 向服务端中的channel写入数据 触发 出战处理器
// ch.writeAndFlush(ctx.alloc().buffer().writeBytes("hello".getBytes()));
// *** 注意
ctx.writeAndFlush(ctx.alloc().buffer().writeBytes("hello".getBytes()));
}
});
// 添加出战处理器
pipeline.addLast("out-1", new ChannelOutboundHandlerAdapter() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
logger.error("out-1");
super.write(ctx, msg, promise);
}
});
}
}).bind(8080);
}
}
入站处理前前 加上出站处理器 再观察一下
// 添加出战处理器
pipeline.addLast("out-2", new ChannelOutboundHandlerAdapter() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
logger.error("out-2");
super.write(ctx, msg, promise);
}
});
pipeline.addLast("read-2", new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.error("read-2");
logger.error("read-2 msg:{}", msg);
// channelReads(ctx,msg) 是唤醒入栈处理器
super.channelRead(ctx, msg);
// 向服务端中的channel写入数据 触发 出战处理器
// ch.writeAndFlush(ctx.alloc().buffer().writeBytes("hello".getBytes()));
// *** 注意
ctx.writeAndFlush(ctx.alloc().buffer().writeBytes("hello".getBytes()));
}
});
5.2.3.4.embedded-channel
EmbeddedChannel是一个测试用的channel,可以用来测试pipeline 它不需要连接到远程服务器 也不需要绑定端口 只需要添加处理器即可
public class TestEmbeddedChannel {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static void main(String[] args) {
ChannelInboundHandlerAdapter read1 = new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.error("read-1");
super.channelRead(ctx, msg);
}
};
ChannelInboundHandlerAdapter read2 = new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.error("read-2");
super.channelRead(ctx, msg);
}
};
ChannelOutboundHandlerAdapter out1 = new ChannelOutboundHandlerAdapter() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
logger.error("out-1");
super.write(ctx, msg, promise);
}
};
ChannelOutboundHandlerAdapter out2 = new ChannelOutboundHandlerAdapter() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
logger.error("out-2");
super.write(ctx, msg, promise);
}
};
// 通过EmbeddedChannel测试pipeline
//
EmbeddedChannel embeddedChannel = new EmbeddedChannel(read1, out1, read2, out2);
embeddedChannel.writeInbound("hello, pipeline!");
embeddedChannel.writeOutbound("hello, netty!");
}
}