Pipeline
Netty에서 pipeline에는 channel Handler의 흐름을 기록하고 있다.
아래 코드를 보면
카페를 차리고 (Serverbootstrap),
문을 만들고, 카운터를 배치시키고 ( serverbootstrap.group(bossGroup, workGroup) )
어떤말로 할지 결정하고 ( serverbootstrap.channel(NioServerSocketChannel.class) )
그 다음에 손님이 왔을때 어떤 순서로 응대할지 정한 메뉴얼이 pipeline이다
LoggingHandler ▶ StringDecoder ▶ SampleClientHandler ▶ StringEncoder ▶ LoggingHandler 의 순서로 흐른다.
serverbootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
logger.info("손님 문열고 들어옴 .. ");
ChannelPipeline p = ch.pipeline();
p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new StringDecoder(CharsetUtil.UTF_8));
p.addLast(new SampleClientHandler());
p.addLast(new StringEncoder(CharsetUtil.UTF_8));
}
});
이걸 테스트 하기 위해선 카페를 차리고 ( Server ), 고객 입장을 시켜야 한다 (Client).
고되다. 디버깅도 힘들고 ..
Netty의 PipeLine 테스트를 위한 EmbededChannel
그래서 간단하게 가게는 차려주고 메뉴얼 순서만 테스트 할수 있게 만들어 놓은게 있다.
앞에서 이야기한 카페를 차리고, 문을 만들고, 어떤말로 할지 결정하는 부분은 간단하게 EmbededChannel로 묶어서 처리할수 있다.
EmbededExample.java 전체코드
- 프로젝트에 아래 jar 추가
- log4j2 관련
log4j-api-${lastest.version}.jar , log4j-core-${lastest.version}.jar, log4j-slf4j-impl-${lastest.version}.jar
- slf4j 관련
slf4j-api-${lastest.version}.jar
- netty 관련
netty-all-${lastest.version}.jar
그리고 이거 돌려보자
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.CharsetUtil;
public class EmbededExample {
final static Logger logger = LoggerFactory.getLogger(EmbededExample.class);
public static void main(String[] args) throws Exception {
EmbeddedChannel channel = new EmbeddedChannel(); //카페 차림
channel.pipeline().addLast(new LoggingHandler(LogLevel.INFO)); //들고 나가는 고객 로그 기록
channel.pipeline().addLast(new LineBasedFrameDecoder(80)); // 엔터(\n)키로 단락 분리하고 80byte가 최대 임
channel.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8)); // ByteBuf -> 문자열로 변경
channel.pipeline().addLast(new EchoServerHanlder()); // channelRead에 전달
channel.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8)); // channelRead에 받은 String을 ByteBuf 로 변경 후 LoggingHandler로 전달
ByteBuf buf = Unpooled.buffer();
buf.writeBytes("안녕 반가워\n".getBytes());
channel.writeInbound(buf); // 손님 들어간다
ByteBuf readBuff = channel.readOutbound(); // 손님 나온다
logger.info("readBuff={}",((ByteBuf)readBuff).toString(CharsetUtil.UTF_8));
channel.finish(); // 카페 닫는다
}
}
class EchoServerHanlder extends ChannelInboundHandlerAdapter {
final static Logger logger = LoggerFactory.getLogger(EmbededExample.class);
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
logger.info("channelActive..");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.info("channelRead..{}", msg);
ctx.channel().writeAndFlush(">> " + msg); // 덧붙혀서 리턴한다.
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
logger.info("channelInactive..");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
}
}
결과를 각 단위로 보면
ByteBuf buf = Unpooled.buffer();
buf.writeBytes("안녕 반가워\n".getBytes());
channel.writeInbound(buf); // 손님 들어간다
1) LoggingHandler ▶ StringDecoder ▶ SampleClientHandler 순으로 흘러서 channelRead 까지 도달했다
16:05:38 INFO [main] io.netty.handler.logging.LoggingHandler - [id: 0xembedded, L:embedded - R:embedded] READ: 17B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| ec 95 88 eb 85 95 20 eb b0 98 ea b0 80 ec 9b 8c |...... .........|
|00000010| 0a |. |
+--------+-------------------------------------------------+----------------+
16:05:38 INFO [main] EmbededExample - channelRead..안녕 반가워
다음은 나가는거다
ByteBuf readBuff = channel.readOutbound(); // 손님 나온다
logger.info("readBuff={}",((ByteBuf)readBuff).toString(CharsetUtil.UTF_8));
channel.finish(); // 카페 닫는다
SampleClientHandler 의 channelRead ▶ StringDecoder ▶ LoggingHandler 를 타고 흘러간다
16:05:38 INFO [main] EmbededExample - write...>> 안녕 반가워
16:05:38 INFO [main] io.netty.handler.logging.LoggingHandler - [id: 0xembedded, L:embedded - R:embedded] WRITE: 19B
+-------------------------------------------------+
| 0 1 2 3 4 5 6 7 8 9 a b c d e f |
+--------+-------------------------------------------------+----------------+
|00000000| 3e 3e 20 ec 95 88 eb 85 95 20 eb b0 98 ea b0 80 |>> ...... ......|
|00000010| ec 9b 8c |... |
+--------+-------------------------------------------------+----------------+
16:05:38 INFO [main] io.netty.handler.logging.LoggingHandler - [id: 0xembedded, L:embedded - R:embedded] FLUSH
16:05:38 INFO [main] io.netty.handler.logging.LoggingHandler - [id: 0xembedded, L:embedded - R:embedded] READ COMPLETE
16:05:38 INFO [main] EmbededExample - readBuff=>> 안녕 반가워
16:05:38 INFO [main] io.netty.handler.logging.LoggingHandler - [id: 0xembedded, L:embedded - R:embedded] CLOSE
16:05:38 INFO [main] io.netty.handler.logging.LoggingHandler - [id: 0xembedded, L:embedded ! R:embedded] INACTIVE
16:05:38 INFO [main] EmbededExample - channelInactive..
16:05:38 INFO [main] io.netty.handler.logging.LoggingHandler - [id: 0xembedded, L:embedded ! R:embedded] UNREGISTERED
이런식으로 Server , Client 구성하지 않고 간단하게 테스트를 해볼수 있다.
담에는 JUnit을 추가해서 main() 함수 만들지 않고 쉽게 테스트 할수 있는걸 알아보자
'프로그래밍 > EmbeddedChannel 이란?' 카테고리의 다른 글
EmbeddedChannel with JUnit - 2 (0) | 2022.01.10 |
---|---|
EmbeddedChannel 활용 (LineBasedFrameDecoder 테스트) (0) | 2022.01.10 |