从零开始学习Netty - 学习笔记 -Netty入门【Future & Promise】【Handler & Pipeline】

5.2.3.Future & Promise

在异步处理中,通常使用两个接口

首先说明,Netty中的FutureJDK中的Future的同名。但是两个接口,nettyFuture是继承JDKfuture

Promise又对NettyFuture进行了拓展

  • JDKFuture只能同步等待任务结束(成功或者失败)才能得到结果
  • Netty Future 可以同步等待任务结束得到结果,也就是异步的方式获取结果,但是都要等到任务结束
  • Netty Promise不仅有Netty Future的功能,而且脱离了任务独立存在,只作为两个线程传递结果的容器
特性FuturePromise
定义java.util.concurrent.FuturePromise是一种编程模式,通常由异步库提供。
创建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);

	}
}

image-20240228191132771

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());
			}
		});

	}
}

同步

image-20240228191621240

异步
image-20240228192103824

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());
	}
}

image-20240228202706072

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

入站

  1. 服务端

    1. 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);
      	}
      }
      
      
  2. 客户端

    1. 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);
      		}
      	}
      }
      
      
  3. 结果

    image-20240228204821522
    image-20240228204911022

5.2.3.1.inbound-handler

出站

  1. 服务端

    1. 注意 服务端 只有向channel写入数据才能触发write事件

    2. 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);
      	}
      }
      
  2. 服务端代码一致

  3. 结果

    1. image-20240228205757397
      image-20240228210345730

扩展: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()));
					}
				});

image-20240228211528765

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);
	}
}

image-20240228213038727

入站处理前前 加上出站处理器 再观察一下

				// 添加出战处理器
				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()));
					}
				});

image-20240228213616959

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!");

	}
}

image-20240228215403598

  • 33
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值