1. 背景
netty服务器实际开发过程中,从通道对端读取数据和写入数据到通道,netty底层已经帮我们做了这部分工作,那我们在开发中需要做的就是解码、编写处理逻辑的handler、编码这些工作
这些工作开发完成后,如何测试呢,netty提供了一个专门的通道EmbeddedChannel嵌入式通道来进行测试
2. API
1. writeInBound(): 向通道写入数据
2. readInBound(): 从EmbeddedChannel中读取入栈数据
3. finish(): 结束EmbeddedChannel, 会调用通道的close()方法
3. 使用
3.1 利用EmbeddedChannel测试Handler 的生命周期
public class InHandlerDemo extends ChannelInboundHandlerAdapter {
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Logger.info("被调用:handlerAdded()");
super.handlerAdded(ctx);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Logger.info("被调用:handlerRemoved()");
super.handlerAdded(ctx);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
Logger.info("被调用:channelRegistered()");
super.channelRegistered(ctx);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Logger.info("被调用:channelActive()");
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Logger.info("被调用:channelInactive()");
super.channelInactive(ctx);
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
Logger.info("被调用: channelUnregistered()");
super.channelUnregistered(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Logger.info("被调用:channelRead()");
super.channelRead(ctx, msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
Logger.info("被调用:channelReadComplete()");
super.channelReadComplete(ctx);
}
}
@Test
public void testInHandlerProcessWithChannelInitializer() {
final InHandlerDemo inHandler = new InHandlerDemo();
//初始化处理器
ChannelInitializer initializer = new ChannelInitializer<EmbeddedChannel>() {
protected void initChannel(EmbeddedChannel ch) {
ch.pipeline().addLast(inHandler);
//ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
}
};
//创建嵌入式通道
EmbeddedChannel channel = new EmbeddedChannel(initializer);
ByteBuf buf = Unpooled.buffer();
buf.writeInt(1);
//模拟入站,写一个入站包
channel.writeInbound(buf);
channel.flush();
//模拟入站,再写一个入站包
channel.writeInbound(buf);
channel.flush();
//通道关闭
channel.close();
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
执行结果:
被调用:handlerAdded() : 处理器被加入流水线,该方法会被回调
被调用:channelRegistered() :
被调用:channelActive()
被调用:channelRead()
被调用:channelReadComplete()
被调用:channelRead()
被调用:channelReadComplete()
被调用:channelInactive()
被调用: channelUnregistered()
被调用:handlerRemoved()
3.2 EmbeddedChannel 测试 InboundHandler 入栈的流水线
static class SimpleInHandlerA extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Logger.info("入站处理器 A: 被回调 ");
super.channelRead(ctx, msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
Logger.info("入站处理器 A: 被回调 ");
// super.channelReadComplete(ctx);
ctx.fireChannelReadComplete(); //入站操作的传播
}
}
static class SimpleInHandlerB extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Logger.info("入站处理器 B: 被回调 ");
super.channelRead(ctx, msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
Logger.info("入站处理器 B: 被回调 ");
ctx.fireChannelReadComplete();//入站操作的传播
}
}
static class SimpleInHandlerC extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Logger.info("入站处理器 C: 被回调 ");
super.channelRead(ctx, msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
Logger.info("入站处理器 C: 被回调 ");
ctx.fireChannelReadComplete();//入站操作的传播
}
}
@Test
public void testPipelineInBound() {
ChannelInitializer channelInitializer = new ChannelInitializer<EmbeddedChannel>() {
protected void initChannel(EmbeddedChannel ch) {
//ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
ch.pipeline().addLast(new SimpleInHandlerA());
ch.pipeline().addLast(new SimpleInHandlerB());
ch.pipeline().addLast(new SimpleInHandlerC());
}
};
EmbeddedChannel channel = new EmbeddedChannel(channelInitializer);
ByteBuf buf = Unpooled.buffer();
buf.writeInt(1);
//向通道写一个入站报文
channel.writeInbound(buf);
ThreadUtil.sleepSeconds(Integer.MAX_VALUE);
}
执行结果:
入站处理器 A: 被回调
入站处理器 B: 被回调
入站处理器 C: 被回调
[main|InPipeline$SimpleInHandlerA.channelReadComplete] |> 入站处理器 A: 被回调
[main|InPipeline$SimpleInHandlerB.channelReadComplete] |> 入站处理器 B: 被回调
[main|InPipeline$SimpleInHandlerC.channelReadComplete] |> 入站处理器 C: 被回调
3.3 EmbeddedChannel 测试OutboundHandler 的出栈的流水线
public class SimpleOutHandlerA extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Logger.info("出站处理器 A: 被回调" );
super.write(ctx, msg, promise);
}
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
Logger.info("出站处理器 : 被回调" );
ctx.flush();
}
}
public class SimpleOutHandlerB extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Logger.info("出站处理器 B: 被回调" );
super.write(ctx, msg, promise);
}
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
Logger.info("出站处理器 : 被回调" );
ctx.flush();
}
}
public class SimpleOutHandlerC extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Logger.info("出站处理器 C: 被回调" );
super.write(ctx, msg, promise);
}
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
Logger.info("出站处理器 : 被回调" );
ctx.flush();
}
}
@Test
public void testPipelineOutBound() {
ChannelInitializer i = new ChannelInitializer<EmbeddedChannel>() {
protected void initChannel(EmbeddedChannel ch) {
ch.pipeline().addLast(new SimpleOutHandlerA());
ch.pipeline().addLast(new SimpleOutHandlerB());
ch.pipeline().addLast(new SimpleOutHandlerC());
}
};
EmbeddedChannel channel = new EmbeddedChannel(i);
ByteBuf buf = Unpooled.buffer();
buf.writeInt(1);
//向通道写一个出站报文
// channel.writeOutbound(buf);
channel.writeAndFlush(buf);
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
测试结果:
[main|OutPipeline$SimpleOutHandlerC.write] |> 出站处理器 C: 被回调
[main|OutPipeline$SimpleOutHandlerB.write] |> 出站处理器 B: 被回调
[main|OutPipeline$SimpleOutHandlerA.write] |> 出站处理器 A: 被回调
[main|OutPipeline$SimpleOutHandlerC.flush] |> 出站处理器 : 被回调
[main|OutPipeline$SimpleOutHandlerB.flush] |> 出站处理器 : 被回调
[main|OutPipeline$SimpleOutHandlerA.flush] |> 出站处理器 : 被回调
3.4 EmbeddedChannel测试整数解码器Byte2IntegerDecoder
public class Byte2IntegerDecoder extends ByteToMessageDecoder {
//钩子实现
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
while (in.readableBytes() >= 4) {
Integer anInt = in.readInt();
Logger.info("解码出一个整数: " + anInt);
out.add(anInt);
}
}
}
public class IntegerProcessHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Integer integer = (Integer) msg;
Logger.info("打印出一个整数: " + integer);
}
}
/**
* 整数解码器的使用实例
*/
@Test
public void testByteToIntegerDecoder() {
ChannelInitializer initializer = new ChannelInitializer<EmbeddedChannel>() {
protected void initChannel(EmbeddedChannel ch) {
ch.pipeline().addLast(new Byte2IntegerDecoder());
ch.pipeline().addLast(new IntegerProcessHandler());
}
};
EmbeddedChannel channel = new EmbeddedChannel(initializer);
for (int j = 0; j < 100; j++) {
ByteBuf buf = Unpooled.buffer();
buf.writeInt(j);
channel.writeInbound(buf);
}
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
4. 别的netty测试方法
当开启一个netty服务的时候,可以telnet ip port 进入测试