JEP 353: Reimplement the Legacy Socket API

Reimplement the Legacy Socket API

Summary

java.net.Socketand java.net.ServerSocketAPI에서 사용하는 기본 구현 을 유지 관리 및 디버그하기 쉬운 더 단순하고 현대적인 구현으로 바꿉니다.
새로운 구현은 현재 Project Loom 에서 탐색중인 사용자 모드 스레드 (일명 파이버)를 사용하여 쉽게 적용 할 수 있습니다 .

Motivation

java.net.Socket및 java.net.ServerSocketAPI 및 자신의 기본 구현, JDK 1.0 날짜 다시.
구현은 유지 관리 및 디버깅이 어려운 레거시 Java 및 C 코드의 혼합입니다.
구현시 스레드 스택을 I / O 버퍼로 사용하는데,이 방법은 여러 번 기본 스레드 스택 크기를 늘려야했습니다.
이 구현은 기본 데이터 구조를 사용하여 수년에 걸쳐 미묘한 안정성 및 이식 문제의 원인 인 비동기식 닫기를 지원합니다.
구현에는 또한 여러 가지 동시성 문제가 있으며이를 해결하기 위해 정밀 검사가 필요합니다.
원시 메소드에서 스레드를 차단하는 대신 파킹되는 미래의 광섬유 환경에서 현재 구현은 목적에 맞지 않습니다.

Description

java.net.Socket및 java.net.ServerSocket API를이 모든 소켓 작업을 위임 java.net.SocketImpl, JDK 1.0부터 존재 한 서비스 공급자 인터페이스 (SPI) 메커니즘.
내장 구현을 “일반”구현이라고하며, 비공개 PlainSocketImpl클래스에 의해 지원되는 클래스 SocketInputStream및을 사용 SocketOutputStream합니다.
PlainSocketImplSOCKS 및 HTTP 프록시 서버를 통한 연결을 지원하는 두 개의 다른 JDK 내부 구현으로 확장됩니다.
기본적으로 Socket하고 ServerSocket기반 SOCKS (때로는 느리게)가 생성됩니다 SocketImpl. 의 경우 ServerSocket,
SOCKS 구현의 사용은 JDK 1.4의 프록시 서버 연결에 대한 실험적 (및 제거 된 이후) 지원으로 거슬러 올라가는 이상한 일입니다.

새로운 구현 NioSocketImpl은의 대체품입니다 PlainSocketImpl.
유지 관리 및 디버그가 용이하도록 개발되었습니다.
NIO (New I / O) 구현과 동일한 JDK 내부 인프라를 공유하므로 고유 한 원시 코드가 필요하지 않습니다.
기존 버퍼 캐시 메커니즘과 통합되므로 I / O에 스레드 스택을 사용할 필요가 없습니다.
나중에 섬유와 잘 어울릴 수 있도록 방법 java.util.concurrent synchronized 보다는 잠금을 사용 합니다.
JDK 11에서 NIO SocketChannel및 기타 SelectableChannel구현은 대부분 같은 목표를 염두에두고 다시 구현되었습니다.

다음은 새로운 구현에 대한 몇 가지 사항입니다.

  • SocketImpl레거시 SPI 메커니즘이며 사양이 매우 낮습니다.
    새로운 구현은 지정되지 않은 동작과 해당되는 경우 예외를 모방하여 이전 구현과 호환되도록 시도합니다.
    아래의 위험 및 가정 섹션에는 이전 구현과 새로운 구현 간의 동작 차이가 자세히 설명되어 있습니다.

  • 제한 시간을 사용하여 소켓의 동작은 ( connect, accept, read) 비 차단 모드 폴링 소켓에 소켓을 변경함으로써 구현된다.

  • 이 java.lang.ref.Cleaner메커니즘은 SocketImpl가비지 수집되고 소켓이 명시 적으로 닫히지 않은 경우 소켓을 닫는 데 사용됩니다 .

  • 연결 재설정 처리는 이전 구현과 동일한 방식으로 구현되므로 연결 재설정 후 읽기 시도가 일관되게 실패합니다.

ServerSocket기본적으로 NioSocketImpl(또는 PlainSocketImpl) 를 사용하도록 수정되었습니다 . 더 이상 SOCKS 구현을 사용하지 않습니다.

SocketImpl그들은 과거와 구현 작업 할 수 있도록 지원 SOCKS 및 HTTP 프록시 서버가 수정을 구현 위임합니다.

Java Flight Recorder에서 소켓 I / O에 대한 계측 지원 SocketImpl은 새로운, 기존 또는 사용자 정의 구현으로 실행할 때 소켓 I / O 이벤트를 기록 할 수 있도록 독립적으로 수정되었습니다 .

20 년이 지난 후에 구현 전환의 위험을 줄이기 위해 기존 구현은 제거되지 않습니다.
이전 구현은 JDK에 남아 있으며 이전 구현을 사용하도록 JDK를 구성하기 위해 시스템 특성이 도입됩니다.
이전 구현으로 전환하기위한 JDK 특정 시스템 특성은 jdk.net.usePlainSocketImpl 입니다. true시작시 값을 설정하거나 값으로 설정 하면 이전 구현이 사용됩니다.
향후 일부 릴리스 PlainSocketImpl에서는 시스템 속성 이 제거 됩니다.

이 JEP는 현재 대체 구현을 제안하지 않습니다 DatagramSocketImpl( 대리인 DatagramSocketImpl인스턴스가 기본 구현 java.net.DatagramSocket).
기본 제공 기본 구현 ( PlainDatagramSocketImpl)은 유지 관리 (및 이식) 부담이며 다른 JEP의 주제 일 수 있습니다.

Testing

jdk/jdk저장소 의 기존 테스트는 새 구현을 테스트하는 데 사용됩니다.
jdk_net테스트 그룹은 수년에 걸쳐 코너의 시나리오 네트워킹에 대한 많은 테스트를 축적하고있다.
이 테스트 그룹의 일부 테스트 -Djdk.net.usePlainSocketImpl는 JDK에 두 가지 구현이 모두 포함 된 시간 동안
이전 구현이 비트 로테이션되지 않도록하기 위해 두 번 실행되도록 수정됩니다 .

오늘날 많은 코드 java.nio.channels가 java.net.Socketand java.net.ServerSocketAPI 대신 정의 된 API를 사용하는
라이브러리를 직접 또는 간접적으로 사용 합니다. 제안서에 대한 인식을 높이고 코드를 사용 Socket하고 jdk.java.net
또는 다른 곳에 ServerSocket게시 된 초기 액세스 빌드로 코드를 테스트 하는 개발자를 장려하기 위해 모든 노력을 기울일 것 입니다.

jdk/jdk저장소 의 마이크로 벤치 마크에는 소켓 읽기 / 쓰기 및 스트리밍에 대한 벤치 마크가 포함됩니다.
이러한 벤치 마크는 기존 구현과 새로운 구현을 쉽게 비교할 수 있도록 향상되었습니다.
새로운 구현은 소켓 읽기 / 쓰기 테스트의 기존 구현과 거의 동일하거나 1-3 % 더 우수합니다.

Risks and Assumptions

이 제안의 주된 위험은 기존 구현과 새로운 구현이 다르게 동작하는 경우에 지정되지 않은 동작에 의존하는 기존 코드가 있다는 것입니다.
지금까지 식별 된 차이점이 여기에 나열됩니다. 로 실행하여 처음 두 개를 제외한 모든 것을 완화 할 수 있습니다 -Djdk.net.usePlainSocketImpl.

  • InputStream및 OutputStream의해 반환 PlainSocketImpl의 getInputStream()및 getOutputStream()방법은 확장
    java.io.FileInputStream과 java.io.FileOutputStream각각. 이에 의존하는 기존 코드가있을 수는 있지만 가능하지는 않습니다.

  • ServerSocket사용하여 사용자는 SocketImpl을 반환 연결을 받아 들일 수 Socket있는 플랫폼과를 SocketImpl.
    마찬가지로 ServerSocket플랫폼을 사용하는 경우 custom SocketImpl을 반환하는 연결을 수락 할 수 없습니다 .SocketSocketImpl

  • InputStream및 OutputStream이전 구현에 의해 반환은 EOF 반환 -1 다른 검사 전에 스트림을 테스트합니다.
    새로운 구현은 null 스트림이 EOF인지 확인하기 전에 확인하고 수행합니다.
    점검 순서로 인해 깨지기 쉬운 코드가있을 가능성이 있지만 가능하지는 않습니다.

  • Socket수신 큐에서 읽지 않은 바이트로 a 를 닫으면 기본 소켓이 정상적으로 닫힙니다.
    Microsoft Windows 이외의 플랫폼에서 이전 구현과 동일한 시나리오는 중단 / 강제 종료로 이어집니다.

  • Oracle Solaris 특정 : Oracle Solaris는 응용 프로그램에 “연결 재설정”을보고하는 방식이 다른 플랫폼과 다릅니다.
    예를 들어 네트워크 오류가 발생하면 호출 setsockopt하거나 ioctl실패 할 수 있습니다.
    xnet_skip_checks설정을 구성 할 수 있습니다 /etc/system(이 동작하지 않도록 echo “xnet_skip_checks/W 1” | mdb -kw라이브 시스템을).
    이전 구현에서는 ioctl(FIOREAD)실패한 경우를 처리 하므로 available”연결 재설정”으로 실패한 후 읽기 시도가 일관되게 실패합니다.
    이것은 깨지기 쉽고 유지 보수가 불가능하며 새로운 구현은이 동작을 모방하려고 시도하지 않습니다.

  • Oracle Solaris 특정 : Oracle Solaris에서는 IPV6_TLCASS소켓 옵션을 TCP 소켓에 연결 한 후 소켓 옵션을 변경할 수 없습니다 .
    이전 구현에서는 setTrafficClass메소드에 지정된 값을 캐싱하여이를 마스킹합니다 .

  • java.net패키지는 많은 하위 클래스를 정의합니다 SocketException.
    새로운 구현은 SocketException이전 구현 과 동일한 특정을 던지려고 시도 하지만 동일하지 않은 경우가 있습니다.
    또한 예외 메시지가 다른 경우가있을 수 있습니다. 예를 들어, Microsoft Windows에서
    이전 구현은 Windows Socket 오류 코드를 영어 전용 메시지에 매핑하는 반면 새 구현은 시스템 메시지를 사용합니다.

동작상의 차이 외에도 새로운 워크로드의 성능은 특정 워크로드를 실행할 때 이전과 다를 수 있습니다.
이전 구현 에서는 커널 accept의 ServerSocketwill 큐에서 메소드를 호출하는 여러 스레드 가 있습니다.
새로운 구현에서는 하나의 스레드가 accept시스템 호출 에서 차단되고 다른 스레드는 java.util.concurrent잠금 을 획득하기 위해 대기합니다.
다른 시나리오에서도 성능 특성이 다를 수 있습니다.

마지막으로, 비 공용 java.net.SocketInputStream및 java.net.SocketOutputStream 클래스를 계측하여 I / O 이벤트를 가져 오는 계측 에이전트 또는 도구가있을 수 있습니다.
이러한 클래스는 새로운 구현에서 사용되지 않습니다.

참조