EmbeddedChannel的使用

169 阅读3分钟

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 进入测试