[E6]2012. netty internals
-
Upload
naver-d2 -
Category
Technology
-
view
8.718 -
download
2
Transcript of [E6]2012. netty internals
Netty Internals and Core Concepts이희승
Twitter, Inc.
CONTENTS
● 소개
● 이벤트 모델
● 스레드 모델
● 테스팅
● 생각할 거리
소개
● 네티란 ?
● 구성요소
– 이벤트 루프
– 파이프라인 (BiDiCoR)
네티란 ?
● Java network application framework
– http://netty.io/
– @netty_project
● Asynchronous & event-driven
– High-throughput & highly concurrent connection
– .. with less resources – threads, memory & CPU cycles
● API as an I/O abstraction layer
– Works with NIO, OIO, AIO & more without many changes
● Well-defined event model & thread model
● Flexible event handling with bi-directional chain of responsibility pattern
● Promotes 'separation of concerns'
– I/O layer (Netty)
– Protocol codec (Netty or user)
– Business logic (user)
구성요소
Buffers
I/O Abstraction – Channels, Event Loops, and Pipelines
In-VM
HTTP
TCP UDP
Custom Event Handlers & Codecs
Business Logic
Transports
User Code
Handlers
Core
Event Handlers
Codec Framework
이벤트 루프
● Similar to..
– Windows event dispatcher
– Swing event loop
– Reactor
● 사용자의 I/O 요청을 처리
– 이벤트 루프 스레드 내에서 요청했을 경우 즉시
– 이벤트 루프 스레드 외에서 요청했을 경우 이벤트 루프의 작업 큐에 넣어 스케쥴
● 외부의 자극에 반응하고 파이프라인에 알림
– 데이터를 읽어들임
– 소켓 버퍼가 꽉 찼음
● 사용자가 원하는 임의 작업 실행
– One-time or periodic
파이프라인
● BiDiCoR
– Bi-directional Chain of Responsibility pattern
– Similar to Servlet filters and decorator pattern
● Inbound event
– 이벤트 루프가 발생시킨 이벤트 ( 소켓 연결 , 데이터 수신 등 )
– 사용자가 작성한 inbound event handler 가 수신할 수 있도록 해 줌
● Outbound event
– 사용자가 요청한 동작 ( 쓰기 , 읽기 일시 중단 등 )
– 사용자가 작성한 outbound event handler 가 다른 핸들러에서 요청한 동작을 가로챌 수 있도록 해 줌
– 최종적으로 이벤트 루프에 전달되어 실제 I/O 가 수행되도록 함
● 예시
– [1:EvLoop] → [2:Protocol Decoder] → [3:Protocol Encoder] → [4:Business Logic]
– 수신 : 1 → 2 → 4
– 송신 : 4 → 3 → 1
이벤트 모델
● Before, After & Considerations
기존의 이벤트 모델
● 이벤트 = 자바 객체
● 예시
– ChannelOpen (instanceof ChannelStateEvent)
– ChannelBound (instanceof ChannelStateEvent)
– ChannelConnected (instanceof ChannelStateEvent)
– MessageEvent (inbound)
– MessageEvent (outbound)
– ChannelDisconnected (instanceof ChannelStateEvent)
– ChannelUnbound (instanceof ChannelStateEvent)
– ChannelClosed (instanceof ChannelStateEvent)
● Is GC cheap?
– No at extreme scale
● Can buffer pool beat JVM's memory allocator?
– Think memory bandwidth took by memset
– Modern concurrency constructs enable close-to-native efficiency in object pooling
● Too many state events!
– Open → bound → connected is an overkill in most TCP conns
– What's interesting to user is 'connected'.
● New buffer is created on every MessageEvent.
– GC pressure
– User has no control over the buffer
● Heap or direct
● Bounded or unbounded
● No buffered flush
– Every write is an immediate flush
– Sometimes not desired
새 이벤트 모델
● 이벤트 = 메소드 호출
● 예시
– ChannelRegistered
– ChannelActive
– InboundBufferUpdated
– Flush
– ChannelInactive
– ChannelUnregistered
● 상태 전이 단순화
● 핸들러가 수신 및 송신 버퍼를 만들어 네티에게 제공하면 지속적으로 재사용
– 버퍼 해제 시점이 명확하므로 풀링 구현 단순화 가능
– 이벤트에 버퍼 정보가 없음
● Buffered flush
– 버퍼에 데이터를 채운 뒤 flush 이벤트를 발생시키는 것으로 MessageEvent 대체
– 여러 개의 메시지를 쓴 뒤 flush 하여 시스템 콜 절약
● 훨씬 적은 GC 빈도
– 이벤트 객체 생성 → 호출 스택
– 수신 및 송신 버퍼
● 단점
– 이벤트 유형별로 메소드를 전부 정의해야 함 ( 코드 중복 )
– 사용자 정의 이벤트 처리 어려움
● 별도로 UserEventTriggered 메소드 추가
스레드 모델
● Why is well-defined thread model necessary?
잘 정의된 스레드 모델의 필요성
● 프레임워크에서 사용자의 코드가 어느 스레드에서 언제 실행될 지 충분히 정확히 정의해야 함
● 프레임워크가 어느 정도의 thread safety 를 사용자 코드 (e.g. 이벤트 핸들러 ) 에게 제공할 것인가 ?
– 이벤트 간의 happens-before 관계
– 네티가 핸들러 메소드를 동시 실행하는 경우가 있는가
– 비동기 I/O 요청 이후에 완료 알림은 어느 스레드에서 실행되어야 하는가
– 사용자가 직접 동기화하도록 했을 때 dead lock 발생은 하지 않는가
● 정확한 스레드 모델이 제시되지 않으면 사용자 코드는 방어적으로 ( 또는 부정확하게 ) 작성됨
– 불필요한 동기화 및 그에 따른 dead lock ( 또는 부족한 동기화로 인한 race condition)
– 성능 저하 및 유지 보수 비용 증가
● 예 : 네티의 SSL 핸들러
– LoC: 1469 → 925 (3.5 vs 4.0)
네티 4 스레드 모델
● 3.x 대비 이벤트 모델 개선과 함께 가장 중요하고 긍정적인 변화
● 네티는 핸들러가 @Sharable 어노테이션이 붙어 있는 경우가 아니면 절대 같은 핸들러 메소드를 동시에 호출하지 않는다 .
– 사용자는 자신이 작성한 핸들러 클래스의 메소드를 동기화할 필요가 없다 .
– 단 , 사용자는 같은 핸들러 인스턴스를 파이프라인에 여러 번 추가할 수 없다 .
● 네티가 핸들러 메소드를 호출할 때 각 호출 사이에는 happens-before 관계가 성립한다 .
– 사용자는 핸들러의 멤버 변수를 volatile 로 선언할 필요가 없다 .
● 이벤트루프는 항상 싱글 스레드로 실행된다 .
– 핸들러 메소드는 항상 같은 스레드로부터 호출된다 .
● 비동기 요청에 대한 결과 통보 (ChannelFuture) 는 이벤트 루프 스레드에서 항상 이루어진다 .
테스팅
● 네티 유저의 테스트
● 네티 자체의 테스트
네티 유저의 테스트
● 네티는 EmbeddedChannel 이라 불리는 Channel 구현을 제공
● 사용자는 자신이 작성한 핸들러를 EmbeddedChannel 의 파이프라인에 추가하고 이벤트를 임의로 발생시켜 테스트
@Test public void testLineProtocol() { EmbeddedByteChannel ch = new EmbeddedByteChannel(new LineDecoder());
// Ordinary input ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 'A' })); assertNull(ch.readInbound()); ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 'B' })); assertNull(ch.readInbound()); ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 'C' })); assertNull(ch.readInbound()); ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { '\n' })); assertEquals(Unpooled.wrappedBuffer(new byte[] { 'A', 'B', 'C' }), ch.readInbound());
// Truncated input ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 'A' })); assertNull(ch.readInbound()); ch.close(); assertNull(ch.readInbound()); }
네티 자체의 테스트
● 각 트랜스포트의 정확한 동작을 확인하기 위해 모든 트랜스포트 조합과 주요 프로토콜 조합에 대해 교차 테스트 수행
– TCP: NIO x AIO x OIO = 9 – 1 (OIO x OIO)
– UDP: NIO x OIO = 4
– 프로토콜 : Echo, SSL, SPDY = 3 * TCP (8) = 24
● 자체 제공 핸들러의 경우는 EmbeddedChannel 을 이용
● 기타
– Jenkins
– Animal Sniffer: Build fails on violation before packaging
– Checkstyle: Build fails on violation before compilation
생각할 거리
● Dynamic event routing
● Statistical Distributed Performance Analysis on Mesos Cluster
● Management and Monitoring
● Native Transport
● Scalable Community
유용할만한 곳
● 공식 자료
– 홈페이지 - http://netty.io/
– 4.0 변경 사항 모음 - http://goo.gl/E2BJ2
● 커뮤니티 자료
– 블로그 모음 - http://goo.gl/2iMKP
– 스택오버플로 - http://stackoverflow.com/questions/tagged/netty
● 네티 프로젝트 트위터 계정
– https://twitter.com/netty_project
● 네티 한국어 이용자 모임
– https://groups.google.com/forum/#!forum/netty-ko