Netty 시작하기 (1)

52
Netty 시작하기 (1) 고성능 네트워크 애플리케이션 프레임워크 김대현 @hatemogi 0

Transcript of Netty 시작하기 (1)

Page 1: Netty 시작하기 (1)

Netty 시작하기 (1)고성능 네트워크 애플리케이션 프레임워크

김대현@hatemogi

0

Page 2: Netty 시작하기 (1)

지금 보시는 발표 자료는...

위 주소에서 직접 볼 수 있고, [SPC]를 누르면 다음 슬라이드로, [Shift­SPC]를누르면 이전 슬라이드로 이동합니다.

http://hatemogi.github.io/netty­startup

Page 3: Netty 시작하기 (1)

김대현

2015, Netty 기반 서버 개발2013, 사내 Redis SaaS 개발팀장, 160+ VM2010, 클라우드, 네트워크 파일동기화 프로토콜 설계2010, MyPeople, 네트워크 아키텍쳐 설계2009, Cafe 채팅 부하분산 서버 개발2004, Cafe 한줄메모장 개발, 일일 3억 PV

Page 4: Netty 시작하기 (1)

과정1. Netty의 기본 설명2. 문제 풀이3. 예제 이해 / 실습 개발

목표

Event­Driven, 비동기 I/O 이해Netty로 개발 경험(조만간) 실무에 응용

Page 6: Netty 시작하기 (1)

4시간 진행 주제

1. 비동기 개념, 기본 인터페이스(Channel) 정리2. 3. 4.

예제와 실습

1. Discard 서버, Echo 서버2. HTTP 서버3. 채팅 서버4. 웹소켓 채팅 서버

고성능 메모리 모델과 유연한 파이프라인깔끔한 쓰레드 모델, Future/Promise 활용풍부한 코덱과 WebSocket

Page 7: Netty 시작하기 (1)

매 시간 구조

1. 설명 (20분)2. 예제 설명 / 데모 (10분)3. pair 실습 (25분)

총 9개 문제 풀이

Page 8: Netty 시작하기 (1)
Page 9: Netty 시작하기 (1)

다음의 공통점

NginxHAProxyMemcachedRedisNode.js?

Page 10: Netty 시작하기 (1)

왜 Netty를 쓸까?고성능쓰기 편하다 (유연하고 쉽다)

Page 12: Netty 시작하기 (1)
Page 13: Netty 시작하기 (1)

Netty가 고성능인 이유

Non­blocking Asynchronous 처리가 기본적은 쓰레드로 많은 요청을 처리GC부하를 최소화하는 Zero­copy ByteBuf 지원

Page 14: Netty 시작하기 (1)

Netty가 편한 이유

각종 네트워크 프로토콜 (코덱) 기본 지원필요한 부분을 쉽게 조립해 쓸 수 있는 구조멀티쓰레딩 처리 없이도...

Page 15: Netty 시작하기 (1)

Netty 버전

5.0.0.Alpha2 ‑ 04­Mar­2015 (Development)4.1.0.Beta4 ‑ 04­Mar­2015 (Development)4.0.26.Final ‑ 04­Mar­2015 (Stable, Recommended)3.10.1.Final ‑ 23­Mar­2015 (Stable)

이 자료와 실습은 4.0.26.Final을 기준으로 합니다.

Page 16: Netty 시작하기 (1)

동기와 비동기Synchronous vs. AsynchronousBlocking vs. Non­blocking

우리말로 하자면...

요청하고 결과를 계속 기다리기완료되면 알려달라고 요청하고 다른 일 하기

Page 17: Netty 시작하기 (1)

고객센터에 전화를 했는데... 모든 상담원이 통화중입니다.

1. 연결될 때까지 기다리기2. 전화해달라는 기록 남기고 끊기

동기 or 비동기

커피 주문지하철 이용, 자가 운전업무요청 메일, 업무문의 전화팀장의 업무지시

Page 18: Netty 시작하기 (1)

동기 (Synchronous) 코드 Blocking call

// 동기 방식void greeting(Context ctx) String req = ctx.readLine(); ctx.write("안녕, " + req); System.out.println("완료");

Page 19: Netty 시작하기 (1)

비동기 (Asynchronous) 코드 Non­blocking call

// 비동기 방식void greeting(Context ctx) ctx.readLine().done(line -> ctx.write("안녕, " + req); ); System.out.println("완료");

Page 20: Netty 시작하기 (1)

Event­Driven ProgrammingIn computer programming, event­driven programming is a programmingparadigm in which the flow of the program is determined by events such as useractions (mouse clicks, key presses), sensor outputs, or messages from otherprograms/threads. Event­driven programming is the dominant paradigm used ingraphical user interfaces and other applications (e.g. JavaScript webapplications) that are centered on performing certain actions in response to userinput. Wikipedia

Page 21: Netty 시작하기 (1)

사건 기반 프로그래밍

사건 기반 프로그래밍(영어: Event­driven programming; EDP)은 비주얼 베이직과 같이,사용자의 명령·마우스 클릭·다른 프로그램의 메시지·키보드 글쇠 입력 등의 ‘사건’에 따라, 제어흐름이 결정되어 일을 하도록 하게끔 만들어진 프로그래밍 언어 방식을 뜻한다. 위키백과

Page 22: Netty 시작하기 (1)

등잔 밑의 Event­Driven 코드

자바스크립트iOS 앱Android 앱데스크탑 애플리케이션

$("button").on("click", function(event) console.log(event); alert("클릭"););

Page 23: Netty 시작하기 (1)

True or False

비동기와 Event­driven은 같은 뜻이다?Event­Driven은 데스크탑앱 클라이언트에만 적합하다?비동기식 싱글쓰레드에서는 Blocking Call을 할 수 없다?

Page 24: Netty 시작하기 (1)

업무요청시

업무 요청하고 옆에서 기다리고 있으면?업무 요청하고 돌아갔다가, 나중에 다시 와서 확인하면?업무 요청하면서, "다되면 저한테 알려주세요"하면?

Page 25: Netty 시작하기 (1)

Netty는 Async Event­Driven

됩니다만...

어? 그럼 그냥 Java의 NIO를 쓰면 안되나요

Page 26: Netty 시작하기 (1)

Netty의 핵심 인터페이스Channel

ChannelFuture

ChannelHandler

ChannelHandlerContext

ChannelPipeline

EventLoop

Page 27: Netty 시작하기 (1)

Channel

읽기, 쓰기, 연결(connect), 바인드(bind)등의 I/O 작업을 할 수 있는 요소 또는 네트워크 연결모든 I/O 작업은 비동기 ­> ChannelFuture

핵심 메소드

ChannelFuture write(Object obj)ChannelFuture flush(Object obj)ChannelFuture writeAndFlush(Object obj)ChannelFuture closeFuture()ChannelPipeline pipeline()SocketAddress remoteAddress()

Page 28: Netty 시작하기 (1)

ChannelFuture

Channel의 I/O 작업의 결과ChannelFutureListener를 등록 결과에 따른 작업

핵심 메소드

ChannelFuture addListener(GenericFutureListener<...> listener)Channel channel()boolean isSuccess();Throwable cause();ChannelFuture await()ChannelFuture sync()

Page 29: Netty 시작하기 (1)

ChannelHandler

Netty의 핵심 요소!Netty의 I/O 이벤트를 처리하는 인터페이스ChannelInboundHandlerAdapter

ChannelOutboundHandlerAdapter

전체 메소드

void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)void handlerAdded(ChannelHandlerContext ctx)void handlerRemoved(ChannelHandlerContext ctx)

Page 30: Netty 시작하기 (1)

ChannelHandlerContext

ChannelHandler는 ChannelHandlerContext를 통해다음 ChannelHandler에게 이벤트를 넘기거나,동적으로 ChannelPipeline을 변경할 수 있음 ­ [실습4]

핵심 메소드

Channel channel()ChannelPipeline pipeline()ChannelFuture write(Object msg)ChannelHandlerContext fireChannelActive(Object msg)ChannelHandlerContext fireChannelRead(Object msg)

Page 31: Netty 시작하기 (1)

ChannelPipeline

Channel에 드나드는 inbound / outbound 이벤트를 처리 처리, ChannelHandler 리스트

두번째 시간에 상세 설명

주요 메소드

Intercepting Filter 패턴

ChannelPipeline addLast(ChannelHandler... handlers)ChannelPipeline addLast(String name, ChannelHandler handler)ChannelHandler remove(String name)<T extends ChannelHandler> T remove(Class<T> handlerType)

Page 32: Netty 시작하기 (1)

EventLoop

등록된 Channel들의 모든 I/O 작업을 처리구현체 NioEventLoopGroup를 주로 사용

주요 메소드

boolean inEventLoop()<T> Future<T> submit(Callable<T> task)<V> Promise<V> newPromise()<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)

Page 33: Netty 시작하기 (1)

Channel관련 인터페이스 전체 구조

EventLoop

Channel

Channel

ChannelChannelPipeline

ChannelHandler 1 ChannelHandler 2 ChannelHandler N

Page 34: Netty 시작하기 (1)

예제와 실습용 프로젝트

Git clone

Netty 4.x JavaDoc

https://github.com/hatemogi/netty­startup

git clone https://github.com/hatemogi/netty-startup

처음에는 실패하는 유닛테스트가 준비돼 있고, 실습문제를 모두 풀면 유닛 테스트가 모두 통과돼야합니다.

http://netty.io/4.0/api/index.html

Page 35: Netty 시작하기 (1)

첫 예제: DiscardServer

DiscardServer.javaDiscardServerHandler.java

src/nettystartup/h1/discard

Page 36: Netty 시작하기 (1)

src/nettystartup/h1/discard/DiscardServer.java

public final class DiscardServer public static void main(String[] args) throws Exception EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new DiscardServerHandler()); ChannelFuture f = b.bind(8010).sync(); System.err.println("Ready for 0.0.0.0:8010"); f.channel().closeFuture().sync(); finally workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully();

Page 37: Netty 시작하기 (1)

src/nettystartup/h1/discard/DiscardServerHandler.java

class DiscardServerHandler extends ChannelInboundHandlerAdapter @Override public void channelRead(C..H..Context ctx, Object msg) throws Exception ByteBuf buf = (ByteBuf) msg; try // discard finally buf.release(); // 이 부분은 두번째 시간에 설명합니다. @Override public void exceptionCaught(C..H..Context ctx, Throwable cause) cause.printStackTrace(); ctx.close();

Page 38: Netty 시작하기 (1)
Page 39: Netty 시작하기 (1)
Page 40: Netty 시작하기 (1)

DiscardServer 테스트

telnet localhost 8010

Trying ::1...Connected to localhost.Escape character is ']'.helotest

]telnet> closeConnection closed.

Page 41: Netty 시작하기 (1)

Netty와 유닛테스팅EmbeddedChannel을 활용

EmbeddedChannel ch = new EmbeddedChannel(new HandlerToBeTested());

Page 42: Netty 시작하기 (1)

EmbeddedChannel

public class EmbeddedChannel extends AbstractChannel public EmbeddedChannel(ChannelHandler... handlers)

public Object readInbound() ... public Object readOutbound() ... public boolean writeInbound(Object... msgs) ... public boolean writeOutbound(Object... msgs) ...

public void checkException() ...

Page 43: Netty 시작하기 (1)

test/.../h1/discard/DiscardServerHandlerTest.java

public class DiscardServerHandlerTest @Test public void discard() String m = "discard test\n"; EmbeddedChannel ch = new EmbeddedChannel(new DiscardServerHandler()); ByteBuf in = Unpooled.wrappedBuffer(m.getBytes()); ch.writeInbound(in); ByteBuf r = (ByteBuf) ch.readOutbound(); assertThat(r, nullValue());

Page 44: Netty 시작하기 (1)
Page 45: Netty 시작하기 (1)
Page 46: Netty 시작하기 (1)

첫번째 실습: EchoServerEchoServer

EchoServerHandler

EchoServerHandlerTest

Page 47: Netty 시작하기 (1)

src/nettystartup/h1/echo/EchoServer.java

public final class EchoServer public static void main(String[] args) throws Exception EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class); // TODO: [실습 1-1] 이 부분을 채워서 EchoServerHandler를 등록합니다 ChannelFuture f = b.bind(8020).sync(); f.channel().closeFuture().sync(); finally workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully();

Page 48: Netty 시작하기 (1)

src/nettystartup/h1/echo/EchoServerHandler.java

class EchoServerHandler extends ChannelInboundHandlerAdapter @Override public void channelRead(ChannelHandlerContext ctx, Object msg) // TODO: [실습1-2] 받은로 응답하는 코드를 한 줄 작성합니다. release는 필요하지 않습니다. @Override public void channelReadComplete(ChannelHandlerContext ctx) ctx.flush(); @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) cause.printStackTrace(); ctx.close();

Page 49: Netty 시작하기 (1)

test/nettystartup/h1/echo/EchoServerHandlerTest.java

public class EchoServerHandlerTest @Test public void echo() String m = "echo test\n"; EmbeddedChannel ch = new EmbeddedChannel(new EchoServerHandler()); ByteBuf in = Unpooled.copiedBuffer(m, CharsetUtil.UTF_8); ch.writeInbound(in); ByteBuf r = (ByteBuf)ch.readOutbound(); releaseLater(r); assertThat("응답이 없습니다", r, notNullValue()); assertThat("참조수는 1이어야 합니다",r.refCnt(), is(1)); assertThat("수신한 텍스트가 결과로 와야합니다", r.toString(CharsetUtil.UTF_8), equalTo(m));

Page 50: Netty 시작하기 (1)

실습 정리

Netty로 네트워크 서버를 만들어 봤습니다.ChannelHandler의 기초를 익혔습니다.메모리 모델은 다음 시간에 설명합니다.

Page 52: Netty 시작하기 (1)

다음 시간에는...

http://hatemogi.github.io/netty­startup/2.html