오늘은 지난시간에 자세히 다루지 못했던 Tomcat Connector에 대해 공부해보려고 한다.
Connector는 클라이언트와의 요청을 처리해 서버(서블릿 컨테이너)로 넘겨주는 역할을 맡는다.
Tomcat에는 크게 AJP Connector와 HTTP Connector 2가지가 있다.
이 두 Connector는 BIO Connector(현재는 사용 X), NIO Connector, NIO2 Connector, Apr Connector로 구현된다.
참고로 BIO Connector는 Tomcat 9.0부터 사용할 수 없다.
Tomcat 9.0부터 NIO 및 NIO2 Connector에서 OpenSSL을 사용하여 TLS(TLS/SSL) 암호화를 지원할 수 있다.
아래 표를 참고하면 커넥터에서 JSSE와 OpenSSL을 지원하는 Tomcat 버전을 확인할 수 있다.
JSSE란 Java Secure Socket Extension)의 약어이다.
Java 플랫폼에서 보안 네트워크 통신을 제공하는 API로, JSSE는 SSL (Secure Sockets Layer) 및 TLS (Transport Layer Security) 프로토콜을 구현하여, Java 애플리케이션이 안전하게 데이터 전송을 할 수 있도록 도와준다.
큰 분류의 AJP Connector과 HTTP Connector은 간단하게 살펴보고 넘어가겠다.
AJP Connector
AJP는 Apache JServ Protocol의 약어다.
웹 서버(예: Apache HTTP Server)와 애플리케이션 서버(예: Apache Tomcat) 간의 통신을 효율적으로 처리하기 위해 설계된 바이너리 프로토콜이다.
AJP Connector는 AJP 프로토콜을 통해 웹 커넥터와 통신하는 커넥터다.
이는 Tomcat을 기존 또는 새로운 Apache 설치에 보이지 않게 통합하고자 할 때 사용된다.
Apache가 웹 애플리케이션에 포함된 정적 콘텐츠를 처리하거나 Apache의 SSL 처리를 활용하려는 경우에 유용하다.
공식문서를 읽어보면 AJP를 지원하는 일부 네이티브 Connector들이 공식적으로 지원되지 않는다고 한다.. (작동은 한다)
HTTP Connector
탐갯에서 제일 기본으로 설정되는 Connector이다. 말 그대로 HTTP 요청을 읽고 써서 서버에 보내는 커넥터이다.
HTTP1.0, HTTP 1.1, HTTP 2.0을 지원하는 Connector들이 각각 존재한다.
이제 본격적으로 중요한 개념인 BIO Connector, APR Connector, NIO Connector에 대해 알아보겠다.
BIO Connector
- 우선 BIO는 Blocking IO의 줄임말이다.
- Blocking 기반 서블릿 IO과 함께 잘 동작하며 비동기 서블릿과 웹소켓과 사용하는 것은 적합하지 않다.
- 한 개의 TCP 커넥션 당 하나의 스레드가 할당된다. 연결이 유휴 상태일 때도 대기한다.
- 스케일링을 하기 쉽지 않다. 스케일을 하기 위해서는 keep-alive를 비활성화해야 한다.
- 그러나 keep-alive를 활성화하지 않으면 지속적인 HTTP 요청을 진행할 수 없고 매번 커넥션을 맺어야 하므로 성능상 문제가 생길 수 있다.
APR Connector
- APR (Apache Portable Runtime) Connector는 Apache Tomcat에서 고성능의 네이티브 라이브러리를 사용하여 네트워크와 파일 I/O 작업을 처리하는 Connector이다.
- Blocking과 Non-Blocking을 모두 지원한다.
- 처리가 필요한 각 커넥션마다 하나의 스레드가 할당된다. 커넥션이 유휴 상태일 때 대기하지 않는다.
- 유휴 상태를 관리하기 위해 폴링 메커니즘을 사용한다. 즉 폴링을 통해 커넥션의 상태를 계속체크한다.
- 스케일링하기 좋다.
NIO Connector, NIO2 Connector
- NIO는 Non-Blocking의 약어다.
- 처리가 필요한 각 커넥션마다 하나의 스레드가 할당된다. 연결이 유휴 상태일 때 대기하지 않는다.
- NIO Connector에서는 유휴 상태를 관리하기 위해 폴링 메커니즘을 사용한다. 즉 폴링을 통해 커넥션의 상태를 계속체크한다.
- NIO2 Connector에서는 유휴 상태를 관리하기 위해 콜백 패턴을 사용한다. 이는 폴링을 주기적으로 하지 않아도 되서 더 효율적이다.
- 스케일링하기 좋다.
BIO Connector가 유휴 커넥션을 계속 붙잡고 있으므로 다른 Connector들이 등장한 것으로 보인다.
어떻게 NIO Connector가 유휴 상태를 관리할까?
간단하게 생각해보면 BIO가 NIO로 바뀌면서 가능해진 일이다. (APR은 빼고 생각)
그렇다면 자바 NIO 기술을 Connector에 도입했기 때문에 유휴 상태 관리가 가능한 것이다.
그럼 자바 NIO 기술을 살펴보면 유휴 상태 관리가 가능한 이유를 확인할 수 있을 것이다.
한번 알아보자..!
Java NIO (Java New Input Output)
Java NIO는 기존 Java IO의 대안으로 고성능 I/O를 처리하기 위해 Java 1.4 버전에서 등장했다.
기존 Java IO가 스트림 지향적이다. 이 말의 의미는 스트림에서 한 번에 1 바이트 이상을 읽을 수 있다는 것이다.
데이터 소스에서 데이터 싱크로 데이터를 전송하기 위해 스트림을 사용하며, 단방향 데이터 전송이 특징이다.
Java IO에서는 객체에 직접 데이터를 쓰고 스트림 객체에서 직접 데이터를 읽는다.
Java NIO는 버퍼 지향적이다. Java NIO 버퍼는 쓰거나 읽은 데이터를 보유할 수 있다.
버퍼에서 데이터를 읽으면, 채널을 사용해 추가로 어떤 작업을 처리할 수 있다.
위에서 Java IO와는 다르게 스트림 대신 채널이라는 새로운 개념이 등장했다.
채널은 스트림과 유사하며 데이터 소스/싱크와 데이터 전송을 위한 Java 애플리케이션의 커넥션(연결)을 의미한다.
스레드가 채널에 데이터를 버퍼로 읽어오도록 요청하면, 채널이 데이터를 버퍼로 읽는 동안 동시에 스레드는 다른 작업을 할 수 있다.
데이터가 버퍼로 읽힌 후, 스레드는 읽기 작업 중에 중단했던 작업을 계속 처리할 수 있다.
따라서, NIO는 양방향 데이터 전송이 가능하다.
Java NIO 라이브러리에서는 모든 데이터가 버퍼로 처리된다.
다시 한번 강조하자면
- 데이터를 읽으면 버퍼로 직접 읽혀지며 데이터가 기록되면 버퍼에 기록된다.
- NIO의 데이터에 액세스할 때마다 해당 데이터를 버퍼에서 꺼낸다.
Java IO는 블로킹 IO다.
스레드가 read() 또는 write() 작업을 호출할 때, 읽을 데이터가 있거나 데이터가 완전히 작성될 때까지 해당 스레드가 블록된다.
Java IO와 달리, Java NIO는 논블로킹 IO다.
스레드가 read() 또는 write() 작업을 호출할 때, 읽을 데이터가 있거나 데이터가 완전히 작성될 때까지 스레드가 블록되지 않고, 대신 다른 작업을 수행할 수 있다.
Selector
Java NIO 패키지에는 버퍼와 채널 말고도 핵심 요소(클래스)가 하나 더 존재한다.
바로 Selector이다. Selector를 사용하면 싱글 스레드에서 다중 채널을 쉽게 처리할 수 있다.
Selector가 여러 채널을 등록한 후 단일 스레드를 사용해 처리할 수 있는 입력이 있는 채널을 선택하거나 쓰기 준비가 된 채널을 선택할 수 있는데, 이 메커니즘을 통해 단일 스레드가 여러 채널을 쉽게 관리할 수 있다.
다중 스레드를 사용하는 것보다 보다 자원을 효율적으로 사용할 수 있다..!
Selector은 Reactor Pattern을 따르고 있으며, 여러 개의 입력 신호를 하나의 출력 신호로 결합해주는 멀티 플렉서 역할도 맡고 있다.
Selector로 인해 NIO Connector가 유휴 커넥션이 있을 때 스레드가 대기하지 않도록 만든다.
Tomcat에서 NioConnector 클래스의 실제 이름은 org.apache.coyote.http11.Http11NioProtocol이다.
마찬가지로 Tomcat NioConnector2 클래스의 실제 이름은 org.apache.coyote.http11.Http11Nio2Protocol이다.
Http11NioProtocol가 사용하는 NioEndpoint에는 Poller라는 인스턴스 변수가 존재한다. 해당 변수가 Polling 작업을 맡는다.
Poller 클래스 내부에는 셀렉터도 변수로 존재한다.
Http11Nio2Protocol가 사용하는 Nio2Endpoint 클래스의 정적 클래스로 Nio2SockectWrapper가 CompletionHandler을 적극적으로 사용하면서 콜백 패턴을 사용한 비동기 I/O 작업을 처리하는 것을 확인할 수 있었다.
참고자료
https://www.youtube.com/watch?v=LBSWixIwMmU
https://www.baeldung.com/java-nio-selector
https://www.geeksforgeeks.org/difference-between-java-io-and-java-nio/
https://medium.com/@nilasini/java-nio-non-blocking-io-vs-io-1731caa910a2
자바 네트워크 프로그래밍
댓글