ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 대규모 트래픽 분산처리를 위한 서버개발 지식
    vert.x 2021. 3. 26. 11:27
    728x90
    반응형

    부제: NIO부터 Netty까지

     Introduction


    인터넷 사용 인구의 폭발적인 증가와 글로벌한 소셜서비스들로 인해 대용량 트래픽처리와 데이터캐싱, 비동기처리 등은

    현대의 서버 개발환경에서 늘 마주하게 되는 주제들입니다.

    5주차스터디에서는, JDK1.4+ 부터 제공되는 NIO와 이를 wrapping한 Netty를 학습하고, 효율적인 방법을 토론합니다.

    JAVA IO 기본

    • stream이란?

    • 스트림은 데이터 소스(File, DB, Socket)에 저장되어 있는 데이터를 다른 데이터 소스에 전달하고자 할 때 사용한다.
      • 키보드의 입력을 모니터에 출력할 때도 스트림을 사용한다.
    • stream 특징
      • stream은 FIFO(first in first out, 선입선출)이다.
      • stream은 단방향이다. 자바에서는 읽기와 쓰기가 동시에 되지 않기 때문에 입력 스트림과 출력 스트림을 각각 생성해야 한다.
      • stream은 데이터 처리가 완료될 때까지 stream을 사용하는 스레드는 waiting 상태에 빠진다.
    • Java IO의 기본 사용 예
      • Java IO는 한 개의 IO만을 사용하는 것이 아니라 여러 개의 IO를 조합해서 사용하는 것이 일반적이다. decorator 디자인 패턴을 활용하고 있다.
      • decorator 디자인 패턴????  참고 : http://iilii.egloos.com/3850836
    • Java IO 패키지 Naming Rule
      • InputStream, OutputStream이 포함된 클래스들은 byte단위의 입출력을 담당한다. 특성에 따라서 앞에 단어를 추가한다.
      •  - ex) FileInputStream file을 읽는 스트림, ByteArrayStream byteArray를 읽는 스트림
      • Reader와 Writer는 2byte 문자단위의 입출력을 담당한다. 특성에 따라서 앞에 단어를 추가한다.
      • Reader와 Writer를 사용하는 가장 큰 이유는 국제화이다. InputStream, OutputStream는 8비트 스트림만을 지원한다.
      • c언어의 기본 문자셋으로 사용되는 ASCII와 자바에서 기본 문자셋으로 사용되는 Unicode의 차이에 대한 이해
    • 동기화 문제
      • 기본데이터타입을 사용할 때를 제외하고, 박싱하거나 레퍼런스 변수를 사용하는 경우 해당 변수 값들은 모두 힙에 저장되어 있다.
      • 이때 힙에 저장되어 있는 녀석들을 'Object' 라고 부른다.
      • 이 Object가 가지고 있는 변수 값들이 Multi-thread에 의해서 변경되려고 할 때 동기화 문제가 발생한다.
      • 즉 동기화문제가 발생하는 최소 단위는 객체이다.
      • 자바에서는 동기화 문제를 해결하기 위해서 모든 객체에 락을 포함시켰다.
      • - 세마포어 http://ko.wikipedia.org/wiki/%EC%84%B8%EB%A7%88%ED%8F%AC%EC%96%B4
      • 락은 객체에 여러 스레드가 동시에 접근하는 것을 방지하기 위한 것으로 힙 영역에 객체가 저장될 때 자동으로 생성된다.
    • 락의 사용
    •   - Synchronized 키워드를 사용한다.
    • Socket 에 대한 언급필요한가??
    • Input,Output.pdf : 스트림 개념, 자바의 Input과 Output과 관련하여 대략적으로 정리한 문서

    Java IO 성능 이슈

    기존의 자바 IO는 커널 버퍼를 직접 핸들링 할 수 없어서 느리다.

    • 소켓이나 파일에서 stream을 읽으면 커널 버퍼에 쓰여지게 된다. 그런데 자바 코드에서는 이 커널 버퍼에 직접 접근할 수 없다. 이 커널 버퍼의 데이터는 다시 JVM 내부 메모리에 불러온 후에야 접근할 수 있다.
    • 즉, 커널에서 JVM 내부 메모리로 복사하는데 상당한 오버헤드가 발생한다.
    • 기본의 Java가 디스크에서 파일을 읽는 과정은 다음과 같다.
      • Process(JVM)이 file을 읽기 위해 kernel에 명령을 전달
      • Kernel은 시스템 콜(read())을 사용하
      • 디스크 컨트롤러가 물리적 디스크로 부터 파일을 읽어옴
      • DMA를 이용하여 kernel 버퍼로 복사

    Process(JVM)내부 버퍼로 복사

    • 따라서 다음 과 같은 문제점 이 생길 수 있죠.
      • JVM 내부 버퍼로 복사할 때, CPU가 관여
      • 복사 Buffer 활용 후 GC 대상이 됨
      • 복사가 진행중인 동안 I/O요청한 Thread가 Blocking
      • 위 와 같은 오버헤드가 생길 수 있습니다. 이에 대해 자세히 알아보도록 하겠습니다.

    기존 자바 IO는 Blocking IO 라서 느리다!

    • Thread에서 블로킹이 발생하기 때문에 느리다.
    • 기존 Java I/O로는 끔찍하게 비효율적인 Server Program(네트워크 서버)을 만든다.
      • 기존 I/O의 Server는 블로킹 되므로 느리다.
      • 클라이언트 접속할 때마다 Thread가 생성됩니다.

     Thread와 동시성 문제

    Thread Life Cycle

    JAVA NIO(new IO)

    개요 ? Non-blocking I/O

    NIO는 왜 등장한 것인가?

    Channel 

    NIO는 기존의 Java I/O보다 왜 더 빠른가?NIO는 Direct Buffer 로 커널 버퍼를 직접 핸들링하기 때문에 빠르다!

    • NIO에서는 커널 버퍼를 직접 이용할 수 있게 해주는 Buffer Class를 지원한다! 커널 버퍼를 직접 이용할수 있는건 ByteBuffer.directAllocate(SIZE); 로 생생된 ByteBuffer뿐이며, 다른 Buffer들은 기존의 방식과 똑같다.

    NIO에서 System Call을 간접적으로 사용가능하게 해주기 때문에 빠르다!

    • c나 c++로 만들어진 Server Program은 Thread를 생성하지 않고도 많은 수의 클라이언트를 처리할 수 있습니다. 이를 가능케 해주는 것이 OS 레벨에서 지원하는 Scatter/Gather 기술과 Select() 시스템 콜입니다. Scatter/Gather은 시스템콜의 수를 줄이는 기술인데요, 덕분에 I/O를 빠르게 만들 수 있죠.
    • 이런 것을 가능하게 해주는 Class가 바로 Channel과 Selector입니다.

    NIO 사용하기

    • http://eincs.net/2009/08/java-nio-bytebuffer-channel/ 문서 참고한다. 중요한 내용만 정리하면 다음과 같다.
    • 커널 버퍼를 직접 접근하려면 ByteBuffer를 사용해야 한다.
    • Buffer 에는 현재 쓰거나 읽을 위치, 유효하게 읽을 수 있는 위치, 현재 용량의 위치 등을 나타내는 포인터가 네가지가 있습니다. position, limit, capacity, mark 로 붙여진 이 네가지 포인터에 대해서 빠삭하게 숙지하고 계셔야 Buffer를 잘 사용하실 수 있습니다. 다음은 이 네 가지 포인터에 대한 간략한 설명입니다.
      • position : 현재 읽을 위치나 현재 쓸 위치를 가리킵니다.. ByteBuffer에서 get()함수로 읽기를 시도할 경우 position위치부터 읽기 시작하며, put()함수로 ByteBuffer에 쓰기를 시도할경우 position 위치부터 쓰기를 시작합니다.. 읽거나 쓰기가 진행될 때마다 position의 위치는 자동으로 이동합니다.
      • limit : 현재 ByteBuffer의 유요한 쓰기 위치나 유효한 읽기 위치를 나타냅니다. 다시 말해, "이 버퍼는 여기까지 읽을 수 있습니다" 혹은 "여기까지 쓸 수 있습니다"를 나타냅니다. 헷갈리시죠? 자세한 사용법은 아래서 알아보도록 합시다. 다르게 말하면 "여기서부터는 쓸 수 없습니다", "여기서부터는 읽을 수 없습니다" 라고 표현 가능합니다.
      • capacity : ByteBuffer의 용량을 나타냅니다. 따라서, 항상 ByteBuffer의 맨 마지막을 가리키고 있습니다. 그 때문에 position과 limit와는 달리 그 위치랄 바꿀 수가 없죠^^
      • mark : 편리한 포인터입니다. 특별한 의미가 있는 것은 아니고, 사용자가 마음대로 지정할 수 있습니다. 특별히 이 위치를 기억하고 있다가 다음에 되돌아가야할 때 사용합니다. 이 포인터에 대해선 차차 사용할 일이 있을 때 사용하실테고, 이 포스팅에선 자세히 다루지는 않겠습니다.
    • 예제를 통해 이 4가지 개념을 명확히 이해할 수 있도록 하면 좋겠다.
    • NIO의 Channel은 Buffer에 있는 내용을 다른 어디론가 보내거나 다른 어딘가에 있는 내용을 Buffer로 읽어들이기 위해 사용됩니다. 예를 들면 네트워크 프로그래밍을 할 때 Socket을 통해 들어온 내용을 ByteBuffer에 저장하기 위해서나, ByteBuffer로 Packet을 작성 후 Socket으로 흘려 보낼 때 Channel을 사용합니다
    • FileChannel은 Blocking 모드만 가능하다. 그 이유는 각 운영체제나 시스템다 File 입출력시 Non-Blocking을 지원해주지 않는 시스템이 있기 때문이라고 한다.
    • 네트워크 프로그래밍을 담당하는 ServerSocketChannel이나 SocketChannel의 경우는 Selector를 활용해 Non-Blocking 프로그래밍이 가능하다.
    • Channel은 직접 인스턴스화 할 수가 없다!, OutputStream/InputStream에서 만들어야한다!

    Reactor Pattern

    네트워크 개발 패턴

    Netty

    NIO기반의 라이브러리

    NIO 참고 문서

    Java IO, NIO에 대한 초간단 예제

    윈도우즈 파일시스템 이론

    http://cappleblog.co.kr/538

     윈도우즈 8 파일시스템 refs

    http://www.infoq.com/news/2012/01/ReFS

    Java IO로 파일 읽고, 쓰기(또는 다른 예제)

    • 기존에 이미 많이 사용한 경험이 있다.
    • 파일 읽고, 쓰는 정도의 간단한 예제면 충분하지 않을까?

    Java NIO로

    웹 서버 구현

    프로세스(Process): 컴퓨터에서 연속적으로 실행되고 있는 프로그램을 말하는 것으로 여러개의 프로세서를 사용하면 멀티프로세싱이고 여러개의 프로그램을 같은 시간에 시분할 방식으로 돌리면 멀티태스킹이라 말한다.

    쓰레드(Thread): 프로세스내에서 실행되는 흐름의 단위를 말하는 것으로 하나의 프로그램은 최소한 하나의 쓰레드(main thread)를 가진다. 프로그램에 따라 둘 이상의 쓰레드를 동시에 실행할수 있는데 이를 멀티쓰레드라고 부른다.

    Java 기본 IO를 활용한 웹 서버 구현

      public class StudyServer {
        private String final int PORT = 8080;


        public static void main(String ar[])throws IOException{
              //서버생성
             HttpServer server = HttpServer.create(new InetSocketAddress(PORT) , 0);
           //context생성
           server.createContext("/study", new WebServerHandler());
          server.start();
          System.out.println("server started..");
        }




        private static class WebServerHandler implements HttpHandler{
           public void Handle(HttpExchange t) throws IOException{
             System.out.println("Accepted");
                String response ="i `m web server";
              t.sendResponseHandlers(HttpURLConnection.HTTP_OK.response.lengh() );
                OutputStream os = t.getResponseBody();
                os.write( response.getBytes() );
                System.out.println("complete");
                os.close();
            }
        }
    }
    • https://github.com/javajigi/slipp-java에 초간단 웹 서버 버전 구현해 올려 놓음.
      • Web Server - IO.pdf 에 웹 서버를 구현하는 단계를 Step By Step으로 구성해 놓음. 이 소스 코드를 바탕으로 Step By Step으로 웹 서버 구현하고 리팩토링하는 연습 가능

    Java NIO를 활용한 웹 서버 구현

    실습계획

    • 다음과 같이 진행해 보면 어떨까?
    • github 어딘가에 아래와 같은 템플릿 소스 코드 
    • 1단계 : Java IO를 활용해 Web Server 구현
      • 짝 끼리 서버 / 클라 번갈아가면서 실습
      • 네트윅은 짝끼리 무조건 되게 하는 걸로.. 그런걸로...
      • 예제를 확장하거나.. 통신스펙을 보고 직접 만들거나
    • 2단계 : Java NIO를 활용해 Web Server 구현(실습 시간이 너무 길어질 경우 이 단계는 데모로만 구현해도 좋지 않을까?)
      • Java IO로 구현한 예제를 Java NIO로 구현
    • 3단계 : Netty를 활용해 Web Server 구현

    sllip study.pptx

    서버를 만들기 위한 Tip

    • 종료처리 (addShutdownHook)
      1. 쓰레드의 작동이 끝났을 때
      2. 프로그램이 종료되었을 때 
      3. 사용자가 CTRL + C를 눌렀을 때
      4. 운영체제가 종료되거나 사용자가 Log Off해서 세션이 더 이상 존재하지 않을 때
    • java.lang.Runtime.addShutdownHook 
    • runFinalizersOnExit 은 종료 시에 안정성을 이유로 deprecated 되었음.
    public class ShutdownTutorial {
        public void addShutdownHandler() {
            Runtime.getRuntime().addShutdownHook(new Thread(){
                @Override
                public void run(){
                    System.out.println("프로그램이 종료되었을 때 동작합니다.");
                             @Todo
                              //사용자 최종 변경 내역 저장, 로그 저장 , 최종 세션 저장
                }
            });
        }
         
        public static void main(String ar[]){
            ShutdownTutorial tutorial = new ShutdownTutorial();
            tutorial.addShutdownHandler();
            System.out.print("프로그램을 종료합니다.\n");
            System.exit(0);
        }
    }

    참고자료

    728x90
    반응형

    'vert.x' 카테고리의 다른 글

    앱 플랫폼 개발 : Vert.x 개념, Listen 프로젝트 예제  (0) 2021.03.26
    Restful API 디자인 가이드  (0) 2021.03.26
Designed by Tistory.