티스토리 툴바


1984

2011/12/14 19:54


처음 이 책의 존재를 알게 된 것이 고등학교 때 였는 지 대학 때 였는 지 정확히 기억나지 않지만, 그 이후로 여기저기서 인용되는 것을 꽤 많이 보고 들었던 책 입니다. 그럴 때마다 읽어봐야겠다는 생각만 하고 지나쳤었는데, 올해 결정적인 계기가 생겼습니다. 스티브 잡스 열풍이 불면서 애플이 1984를 모티브로 만든 광고를 본 것이죠. 막연히 생각했던 이미지가 광고 영상을 통해 구체화 되고, 상상의 나래가 펼쳐지니 궁금해 견딜 수가 없어 구입하고 말았습니다. 
 
책은 생각보다 쉽게 읽히지는 않았습니다. 초반부를 지나면서 인물과 사건 전개가 다소 사상적으로 변해갔기 때문일까요. 읽다보면 눈꺼풀이 무거워지고 이내 손을 놓기 일쑤였습니다. 덕분에 얼추 2달 정도 걸려 읽은 것 같네요.  
 
이야기 구조는 단순합니다.

오세아니아의 당원인 윈스톤은 깨어 있는 사람이었습니다. 그래서 모순 투성이인 당의 구호나 주변 인물들의 말과 행동을 이해할 수 없었죠. 그러나 감시가 일상화된 사회에서 그런 내색을 하는 것은 파멸에 이르는 길 임을 알고 있기에 불안한 생활을 지속하고 있었습니다. 그런 윈스톤의 내면이 당의 조직적인 강압과 설득에 의해 변해가는 과정을 보여주면서, 오세아니아가 어떻게 그런 사회가 되었고 유지해 나갈 수 있는 지를 보여 줍니다. 
 
국가라는 절대명제와 목표지상주의의 결합에 개인의 자유가 결여되었을 때를 경고하는 소설이라고나 할까요. 더불어 모든 언론이 엄격히 통제되어 과거까지 조작하는 사회의 무서움을 느낄 수 있었습니다. 
 
짧은 시간이나마 국가의 역할, 정치, 대기업, 중소기업, 자영업자, 경제, 조중동, 나꼼수, 언론, 사회 안전망 등 주위를 둘러싼 많은 것들에 대해 다시 한 번 생각해 볼 수 있었습니다. 
 
이제 겨우 주요 인물과 배경, 오세아니아의 국가철학과 관련된 몇몇 용어를 이해하는 정도일 뿐이라, 어느 정도 시간이 지난 후 한 번 더 읽어봐야 겠습니다. 그 때에는 지금과는 다른 깊이의 느낌을 가질 수 있으리란 생각이 드네요.
 
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by RayG


뽀모도로 테크닉이란 해야할 일을 여러 개의 [25분 X N] 단위로 쪼개고, 규칙을 준수하여 실행하는 것 입니다. 집중과 휴식, 짧은 성공을 반복함으로써 지치지 않고 슬럼프 없이 지속적으로 일을 해나갈 수 있도록 하는 테크닉 이지요.

뽀모도로 테크닉을 알기 위해 굳이 책을 읽을 필요는 없어 보입니다. 테크닉이란 것이 아주 간단하거든요.

1. 주방 타이머가 돌아가는 동안 할 일을 한 가지만 정한다.
2. 주방 타이머를 25분으로 맞춘다.
3. 25분 동안 오로지 그 일에만 집중한다.
4. 타이머가 울리면 5분간 휴식을 갖는다.
5. 다시 주방 타이머를 25분으로 맞추고 일에 집중한다.

도움이 될 것 같아 뽀모도로 테크닉을 업무에 적용해 봤습니다만, 쉽진 않네요. 25분이란 짧은 시간 동안 내외부의 인터럽트는 생각 이상이었고, 어쩌다 25분을 질주하면 그 탄성에 일을 멈출 수가 없었습니다. 일을 중단하고 휴식을 취하는 것도 쉽지만은 않다는 걸 새삼 깨달았습니다. 하지만 며칠 꾸준히 해 본 결과 하루를 온전히 컨트롤 할 수 있을 것 같다는 생각에 뽀모도로 테크닉에 대한 긍정적인 생각을 가지게 되었습니다. 잘 활용하면 하루를 즉흥적으로, 또는 그냥 열심히 보내고 난 후 '오늘 뭘 했더라?' 하는 허탈감에 빠질 일은 적어질 듯 합니다.

뽀모도로 테크닉을 실천하면서 몰랐던 습관 하나를 알게 되었습니다. 무언가 막히거나 잠시 여유가 생길라치면 저절로 메일을 확인하고 있더군요. 별로 중요하지도 않은 메일을 보거나, 급하지 않은 업무메일에 꽤 많은 시간을 소비하고 있더라구요. 앞으로는 메일도 뽀모도로 테크닉에 따라 확인하기로 마음 먹었습니다.

더보기

저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by RayG

Rework

2011/05/15 08:28

저자들은 웹기반 소프트웨어 업체 37signals의 창립자들 입니다. 회사 설립부터 현재에 이르기까지의 경험을 블로그를 통해, 그리고 PDF로 발표했었습니다. 그걸 모아 REWORK란 이름의 책으로 다시 출판한 것 입니다. 기업을 운영하면서 과정을 공유하고, 회고 했으며, 그 결과물로 다시 기업의 부수적인 수입을 마련한 것이지요.

책의 내용대로 라면 위의 일련의 과정은 창립 초기부터 치밀하게 계획된 것이 아닐 겁니다. 37signals란 기업은 장기적인 계획을 세우지 않습니다. 장기적인 계획을 세운다는 것의 단점을 잘 알고 있으며, 대신 추측이란 걸 합니다. 그리고 주 단위의 계획과 할 일을 점검하고 실행해 나갑니다.

기업의 성장은 꼭 필요하다고 생각하지도 않으며, 제품도 단순함을 추구합니다. 너무 많은 기능은 사실 일부 큰 고객사에서나 필요할 뿐 37signals가 타겟으로 잡고 있는 대부분의 중소기업에는 필요없다고 판단하기 때문입니다. 고객사가 성장하여 더 많은 기능을 요구하기 시작하면 그 고객사에는 37signals보다 더 잘 응대할 수 있는(어쩌면 경쟁사라고 할 수 있는) 기업을 추천해 줍니다. 즉 37signals는 회사의 성장에 따라 일부 대형 고객에 귀속되기 보다는, 단순한 제품으로도 커버 가능한 작은 기업들을 상대로 일관성을 유지하겠다는 겁니다.

일 중독, 직원 감시, 강제적인 기업문화 등 직원들의 사기를 저해하는 요소에 대해서는 단호히 NO!를 외칩니다. 일에 중독된 직원이나, 직원을 감시하거나, 규율을 강조하거나, 기업문화란 옷을 강제로 입히는 것은 결국 전체적인 생산성을 저하시키는 원인이라는 것이지요. 제가 경험했던 직장들은 하나같이 37signals와 반대였었군요. 늦게까지 퇴근하지 않는 직원들의 대부분은 일과 시간을 집중해서 사용하지 못하는 경향이 있습니다. 물론 고객사의 강요와 같은 어쩔 수 없는 경우도 있었지만요. 직장 문화도 자연스럽게 발생하는 것인데, 그걸 강제하려고 하니 참 어색했던 것 같습니다.

가장 인상깊었던 주제 하나를 꼽으라면 '나에게 집중하라. 그들에게 신경쓰지 마라.' 입니다. 역시 중요한 건 다른 사람이 무엇을 하고 있느냐가 아니라, 내가 무엇을 하고 있는가 입니다. 남을 이기기 위한 것이 목표가 되기 보다는, 나의 경쟁력을 높이는 것이 목표가 되어야 겠지요.

짧은 주제와 그에 대한 한두페이지 분량의 글, 그리고 인상적인 삽화(?)로 구성되어 아주 쉽게 읽을 수 있었습니다. 그러면서도 중간 중간 무릎을 치게도, 고개를 갸우뚱 거리게도, 내 모습에 비춰 생각해 볼 만한 꺼리들도 꽤 많았고 조금이나마 마음의 근육을 튼튼히 할 수 있었던 것 같네요.

'이 책을 무시하면 위험해진다' - 세스 고딘

세스고딘이 누구인지는 잘 모르겠지만, 이 책에 가장 어울리는 추천사를 한 것 같습니다. 
 

더보기

저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by RayG


이 책을 손에 잡고 읽게된 계기는 에피소드로 시작하는 초반부의 이야기 덕분 이었습니다. 착하고 힘없는 개발자가 고생해서 만든 시스템을 사용자는 사용하기 불편해서 거만하기 짝이 없는 소프트웨어로 인식한다는 내용이었죠. 착하고 힘없는 개발자에게 감정이입되어 임백준씨가 썼던 책들과 비슷한 류라 생각하고 기대에 차 계속 읽게 되었습니다.

하지만 짧은 에피소드 뒤에 이어지는 내용들은 꼭지로만 보면 괜찮았지만 그다지 인상적이진 못했습니다. 쟁쟁한 토픽들을 유기적으로 활용하지 못하고 그저 펼쳐 놓기만 한 느낌이랄까요. 마치 맨유 선수들이 우리나라 청소년 대표팀을 만나 졸전을 치루는 경기를 본 것만 같습니다.

그렇다고 해서 책 내용이 형편없는 것은 아닙니다. 단지 저와는 코드가 맞지 않을 뿐이었던 것 같습니다. 번역서가 아닌 국내 개발자의 살아있는 경험과 통찰을 배울 수 있는 책을 만나기가 쉽지 않은데, 그런 측면에서는 아주 좋았던 것 같습니다. 

앞으로도 국내 현장의 실전 경험이 많은 분들의 통찰을 배울 수 있는 책들이 많이 나왔으면 하는 바람입니다.
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by RayG

아트 스피치

2011/04/21 19:36


청중 앞에서 정해진 시간 동안 한 주제에 관해 설득력 있게 이야기를 하는 것은 쉬운 일이 아닙니다. 사회적으로 성공한 위치에 있는 사람들이라 해도 마찬가지 입니다. 사회적 성공과 말하기는 전혀 별개인 때문입니다. 아무리 사회적인 성공을 이루었다고 하더라도, 국민학교 시절 교장 선생님 훈화 말씀처럼 아이들을 쓰러지게 만드는 그런 정도라면 곤란하겠지요. 스피치를 잘 하기 위해서는 사회적인 성공을 이루기 위해 그랬던 것처럼 노력이 필요합니다. 

이 책은 아트스피치의 창시자 김미경 원장이 스피치를 잘 하기 위해서 어떤 노력을 하면 되는 지 최적화된 방안을 알려주는 책 입니다. 이야기를 효과적으로 전달할 수 있는 템플릿과 적절한 사례를 들어 설명하기 때문에 비교적 쉽게 읽히는 편입니다.  

아트스피치의 핵심은 무언가를 이야기 하기 위해선 내 속에서 먼저 소화가 되어야 한다는 것 입니다. PT, 강의, 세미나 등 어느 자리에서도 효과적인 의사전달을 하기 위해서는 미리 고민하고 곱씹어서 자신의 언어로 표현할 수 있게 만들어 두어야 한다는 거죠. 그리고 고민하고 곱씹은 그 컨텐츠를 표현할 때 아트스피치에서 제공하는 템플릿이란 옷을 입히면 좀 더 효과적으로 할 수 있다는 겁니다. 

그러나 템플릿은 템플릿일 뿐이며 자기 주변에서, 인생에서 이야기 거리를 끊임없이 없이 찾아 내용을 채워나갈 것을 주문합니다. 그 꺼리들을 아트스피치에서 제공하는 템플릿에 맞춰보고, 자신의 것으로 체득하는 노력이 중요하다고 강조합니다. 결국 하려는 것은 나의 이야기이기 때문입니다.


더보기


저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by RayG



2000년대 초중반 성능테스트를 수행해야 했던 적이 있습니다. 사전 지식도 없이 혼자 진행해야 했던터라 단편적인 지식을 채워 가면서 열심히 삽질을 했었지요. 그 때 사용했던 툴이 MS Stress Test Tool과 JMeter 였습니다. '이렇게 하면 맞는건가?' 하는 답답함을 꾹꾹 눌러가며 하다보니, 개념이나 툴 사용법이 어설프게나마 이해는 되더군요. 

이 책을 보는 내내 느낀 감정은 삽질하던 그 시기에 이런 책이 있었다면 정말 좋았었을텐데 하는 아쉬움 이었습니다. 깔끔하고 쉽게 정리해서 읽기 편했고, 엉성했던 개념들이 차곡차곡 정리되는 기쁨에 읽는 재미도 좋았습니다. 게다가 잘 몰랐던 보안테스트와 툴에 대한 개념도 머리 속에 착하게 자리 잡은 듯 합니다.  

출퇴근 길에 가벼운 마음으로 읽어 볼 요량이었습니다만, 오히려 공부 한 판 제대로 한 느낌 입니다.

더보기

저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by RayG
<<원문보기 클릭>>

수신한 ChannelBuffer를 의미있는 frame 오브젝트로 디코딩 합니다.

TCP/IP와 같은 스트림 기반 전송 시 패킷은 단편화되고 재조립 됩니다. LAN 환경에서도 마찬가지 입니다. 예를 들어 아래와 같은 세 패킷을 가정해 봅시다 :

 +-----+-----+-----+
 | ABC | DEF | GHI |
 +-----+-----+-----+

전송 과정에서 패킷 단편화의 영향으로 서버는 아래와 같은 형식으로 받을 수 있습니다.

 +----+-------+---+---+
 | AB | CDEFG | H | I |
 +----+-------+---+---+

FrameDecoder는 위와 같이 수신한 패킷을 하나 또는 여러개의 의미있는 프레임으로 조합하여 어플리케이션 로직에서 사용될 수 있도록 해줍니다. 위의 예와 같은 경우 FrameDecoder 구현은 수신한 패킷을 아래와 같이 만들어 줄 것 입니다. 

 +-----+-----+-----+
 | ABC | DEF | GHI |
 +-----+-----+-----+

아래의 코드는 프레임을 디코딩하는 샘플 handler 입니다. 최초 4바이트는 헤더를 제외한 프레임의 길이를 나타냅니다.

 MESSAGE FORMAT
 ==============

 Offset:  0        4                   (Length + 4)
          +--------+------------------------+
 Fields:  | Length | Actual message content |
          +--------+------------------------+


DECODER IMPLEMENTATION
 ======================

 public class IntegerHeaderFrameDecoder extends FrameDecoder {

   @Override
   protected Object decode(ChannelHandlerContext ctx,
                           channel,
                           ChannelBuffer buf) throws Exception {

     // Make sure if the length field was received.
     if (buf.readableBytes() < 4) {
        // The length field was not received yet - return null.
        // This method will be invoked again when more packets are
        // received and appended to the buffer.
        return null;
     }
 
     // The length field is in the buffer.
     // Mark the current buffer position before reading the length field
     // because the whole frame might not be in the buffer yet.
     // We will reset the buffer position to the marked position if
     // there's not enough bytes in the buffer.
     buf.markReaderIndex();

     // Read the length field.
     int length = buf.readInt();

     // Make sure if there's enough bytes in the buffer.
     if (buf.readableBytes() < length) {
        // The whole bytes were not received yet - return null.
        // This method will be invoked again when more packets are
        // received and appended to the buffer.

        // Reset to the marked position to read the length field again
        // next time.
        buf.resetReaderIndex();

        return null;
     }

     // There's enough bytes in the buffer. Read it.
     ChannelBuffer frame = buf.readBytes(length);

     // Successfully decoded a frame.  Return the decoded frame.
     return frame;
   }
 }


Returning a POJO rather than a ChannelBuffer
decode()와 decodeLast()를 구현하면 ChannelBuffer와 다른 타입의 오브젝트를 리턴할 수 있습니다. 예로 POJO를 리턴하면, 다음 ChannelUpstreamHandler는 ChannelBuffer 대신 POJO를 가진 MessageEvent를 받게 됩니다.


Replacing a decoder with another decoder in a pipeline
멀티플렉서 프로토콜을 작성한다면, FrameDecoder(protocol detector)를 다른 FrameDecoder나 ReplayingDecoder(Actual protocol decoder)로 교체해야 할 수도 있습니다. 이런 경우 ChannelPipeline.replace(ChannelHandler, String, ChannelHandler)를 호출하여 간단히 해결할 수는 없으며, 몇가지 추가 절차가 필요합니다.


 public class FirstDecoder extends FrameDecoder {

     public FirstDecoder() {
         super(true); // Enable unfold
     }

     @Override
     protected Object decode(ChannelHandlerContext ctx,
                             Channel channel,
                             ChannelBuffer buf) {
         ...
         // Decode the first message
         Object firstMessage = ...;

         // Add the second decoder
         ctx.getPipeline().addLast("second", new SecondDecoder());

         // Remove the first decoder (me)
         ctx.getPipeline().remove(this);

         if (buf.readable()) {
             // Hand off the remaining data to the second decoder
             return new Object[] { firstMessage, buf.readBytes(buf.readableBytes()) };
         } else {
             // Nothing to hand off
             return firstMessage;
         }
     }
 }
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by RayG
<<원문보기 클릭>>

역할 
  1. ChannelEvent를 처리하거나, 인터셉트
  2. ChannelPipeline 상에서 채널 이벤트를 다음 핸들러에게 전달

 

Sub-types
ChannelHandler는 메소드를 제공하지 않으며, ChannelEvent를 다루기 위해서는 서브 인터페이스를 구현해야 합니다. 서브 인터페이스에는 두 종류가 있습니다. 각 서브 인터페이스는 전달받은 이벤트를 처리하는데, 이벤트는 upstream과 downstream으로 나눕니다.

  • ChannelUpstreamHandler - upstream 채널 이벤트의 처리 및 인터셉트
  • ChannelDownstreamhandler - downstream 채널 이벤트의 처리 및 인터셉트

각 서브 인터페이스 문서를 보면 보다 더 자세한 설명을 볼 수 있습니다.


The context object
ChannelHandler는 ChannelHandlerContext 오브젝트와 함께 제공됩니다. ChannelHandler는 ChannelPipeline과 상호작용하며, context 오브젝트를 통해 연결 됩니다. context 오브젝트를 사용함으로써, ChannelHandler에서 이벤트를 upstream, 또는 downstream으로 전달할 수 있으며, 파이프라인을 동적으로 변경하거나, 특정한 Handler에 정보(첨부파일)를 저장할 수도 있습니다.


State management
ChannelHandler에 특정한 stateful 정보를 저장해야 할 경우가 있습니다. 이런 경우 권장하는 방법은 멤버 변수를 사용하는 것 입니다.

 public class DataServerHandler extends SimpleChannelHandler {

     private boolean loggedIn;

     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
         Channel ch = e.getChannel();
         Object o = e.getMessage();
         if (o instanceof LoginMessage) {
             authenticate((LoginMessage) o);
             loggedIn = true;
         } else (o instanceof GetDataMessage) {
             if (loggedIn) {
                 ch.write(fetchSecret((GetDataMessage) o));
             } else {
                 fail();
             }
         }
     }
     ...
 }


그리고 handler 인스턴스는 한 커넥션에 종속된 상태변수를 가지기 때문에, 새로운 각각의 채널들을 위해서 handler 인스턴스를 새로 생성하도록 해야 합니다. 

 // Create a new handler instance per channel.
 // See Bootstrap.setPipelineFactory(ChannelPipelineFactory).
 public class DataServerPipelineFactory implements ChannelPipelineFactory {
     public ChannelPipeline getPipeline() {
         return Channels.pipeline(new DataServerHandler());
     }
 }



Using an attachment

handler의 상태 정보를 저장하기 위해 멤버 변수 사용을 권장합니다다. 그러나 수많은 handler 인스턴스를 생성하고 싶지 않은 경우에는 ChannelHandlerContext에서 제공하는 attachment를 사용하여 처리할 수 있습니다. 

@Sharable
public class DataServerHandler extends SimpleChannelHandler {
     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
         Channel ch = e.getChannel();
         Object o = e.getMessage();
         if (o instanceof LoginMessage) {
             authenticate((LoginMessage) o);
             ctx.setAttachment(true);
         } else (o instanceof GetDataMessage) {
             if (Boolean.TRUE.equals(ctx.getAttachment())) {
                 ch.write(fetchSecret((GetDataMessage) o));
             } else {
                 fail();
             }
         }
     }
     ...
}


위와 같이 처리하면 handler의 상태는 attachment로 저장됩니다. 동일한 handler 인스턴스는 다른 파이프라인에 추가할 수도 있습니다. 

public class DataServerPipelineFactory implements ChannelPipelineFactory {
     private static final DataServerHandler SHARED = new DataServerHandler();
     public ChannelPipeline getPipeline() {
         return Channels.pipeline(SHARED);
     }
}



Using a ChannelLocal

다른 handler나 외부의 handler들이 상태 변수에 접근해야 할 경우에는 ChannelLocal을 사용할 수 있습니다. 

public final class DataServerState {
     public static final
        ChannelLocal<Boolean> loggedIn = new ChannelLocal<Boolean>() {
         protected Boolean initialValue(Channel channel) {
             return false;
         }
     }
     ...
}


@Sharable
public class DataServerHandler extends SimpleChannelHandler {

     @Override
     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
         Channel ch = e.getChannel();
         Object o = e.getMessage();
         if (o instanceof LoginMessage) {
             authenticate((LoginMessage) o);
             DataServerState.loggedIn.set(ch, true);
         } else (o instanceof GetDataMessage) {
             if (DataServerState.loggedIn.get(ch)) {
                 ctx.getChannel().write(fetchSecret((GetDataMessage) o));
             } else {
                 fail();
             }
         }
     }
     ...
}

// Print the remote addresses of the authenticated clients:
ChannelGroup allClientChannels = ...;
for (Channel ch: allClientChannels) {
     if (DataServerState.loggedIn.get(ch)) {
         System.out.println(ch.getRemoteAddress());
     }
}  



The @Sharable annotation

ChannelHandler에 @Sharable 어노테이션을 사용하면 handler 인스턴스는 하나만 생성하며, 여러 ChannelPipeline에 추가해도 race condition 없이 사용할 수 있습니다.

이 어노테이션을 사용하지 않을 경우에는 새 handler 인스턴스를 매번 생성해서 파이프라인에 추가해야 합니다.

저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by RayG
<<원문보기 클릭>>

Helper 클래스이며, 두가지 역할을 수행합니다.  
  1. 서버사이드 채널 생성
  2. incoming 커넥션 수용

연결형 통신을 지원
TCP/IP나 local 통신과 같은 연결형 통신만을 지원합니다. UDP/IP 등의 비연결형 통신은 ConnectionlessBootstrap을 사용해야 합니다.  

부모 채널과 자식 채널
부모 채널은 incoming 커넥션을 수신하는 채널입니다. 이것은 ChannelFactory의 bind() 메소드를 통해 생성 됩니다. 

부모채널이 올바로 생성되면 incoming 커넥션들을 수신할 수 있습니다. 이 커넥션들은 모두 부모 채널의 자식들이 됩니다. 

부모 채널 파이프라인 설정
부모 채널은 정형화 되어 있습니다. 때문에 부모채널의 파이프라인을 커스터마이징 하는 것은 권장하지 않습니다만, 꼭 필요한 경우를 위해서 parentHandler란 프로퍼티를 제공하고 있습니다. 

자식 채널 파이프라인 설정 
모든 채널은 고유의 채널 파이프라인을 가지고 있습니다. 이 채널 파이프라인을 커스터마이징 하기 위해 권장하는 방법은 ChannelPipelineFactory를 사용하는 방법 입니다. (아래 소스 참고)

ServerBootstrap b = ...;
b.setPipelineFactory(new MyPipelineFactory());

public class MyPipelineFactory implements ChannelPipelineFactory {
 public ChannelPipeline getPipeline() throws Exception {
   // Create and configure a new pipeline for a new channel.
   ChannelPipeline p = Channels.pipeline();
   p.addLast("encoder", new EncodingHandler());
   p.addLast("decoder", new DecodingHandler());
   p.addLast("logic",   new LogicHandler());
   return p;
 }
}

다른 방법은 기본 파이프라인을 사용하는 방법 입니다. 즉 bootstrap이 각각의 채널에 기본 파이프라인을 shallow-copy 하도록 합니다. (아래 소스 참고)

ServerBootstrap b = ...;
ChannelPipeline p = b.getPipeline();

// Add handlers to the default pipeline.
p.addLast("encoder", new EncodingHandler());
p.addLast("decoder", new DecodingHandler());
p.addLast("logic",   new LogicHandler());

여기서 사용된 ChannelHandler들은 clone이 아니라, reference만 추가한 것이란 걸 주의해야 합니다. 하나 이상의 채널을 사용하거나, incoming 커넥션을 자식 채널에 넘기는 서버 모드 인 경우에는 적용할 수 없기 때문 입니다. 

다른 채널들 간에 다른 설정 적용하기
ServerBootstrap은 생성자의 ChannelFactory 구현체만 관리할 뿐, 그 외 어떤 리소스에도 관여하지 않습니다. 그러므로 여러 개의 ServerBootstrap에서 동일한 ChannelFactory를 적용해도 각 채널들 간에는 영향이 없습니다. 

저작자 표시 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by RayG

NioServerSocketChannelFactory는 대량 동시접속을 처리할 수 있는 NIO 기반 서버소켓채널을 생성합니다. 서버소켓채널은 보스/워커쓰레드로 부르는 두가지 형태의 쓰레드을 가지고 있습니다.  

보스쓰레드
바인드되어 있는 동안 Incoming 커넥션을 받습니다. 그리고 연결된 채널을 서벗소켓채널이 관리하는 워커쓰레드 중 하나에게 패스 합니다. 

워커쓰레드
서버소켓채널은 하나 이상의 워커쓰레드를 가지고 있습니다. 워커쓰레드는 non-blocking 모드에서 하나, 또는 여러 채널을 위해 non-blocking 읽기/쓰기를 수행합니다. 

쓰레드의 생명주기와 종료
모든 쓰레드는 서버소켓채널을 생성할 때 인자로 사용한 java.util.concurrent.Executors로부터 생성합니다. Netty는 NewCachedThreadPool 사용을 권장합니다. 

보스/워커쓰레드는 필요한 시점(lazely)에 활성화되며, 프로세스에서 사용되지 않을 때 릴리즈 됩니다. 모든 관련 리소스(Selector 등)들도 보스/워커쓰레드와 함께 릴리즈 됩니다. 그러므로 종료할 때는 다음의 순서로 하는 것이 좋습니다. 

  1. 생성된 모든 채널의 바인딩 해제
  2. 바인딩 해제된 채널들의 자식 채널을 종료 (보통 ChannelGroup.close()로 처리)
  3. releaseExternalResources() 호출

반드시 모든 채널들이 종료된 후에 executor을 종료합니다. 만약 executor를 먼저 종료하면 RejectedExecutionException이 발생하며, 관련 리소스들은 비정상 종료될 수 있으므로 주의 합니다. 

저작자 표시 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by RayG