1. 버퍼를 사용할 때
: 버퍼를 null로 만들어서 카비지 커렉션 대상으로 하는것은 느려지는 원인
그러므로 ByteBufferPoll을 사용하여 메모리를 재사용하고 매번 ByteBuffer 객체를 생성, 해제에 따른 가비지 생성 부담이 줄어든다
2. 비효율적인 데이터 전송
: 클라이언트가 100byte를 보냈는데 그중 1byte만 먼저 서버에 도착했을때,
ⓐ 읽기 준비가 되었다고 셀렉션키의 레디키에 표시
ⓑ select()가 호출되고 isAcceptable(), isReadable() 이 호출
ⓒ read로 1byte를 읽고 다시 1번부터 반복한다.
: 읽기의 경우 무조건 read() 메소드를 두번 호출
//데이터의 일부만 전송되었을 수도 있으므로 두번 read 한다. for (int i = 0; i < 2; i++) { sc.read(buffer); } |
쓰기의 경우 compact()또는 hasRemaining() 사용하여 아직 버퍼의 내용을 다 보내지 못한경우 곧바로 다시 전송
Iterator iter = ChattingRoom.getInstance().iterator(); while (iter.hasNext()) { SocketChannel member = (SocketChannel) iter.next(); if (member != null && member.isConnected()) { //데이터 전송이 일부만 되었을 수 있으므로 버퍼를 확인해서 모두 보낸다 while (buffer.hasRemaining()) { member.write(buffer); } //position 초기화 buffer.rewind(); } } |
3. 동시성을 이용한 성능 극대화
서버의 효율적인 측면을 강화하기 위해서는 쓰레드를 어떻게 활용하느냐가 중요하다
ⓐ Accept, Process 분리
- accept()는 상당히 느린 작업이다. 한명과의 접속이 0.01초라면 동시에 만명이 접속했을때 마지막사람은 1분30초가 걸린다
- accept 에 해당하는 부분은 별도의 스레드로 셀렉터로 분리시켜서 클라이언트의 접속을 전담하게 한다
- 나머지 이벤트 Connect, Read, Write 도 각각 별도의 처리 로직으로 분리하는 것이 대용량 서버에 유리하다
ⓑ SelectorPool 사용
- 셀렉터 하나에 동시접속자가 만 명일때, 관리할 채널이 너무 많아 비효율 적이다
- Read 를 수행하는 셀렉터 n개로 만들어서 사용하는 것이 효과적이다
ⓒ ThreadPool 사용
- Accept를 처리하는 것과 Processe를 처리하는 것으로 나눠서 별도의 독립적인 쓰레드풀로 운영하는것이 좋다
- 병목지점을 파악했을때 그쪽의 스레드 개수를 늘려주면 된다
'코딩 내공 Project > IO&NIO 네트워크' 카테고리의 다른 글
IO 클래스 (0) | 2011.08.09 |
---|---|
[NIO] 셀렉터 부분 정리 (0) | 2011.05.08 |
[NIO] Chanel 부분 정리 (0) | 2011.05.07 |
[NIO] Buffer 부분 정리 (0) | 2011.05.05 |
생성자 - 소비자 (0) | 2011.03.16 |