자바네트워크 제15주 Java NIO 버퍼, 채널, 셀렉터
Java NIO 개요 필요성 자바IO는 느리다? 블록킹 자바 IO IO 향상을 위한 운영체제 수준의 기술 자바의 새로운 변화 논의
필요성 자바 IO는 native 보다 상대적으로 느렸다. Nonblocking IO 기능
자바 IO는 느리다? 자바가 느린 부분은 swing과 IO 이다. 사용이 간편하다.
블록킹 자바 IO IO의 흐름 유저영역 커널영역 하드웨어 ㅍ 프로세스 read() DMA 디스크 버퍼 디스크 콘트롤러 버퍼
IO 향상을 위한 운영체제수준의 기술 Buffer Scatter/Gather One byte read Using buffer Full buffer(enough size) Scatter/Gather Using one system call for three buffers 버퍼 버퍼 버퍼 버퍼
Buffer 크기에 따른 차이 NonBuffer.java SmallBuffer.java FullBuffer.java 작은 Buffer 크기를 사용한 경우(2048 bytes) FullBuffer.java 파일 크기만큼의 버퍼를 사용한 경우
NonBuffer 소스 자료크기 만큼의 system call
SmallBuffer 소스 자료크기를 Buffer의 크기로 나눈 만큼의 system call outBuffer.write(byteData);
FullBuffer 소스 단 한 번의 system call
실행 결과
가상 메모리와 물리메모리 가상메모리 물리메모리 가상메모리
가상 메모리(Virtual Memory) 커널영역에서 유저영역으로 복사할 필요가 없음 유저영역 커널영역 하드웨어 ㅍ 프로세스 read() DMA 디스크 버퍼 디스크 콘트롤러 버퍼 물리메모리
메모리 맵 파일 디스크 페이지와 메모리를 일치시킴 (MappedByteBuffer) 커널영역 유저영역 하드웨어 페이지 매핑 ㅍ 프로세스 read() 디스크 버퍼 디스크 콘트롤러 버퍼 물리메모리
파일 락 스레드의 동기화와 비슷하다. 프로세스가 파일을 사용하는 동안 다른 프로세스가 같은 파일에 접근하는 방식을 제한한다. Shared lock : 읽기 작업 Exclusive lock : 쓰기 작업
공유락/배타락 reader reader writer 배타락 공유락 공유락
공유락/배타락 reader reader writer 공유락 공유락 배타락
자바의 새로운 변화 자바의 포인터 버퍼 도입 네이티브 IO서비스를 제공해주는 채널 도입 셀렉터 도입 DiectByteBuffer 네이티브 IO서비스를 제공해주는 채널 도입 스트림과는 다르게 읽거나 쓰거나, 읽고 쓰거나 할 수 있다. 셀렉터 도입 네트워크 프로그래밍의 효율을 높이기 위한 것이다. 준비된 채널을 효과적으로 셀렉트할 수 있도록 해준다.
An Example Server Selector register select watch Channel Pool Server Channel(OP_ACCEPT) Selector Client Channel(OP_READ) register select Client Channel(OP_READ) watch Channel Pool OP_CONNECT OP_READ
버퍼 필요성 버퍼개요 버퍼의 네 가지 기본 속성 버퍼에서 데이터 읽고 쓰기 Buffer 클래스가 제공하는 유틸리티 메소드 버퍼 만들기 ByteBuffer CharBuffer 채널에서 버퍼 사용하기 논의
필요성 Native 코드가 빠르다는 통념을 버려라. 주석이 필요 없을 정도로 프로그램은 간결하게 작성하라. 병목 현상은 20% 정도 밖에 되지 않는다 기존의 잘 검증된 라이브러리를 활용하라. 프로그램하려는 대상을 이해하고 가장 효율적인 구조를 만들어라.
버퍼개요 자바에 직접 시스템메모리를 활용할 수 있도록 함.
버퍼 개요 버퍼 클래스 기능 Buffer Position, limit, and capacity; Clear, flip, rewind and mark/reset ByteBuffer Get/put, compact, views; allocate, wrap MappedByteBuffer A byte buffer mapped to a file CharBuffer Get/put, compact; allocate, wrap ShortBuffer IntBuffer LongBuffer FloatBuffer DoubleBuffer ByteOrder Typesafe enumeration for byte orders
버퍼의 계층 구조
java.nio Package
Little/Big endian
버퍼의 네 가지 기본 속성 Capacity
버퍼의 속성
버퍼의 함수
버퍼의 함수
Chain 형식 사용 버퍼의 함수는 모두 리턴 값이 다시 버퍼이므로 함수 호출을 연결하여 사용하는 chain 방법을 사용할 수 있다. 다음과 같은 네 줄의 코드를 buf.position(1); buf.mark(); buf.position(5); buf.reset(); 다음과 같이 한 줄로 표현할 수 있다. buf.position(1).mark().position(5).reset()
버퍼에서 읽고 쓰기 1 바이트씩 상대적 위치로 읽고 쓰기: 1 바이트씩 절대적 위치로 읽고 쓰기: get(), put(byte) 1 바이트씩 절대적 위치로 읽고 쓰기: get(index), put(index, byte) 배열을 사용한 다량의 데이터 읽고 쓰기: get(byte[]), get(byte[], offset, length) put(byte[]), put(byte[], offset, length) 버퍼를 매개변수로 사용 : put(Buffer)
get position limit capacity position limit capacity
put position limit capacity position limit capacity
예제 프로그램 RelativeBuffer
get(index) position limit capacity index position limit capacity
put(index) position limit capacity position index limit capacity
AbsoluteBuffer
get(byte[] bulk) position limit capacity byte[] bulk position capacity
BulkRead
put(byte[] bulk) position limit capacity byte[] bulk position capacity
BulkWrite
put(ByteBuffer) position limit capacity byte[] bulk position limit
clear position limit capacity position limit capacity
rewind position limit capacity limit position capacity
flip limit->position position -> 0 position limit capacity limit
compact position limit capacity position limit capacity
duplicate Memory 동일한 메모리 참조 buf buf2 mark position limit capacity
DuplicateBuffer
asReadOnlyBuffer 동일한 메모리 참조, 읽기만 가능 mark position limit capacity
slice position limit capacity position limit capacity 동일한 메모리 참조
SliceBuffer
버퍼 만들기
DirectBuffer allocateDirectBuffer() view Buffer
nondirect/direct buffer allocate, wrap 에 의해 생성된 buffer는 모두 JVM heap 영역에 저장되는 nondirect buffer 이다. allocateDirect 에 의해 생성되는 버퍼는 시스템 메모리를 사용하는 direct buffer 이다. ByteBuffer 만이 direct buffer 를 생성할 수 있다. 다른 버퍼가 direct buffer 를 사용할 수 없는 이유는 OS 마다 자료형을 표현하는 방식이 다르기 때문이다. Direct buffer 를 생성하는 일은 매우 시간적으로 오래 걸리는 일이다. Channel 에서는 항상 direct buffer 를 사용해야 한다. 다른 종류의 버퍼를 사용하면 효율이 떨어진다.
ViewBuffer ByteBuffer 를 원하는 자료형 버퍼로 wraping 하는 방법 position limit capacity position limit capacity
ViewBuffer
OtherType
ByteOrderTest
CharBuffer 2바이트 문자도 하나로 카운트 한다.
CharBuffer 버퍼의 일부 만을 조작할 수도 있다.
동기화 문제 Buffer 의 모든 메소드는 동기화 되어 있지 않다. 따라서 개발자가 동기화에 신경을 써야 한다.
채널에서 버퍼 사용하기 read() write() position limit capacity position limit
채널(Channel) 필요성 채널개요 채널의 기본인터페이스 파일채널 소켓채널 논의
필요성 채널은 궁극적으로 native 자료에 매핑하는 것이다.
채널개요 Channel 파일 또는 소켓의 native IO 서비스를 활용하도록 해준다. Interface 임. ByteBuffer ByteBuffer Channel
Java Document
채널의 특징 데이터의 타겟이 ByteBuffer 이다. 직간접적으로 native IO 서비스를 사용하여 빠르다. 일반적으로 양방향 통신이 가능하다. (파일 채널은 추가 설정이 필요하다.)
채널의 기본 인터페이스 Interface 기반으로 작성하여 플랫폼이 다르더라도 같은 동작을 할 수 있도록 동작의 정의만 기술함. Channel WritableByteChannel ReadableByteChannel InterruptibleChannel GatheringByteChannel ByteChannel ScatteringByteChannel DatagramChannel SocketChannel FileChannel Selector SelectionKey
Channel, InterruptibleChannel
StreamTest StreamTest TestThread IO 상태의 불일치 발생 실행 결과
TestThread
ReadableByteChannel, WritableByteChannel, ByteChannel ByteChannel read and write
SimpleChannelTest 한글을 입력할 때는 마우스로 커서를 내린 후에 입력 ^Z로 종료
ScatteringByteChannel, GatheringByteChannel
Scatter 동작 channel.read(buffers)
ScatterTest
Gather 동작 channel.write(buffers)
GatheringTest
파일채널
파일채널의 특징 항상 블록킹 모드로 동작한다. 파일채널 객체는 직접 만들 수 없다. 비블록킹모드로 설정할 수 없다. 파일채널 객체는 직접 만들 수 없다. 열려있는 파일스트림의 getChannel() 함수를 사용해야 한다. 파일채널도 native IO 서비스를 최대한 사용하려고 한다. 파일채널 객체는 스레드에 안전하다.
파일채널 속성
파일채널 속성
SharedFileChannelInstanceTest
파일락킹(File Locking) 파일락킹은 JVM내의 프로세스사이의 관계가 아니라 JVM 외부의 프로세스(다른 프로그램)과의 관계를 설정한다.
FileLock
사용상 주의 항상 사용 후에는 release 해 주어야 한다.
FileLockTest
메모리 매핑 FileChannel.map() MappedByteBuffer File
MapMode 매핑은 한번 설정되면 Buffer가 없어질 때까지 지속된다.
MappedByteBuffer
방법에 따른 시간 측정 CopyIOTest CopyMaptest CopyByteBufferTest CopyDirectByteBufferTest
채널간 직접전송
소켓 채널 비블록킹 IO 가능 멀티플렉스 IO 지원 (Selector)
SelectableChannel
ServerSocketChannel
SocketChannel methods
DatagramChannel
셀렉터(Selector) 필요성 셀렉터의개요 기존의 네트워크 프로그래밍 모델 비블록킹 모델 SelectableChannel SelectionKey Selector 비블록킹 서버 만들기 논의
필요성 멀티스레드 서버는 성능에 한계가 있음 Thread context switching is expensive 셀렉터를 이용한 멀티플렉스 모델이 효율적임
셀렉터의 개요
기존의 네트워크프로그래밍 모델(single thread) Socket s = serverSocket.accept(); InputStream in =s.getInputStream(); OutputStream out = s.getOutputStream(); in.read(); out.write(); s.close();
멀티스레드 기반 서버 Service Thread Socket s = serverSocket.accept(); Service service = new Service(s); service.start(); InputStream in =s.getInputStream(); OutputStream out = s.getOutputStream(); in.read(); Service Thread out.write(); s.close();
멀티스레드 기반의 단점 많은 스레드 생성에 따른 스레드 컨텍스트 스위치 부하 스레드 자체가 CPU와 고유 스택을 갖는 데 따른 컴퓨터 리소스 부하 클라이언트의 빈번한 접속과 종료에 따라 많은 가비지가 생성되는 문제점 클라이언트가 접속할 때마다 매번 스레드를 새로 생성하는 부담 서버의 메모리가 부족하여 OutOfMemoryException 이 발생할 가능성
비블록킹 모델 in.read(); Do something Selector SelectableChannel SelectionKey 블록킹 모드의 자료 읽기 in.read(); Do something
비블록킹 모드 동작 과정 Selector 생성 Selector에 SelectableChannel 등록 Selector로부터 준비된 SelectionKey 검색 SelectionKey로 부터 Channel 작업
SelectableChannel
Methods
이벤트 종류
서버소켓의 등록
일반소켓채널 등록 채널은 한 Selector에 한번밖에 등록이 되지 않는다. 여러 번 등록하면 마지막 등록만이 유효하다.
가능한 이벤트 ServerSocketChannel SocketChannel DatagramChannel OP_ACCEPT SocketChannel OP_CONNECT, OP_READ, OP_WRITE DatagramChannel OP_READ, OP_WRITE Pipe.SinkChannel OP_WRITE Pipe.SourceChannel OP_READ
동작 Selector SelectionKey Write SelectionKey Read SelectionKey Read & Write Selector
SelectionKey
Methods
Methods
SelectionKey의 동작별 이벤트 OP_ACCEPT OP_CONNECT OP_READ OP_WRITE 클라이언트가 ServerSocketChannel에 접속을 시도했을 때 발생한다. OP_CONNECT 서버가 클라이언트 접속을 허락했을 때 나타난다. OP_READ 서버가 클라이언트의 요청을 read 할 수 있을 때 발생한다. OP_WRITE 서버가 클라이언트에 응답을 write 할 수 있을 때 발생한다.
Selector Registered Key Set Selected Key Set Cancelled Key Set 채널들이 발생시킨 이벤트를 적절한 이벤트처리기로 분기 시켜주는 콘트롤러 역할을 한다.
Methods
Selection 동작 Cancelled Key Set Registered Key Set Selected Key Set Cancelled Key Set을 조사하여 이 Set에 있는 Key가 Registered Key set와 Selected Key set에 있으면 삭제한다. 이 작업이 끝나면 이 Set는 비워진다. Registered Key Set Selected Key Set Cancelled Key Set Registered Key Set을 조사하여 이 Set에 있는 Key가 ready가 되었으면 Selected Key set로 옮겨 준다. Selected Key Set을 조사하여 각 Key 해당하는 적절한 핸들러로 이벤트를 넘긴다.
select() select(long timeout) selectNow() wakeup() 블록킹 메소드이다. 호출 순간에 Selected Key Set가 비어 있으면 기다린다. select(long timeout) 블록킹 메소드이다. Selected Key 가 존재하거나 시간이 timeout이 지날 때 까지 블록 된다. selectNow() 비블록킹 메소드이다. Selected Key 가 없으면 바로 0을 return 한다. wakeup() 블록킹 된 동작을 해제 한다.
ArrayList room : 접속자와 연결된 SocketChannel 집합 비블록킹 채팅 서버만들기 ArrayList room : 접속자와 연결된 SocketChannel 집합 SimpleChatServer SimpleChatClient 서버 루프 서버 Main 스레드 MyThread
SimpleChatServer main
로그초기화(initLog) 로거(Logger) 클래스는 표준적인 로그를 생성할 수 있도록 해주는 클래스이다. 로거 인스턴스는 static 함수인 getLogger를 통하여 얻을 수 있다. 로그를 남기는 대상에 따라 ConsoleHandler, FileHandler, SocketHandler, MemoryHandler 를 사용한다. 이 예제에서는 FileHandler를 사용하여 파일에 로그를 남겼다. 로거의 log 함수와 info 함수를 이용하여 로그를 남길 수 있고 level에 따라 로그의 양을 조절할 수 있다.
initLog 함수
initLog 함수
서버초기화(initServer)
initServer 함수
서버시작(startServer)
startServer
Accept 작업(accept)
accept()
Read 작업(read)
read()
클라이언트 SocketChannel 등록
registerChannel()
사용자 등록(addUser) room에 접속한 클라이언트의 SocketChannel 추가
다른 접속자에게 전달(broadcast) room 에 있는 다른 접속자에게 대화 내용 전달 flip을 연속해서 두 번 사용하지 않도록 주의 한다.
boardcast()
SimpleChatClient 각자 소스를 보고 분석하기 바랍니다.
향상된 서버 만들기 필요성 들어가기에 앞서 효율적인 메모리 사용 비효율적인 데이터 전송에 대한 고려 동시성을 이용한 성능 극대화 Accept process 분리 Selector Pool 사용 Thread Pool 사용 향상된 서버 만들기 논의
필요성 시스템의 동작을 이해하고 문제점을 파악하여 해결
들어가기에 앞서 NIO 기본 만으로는 비효율적인 점을 개선하는 데 한계가 있다.
효율적인 메모리 사용 클라이언트가 접속할 때 마다 만들고 없애기 때문에 비효율적이다.
slice(ByteBufferPool) position limit capacity position limit capacity 동일한 메모리 참조
비효율적인 데이터 전송에 대한 고려 1 byte씩 느리게 전송되는 경우 매우 비효율적으로 동작한다. read() two times Use compact() or hasRemaining()
동시성을 이용한 성능 극대화 Accept process 분리하여 따로 처리하는 것이 효율 적이다.
Selector pool 사용 Use multiple selector for accept and read
Read/Write Thread Pool Read Thread Accept thread Accept Thread Pool Accept Selector Pool Accept Queue Process Events Read Queue Read Selector Pool Read/Write Thread Pool