[Network/Netty]07. Netty 구축을 위한 beans(3) - ChannelPipeline

(출처 : http://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html
           
http://ash84.net/2013/03/08/exs4j-netty-channelpipeline--ec-97-90--eb-8c-80-ed-95-9c--ec-9d-b4-ed-95-b4/)


1. ChannelPipeline
    
public abstract interface ChannelPipline extends Iterable<Map.Entry<String, ChannelHandler>>
    ChannelHandler들을 모아놓은 일련의 목록.
    
이벤트가 발생됨에 따라 내부에 등록된 ChannelHandler들을 동작하기 위한 관리자라고 볼 수 있다.


2. 동작원리

    • Preview
      입출력 이벤트는 ChannelInoundHander, ChannelOutboundHandler에 의하여 다루어진다.
      또한 그 이벤트는 ChannelHandlerContext에서 정의된 이벤트 전달 method를 호출하여 근접한 핸들러에 전달된다.
    • Inbound event
      외부로부터 무언가를 받았다는 의미인 만큼 inbound handler의 최초 시점은 Netty의 내부에 있는 thread이다.
      부르기 편하게 그냥 네티라 하겠음. 네티가 외부에서 받은 어떤 데이터를 읽었을 것이다.(SocketChannel.read(...))
      pipeline에서 inbound handler를 등록했다면, 등록된 순서대로 데이터를 처리하고 다른 inbound handler에 전달한다.
      등록된 모든 inbound event가 끝나게 되면 inbound event는 종료되고 log에 남게된다.
    • Outbound event
      최초 시점은 가장 마지막에 등록된 outbound handler다.
      자신이 외부에 무언가를 보낸다는 의미이기 때문에,
      inbound event와 달리 등록된 outbound event를 모두 거치고
      네티에서 마무리를 짓게된다.(SocketChannel.write(...))

      두 이벤트를 등록한 아래의 예제를 볼 때, 진행되는 순서는 125543이다.
      (5번의 경우 Inbound, Outbound를 모두 상속받은 bean이라 본다.)
1
2
3
4
5
6
 ChannelPipeline p = ...;
 p.addLast("1"new InboundHandlerA());
 p.addLast("2"new InboundHandlerB());
 p.addLast("3"new OutboundHandlerA());
 p.addLast("4"new OutboundHandlerB());
 p.addLast("5"new InboundOutboundHandlerX());
cs


3. 실제 서버 구현
    아래 예제에서, 
다른 내용은 다 무시하고 실제 pipeline에 등록된 내용에 집중한다.
    ByteArrayDecoder, ByteArrayEncoder는 네티에서 제공해주는 bean이고
    요청과 응답에 대한 데이터들을 
byte array로 암복호화 해주는 역할을 담당한다.

    
그러니까 등록된 handler의 로직을 대략적으로 본다면

    1. 요청 데이터 유입 - Netty
    2. byte array로 복호화 - ByteArrayDecoder
      (참고로 들어오는 모든 데이터들은 ByteBuf로 추상화 되어있음.)
    3. 필자가 작성한 EchoServerHandler 로직 수행. SimpleChannelInoundHandler를 상속받음.
    4. byte array로 암호화 - ByteArrayEncoder
    5. 요청에 대한 응답 - Netty
    6. handler로직 수행 끝.


 4. Why SimpleChannelInoundHandler?
       설명하기 앞서 해당 bean은 ChannelInboundHandler를 구현받았고 가장 추상적인 bean은 ChannelHandler다.
       
ChannelInboundHandler에서 override된 method들을 통해 이벤트들을 처리하게 될 것이다.


       주의깊게 볼 method는 channelRead0다.
       다른 bean에서도 있을지 모르겠지만 channelRead0는 SimpleChannelInoundHandler에서만 작성되어있다.
       
channelRead의 내용을 분석하면
       SimpleChannelInoundHandler의 channelRead는 
       해당 매개변수 중에 요청받은 데이터인 Object msg를 SimpleChannelInoundHandler에서 상속하였을때
       지정한 Generic과 일치하는가를 따져본 후에 
channelRead0을 진행한다.
       그렇지 아니하면 오류가 발생되어 log에 WARN이 출력된다.
       
그러나 SimpleChannelInoundHandler의 channelRead0는 요청받은 데이터의 유형이
       
애초부터 지정한 Generic을 매개변수의 유형으로 두고있다.
       따
라서 channelRead에서 진행되는 일련의 작업들을 할 필요가 없다.

        아무튼 SimpleChannelInoundHandler는 요청받은 데이터인 msg에 대한 유형 검사 작업을 할 필요가 없기 때문에
        
읽을때의 작업을 수월하게 진행 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public static void main(String[] args) {
        
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        try{
            // 서버 부트스트랩 생성
            ServerBootstrap b = new ServerBootstrap();
            
            // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 서버 회선 설정 시작. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        
            // 이벤트루프 설정
            b.group(bossGroup, workerGroup)
            // 채널입출력방식 설정
            .channel(NioServerSocketChannel.class)
            
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 서버부트스트랩의 초기화가 진행될때. 시작 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // 최초 클라이언트로부터의 연결이 들어오는 큐에 대하여 100개까지 지정.
            .option(ChannelOption.SO_BACKLOG, 100)
            // 이벤트 핸들러 설정 - 로그핸들러로 지정.
            .handler(new LoggingHandler(LogLevel.INFO))
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 서버부트스트랩의 초기화가 진행될때. 끄읕 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            
            
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 클라이언트로부터 연결이 완료된 후. 시작 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // 새로 연결된 채널과 이벤트 핸들러 설정
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel paramC) throws Exception {
                    paramC.pipeline().addLast("1"new ByteArrayDecoder());
                    paramC.pipeline().addLast("2"new EchoServerHandler());
                    paramC.pipeline().addLast("3"new ByteArrayEncoder());
                }
            })
            // 세션 해제 여부 감지.
            .childOption(ChannelOption.SO_KEEPALIVE, true);
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 클라이언트로부터 연결이 완료된 후. 끄읕 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            
            // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 서버 회선 설정 끄읕. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
            
            ChannelFuture f = b.bind(PORT).sync();
            f.channel().closeFuture().sync();
            
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
cs




[Netwok/Netty]06. Netty 구축을 위한 beans(1) - ServerBootstrap

(출처 : http://javafactory.tistory.com/1382)

1. Beans

    • public abstract class AbstractBootstrap<Generic........>
      bootstrab의 사전적 의미는 한 번 시작하면 알아서 진행되는 일련의 과정을 말한다.
      이 추상클래스는 헬퍼클래스로서 서버든 클라이언트든 이를 상속시켜주어 실제 소켓을 구현하게 도움을 준다.
      특히 단어의 특성에 따라 method chaining이 가능하다.
      기동을 하기위한 전체적인 일종의 틀 혹은 컨테이너.
    • public class ServerBootStrap
      extends AbstractBootstrap<ServerBootstrap, ServerChannel>
      실제 서버를 구현하는 부트스트랩.
      클라이언트의 연결을 수락하고 연결된 클라이언트 소켓에 대하여 입출력을 처리하는 두 개의 쓰레드 그룹을 관리한다.
      하나의 쓰레드 그룹으로도 지정할 수는 있지만, 원활한 병행처리를 위하여 두 개의 그룹을 생성하기를 권장한다.

    • public class BootStrap extends AbstractBootstrap<Bootstrap, ServerChannel>
      실제 클라이언트를 구현하는 부트스트랩. connect라는 method를 통해 해당 주소에 연결한다.
2. Methods
    • public ServerBootstrap group
      (EventLoopGroup parentGroup, EventLoopGroup, childGroup)
      이벤트 루프 설정.
      클라이언트의 연결을 수락하고 연결된 클라이언트 소켓에 대하여 입출력을 처리하는 두 개의 쓰레드 그룹을 관리한다.
      하나의 쓰레드 그룹으로도 지정할 수는 있지만, 원활한 병행처리를 위하여 두 개의 그룹을 생성하기를 권장한다.
    • public B channel(Class<? extends C> channelClass)
      '해당 서버 부트스트랩에 지정할 채널에 대하여 어떤 방식으로 통신할 것인가'를 지정한다.
      부모클래스의 AbstractBootstrap을 상속받고 있고 개발자가 원하는 채널을 지정할 수 있다.
      지금은 nio만 할 것이므로 NioServerSocketChannel.class를 사용한다.
    • public <T> B option(ChannelOption<T> option, T value)
      AbstractBootstrap으로 부터 상속받았다.
      상속받은 method들은(option, handler) 최초 서버 기동에 대한 초기설정에 불과하다.
      최초 클라이언트로부터 연결에 대한, 서버 자신의 채널(혹은 소켓)옵션을 지정한다.
      이 종류는 ChannelOption에서 확인이 가능하다. 

      참고로 매개변수 SO_BACKLOG, 100의 의미는 
      클라이언트로부터의 연결이 들어오는 큐에 대하여 몇 개까지 지정할 수 있는가를 말한다.

    • public B handler(ChannelHandler handler)
      본 method를 통해 해당 서버소켓(혹은 채널)에서 발생 되는 이벤트들을 담당한다.
      LoggingHandler는 서버에서 발생되는 모든 이벤트들을 Log로 출력한다.
      본 Bean을 들여다보면 
      ChannelInboundHandler를 상속받고 ChannelOutboundHandler를 구현받고 있다.
      이 말인 즉슨 서버입장에서 당발, 타발에 대한 이벤트들을 Log로 출력한다는 의미로 볼 수 있다.

    • public ServerBootstrap childHander(ChannelHander handler)
      ServerBootstrap에서 구현된 child method(childHandler, childOption)이다.
      클라이언트로 부터 연결이 들어 올 때, 본 method를 통해서 해당 클라이언트와의 발생되는 이벤트들을 관리하게 된다.
      매개변수는 ChannelInitializer를 작성함으로서, 새로 연결된 Channel과 환경설정을 지정해준다.
      대부분 ChannelPipeline을 이용하여 handler들을 계속 추가한다.
      실제 이벤트가 발생되면 어떤 동작을 취하는지는 본 method를 통해 작성할 수 있다.

    • public <T> ServerBootstrap childOption
      (ChannelOption<T> childOption, T value)
      클라이언트로 부터 연결이 온 후에 해당 소켓(혹은 채널)에 대한 옵션을 지정한다.
      SO_KEEPALIVE의 의미는 해당 클라이언트와 연결한 세션에 대하여 종료여부를 감지하는 것을 말한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public static void main(String[] args) {
        
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        try{
            // 서버 부트스트랩 생성
            ServerBootstrap b = new ServerBootstrap();
            
            // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 서버 회선 설정 시작. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
        
            // 이벤트루프 설정
            b.group(bossGroup, workerGroup)
            // 채널입출력방식 설정
            .channel(NioServerSocketChannel.class)
            
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 서버부트스트랩의 초기화가 진행될때. 시작 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // 최초 클라이언트로부터의 연결이 들어오는 큐에 대하여 100개까지 지정.
            .option(ChannelOption.SO_BACKLOG, 100)
            // 이벤트 핸들러 설정 - 로그핸들러로 지정.
            .handler(new LoggingHandler(LogLevel.INFO))
            // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 서버부트스트랩의 초기화가 진행될때. 끄읕 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            
            
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 클라이언트로부터 연결이 완료된 후. 시작 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // 새로 연결된 채널과 이벤트 핸들러 설정
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel paramC) throws Exception {
                    paramC.pipeline().addLast(new ByteArrayDecoder());
                    paramC.pipeline().addLast(new EchoServerHandler());
                    paramC.pipeline().addLast(new ByteArrayEncoder());
                }
            })
            // 세션 해제 여부 감지.
            .childOption(ChannelOption.SO_KEEPALIVE, true);
            // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 클라이언트로부터 연결이 완료된 후. 끄읕 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            
            // @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 서버 회선 설정 끄읕. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
            
            ChannelFuture f = b.bind(PORT).sync();
            f.channel().closeFuture().sync();
            
        }catch(Exception e){
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
cs




[Netwok/Netty]05. Netty 구축을 위한 beans(1) - Group

1. public absctract interface EventExecutorGroup extends ScheduledExecutorService, Iterable<EventExecutor>
    
데이터를 주고 받는 이벤트들의 생명주기를 관리한다.
    다시 말하자면 데이터를 주고 받는 그 이벤트들이 일종의 쓰레드라 볼 수 있는데, 이러한 쓰레드들을 관리하는 스케쥴러와 같다.

2. public abstract interface EventLoopGroup extends EventExecutorGroup
    
서로 다른 종류들을 가진 전송 이벤트들의 추상적 모델.

3. public class NioEventLoopGroup extends MultithreadEventLoopGroup
    
NIO라는 전송이벤트로 입출력 동작을 다루는 Multithread EventLoop. 



+ Recent posts