>Java >java지도 시간 >Java의 IO 재사용에 대한 자세한 그래픽 및 텍스트 설명

Java의 IO 재사용에 대한 자세한 그래픽 및 텍스트 설명

黄舟
黄舟원래의
2017-05-28 09:23:001491검색

이 기사는 주로 Java IO 재사용에 대한 관련 지식을 소개합니다. 매우 훌륭하고 참조할 가치가 있습니다. 필요한 친구가 참조할 수 있습니다.

서버의 동시 처리 기능을 위해 필요한 것은: 매 밀리초입니다. 이 밀리초 내에 수백 개의 서로 다른 TCP 연결을 통해 수신된 모든 메시지는 적시에 처리될 수 있습니다. 동시에 서버에는 메시지를 보내거나 받지 않은 상대적으로 비활성 연결이 수십만 개 있을 수 있습니다. 지난 몇 초 동안. 동시에 이벤트가 발생하는 여러 연결을 처리하는 것을 줄여서 수만 또는 수십만 개의 연결을 동시에 처리하는 것을 동시성이라고 합니다. 서버 동시성programming이 추구하는 것은 물리적 자원이 먼저 소진될 때까지 CPU 등의 자원을 효율적으로 사용하면서 무한한 동시 연결을 처리하는 것입니다.

동시 프로그래밍모델에는 많은 구현이 있으며, 가장 간단한 것은 "스레드"와 함께 번들로 제공되며, 1개의 스레드가 1개의 연결의 전체 수명주기를 처리합니다. 장점: 이 모델은 충분히 간단하고 복잡한 비즈니스 시나리오를 구현할 수 있으며 동시에 스레드 수가 CPU 수보다 훨씬 클 수 있습니다. 그런데 스레드 수를 무한정 늘릴 수는 없습니다. 이유는 무엇입니까? 스레드가 실행될 때 운영 체제 커널 스케줄링 알고리즘에 의해 결정되기 때문에 스케줄링 알고리즘은 특정 스레드가 하나의 연결만 제공할 수 있다는 것을 고려하지 않습니다. 즉, 타임 슬라이스가 종료되더라도 실행됩니다. 이 스레드가 실행되면 계속해서 잠을 자야 합니다. 이렇게 스레드를 깨우고 휴면 상태로 왔다갔다하는 것은 횟수가 적을 때 저렴하지만, 운영 체제의 전체 스레드 수가 많으면 비용이 많이 듭니다(증폭). 이러한 기술적인 스케줄링 손실이 스레드에 영향을 미치기 때문입니다. 비즈니스 코드가 실행되는 시간입니다. 예를 들어, 현재 연결이 활성화되지 않은 대부분의 스레드는 우리 국영 기업과 같으며 실행 효율성이 너무 낮습니다. 즉, 활성 연결을 처리하는 민간 기업 스레드 수가 줄어들고 CPU를 확보할 기회가 줄어듭니다. CPU는 핵심 경쟁력이며 CPU의 비효율성은 전체 GDP 처리량에 영향을 미칩니다. 우리가 추구하는 것은 수십만 개의 연결을 동시에 처리하는 것입니다. 수천 개의 스레드가 나타나면 시스템의 실행 효율성은 더 이상 높은 동시성을 충족할 수 없습니다.

고동시성 프로그래밍에는 현재 단 하나의 모델만 있으며 이는 본질적으로 유일한 효과적인 방법입니다. 연결에 대한 메시지 처리는 메시지가 준비될 때까지 기다리는 단계와 메시지 처리라는 두 단계로 나눌 수 있습니다. 기본 차단 소켓(예: 위에서 언급한 하나의 연결을 처리하기 위해 하나의 스레드가 번들로 제공됨)을 사용하는 경우 이러한 두 단계가 하나로 결합되는 경우가 많기 때문에 소켓 코드를 작동하는 스레드는 메시지가 준비될 때까지 대기해야 합니다. , 이로 인해 스레드가 높은 동시성에서 자주 절전 모드로 전환되어 CPU 사용 효율성에 영향을 미칩니다.

고동시성 프로그래밍 방식은 물론 두 단계를 분리하는 것입니다. 즉, 메시지가 준비될 때까지 기다리는 코드 섹션이 메시지를 처리하는 코드 섹션과 분리됩니다. 물론 이를 위해서는 소켓이 비차단 상태여야 합니다. 그렇지 않으면 메시지를 처리하는 코드 세그먼트로 인해 조건이 충족되지 않을 때 스레드가 절전 대기 단계에 쉽게 들어갈 수 있습니다. 그렇다면 문제는 메시지가 준비될 때까지 기다리는 이 단계를 어떻게 달성할 것인가 하는 것입니다. 결국, 여전히 대기 중입니다. 이는 스레드가 여전히 휴면 상태에 있어야 함을 의미합니다! 해결책은 적극적으로 query하거나 1개의 스레드가 모든 연결을 기다리도록 하는 것입니다! 이것이 IO 멀티플렉싱입니다. 멀티플렉싱은 메시지가 준비될 때까지 기다리는 것이지만 동시에 여러 연결을 처리할 수 있습니다! "대기"할 수도 있으므로 스레드를 절전 모드로 전환할 수도 있지만 일대다 방식이고 모든 연결을 모니터링할 수 있으므로 문제가 되지 않습니다. 이러한 방식으로 스레드가 실행을 위해 깨어날 때 코드에서 실행할 준비가 된 일부 연결이 있어야 하며 이는 효율적입니다! "메시지 준비 대기" 단계를 처리하기 위해 경쟁하는 스레드가 그리 많지 않으며 마침내 전 세계가 명확해졌습니다!
다중화 구현이 많이 있습니다. linux에서는 2.4 커널 이전에는 선택 및 폴링이 주요 구현이었습니다. 이제 사용 방법은 매우 다른 것처럼 보이지만 본질은 동일합니다.

효율성도 다르기 때문에 epoll이 select를 완전히 대체한 것입니다.

epoll이 select를 대체하는 이유에 대해 간단히 이야기해 보겠습니다.

앞서 언급했듯이 높은 동시성을 위한 핵심 솔루션은 모든 연결에 대해 "메시지가 준비되기를 기다리는 중"을 하나의 스레드로 처리하는 것입니다. 이 점에서 epoll과 select는 논란의 여지가 없습니다. 그러나 선택 추정에서 한 가지 잘못된 점은 첫 번째 장에서 말했듯이 수십만 개의 동시 연결이 존재하는 경우 밀리초마다 수백 개의 활성 연결만 있을 수 있고 나머지 수십만 개의 연결이 활성화될 수 있습니다. 밀리초는 비활성 상태입니다. select를 사용하는 방법은 다음과 같습니다. 반환된 활성 연결 ==select(모니터링할 모든 연결)

select 메소드는 언제 호출되나요? 패킷이 도착한 활성 연결을 찾아야 한다고 생각되면 이 호출을 호출해야 합니다. 따라서 동시성이 높을 때 select 호출이 자주 호출됩니다. 이처럼 자주 호출되는 방법이 "자주"라는 단어로 인해 약간의 효율성 손실이 증폭되기 때문에 효율적인지 여부를 확인할 필요가 있습니다. 효율성 손실이 있습니까? 분명히 모니터링할 연결은 수십만 개인데 활성 연결은 수백 개만 반환되므로 그 자체로는 비효율적입니다. 증폭된 후에는 select가 수만 개의 동시 연결을 완전히 처리할 수 없다는 것을 알게 될 것입니다.
사진 몇 장을 보세요. 동시 연결 수가 1,000개 미만인 경우 select 실행 횟수가 빈번하지 않으며 epoll과 큰 차이가 없는 것 같습니다.

그러나 동시 연결 수가 증가하면 select의 단점은 "잦은 실행"으로 인해 무한히 확대됩니다. 그리고 동시성이 많을수록 더 분명해집니다.

epoll이 이를 어떻게 해결하는지 이야기해 보겠습니다. 3가지 방법을 교묘하게 사용하여 select 방법이 수행하는 작업을 수행합니다.

New epoll descriptor==epoll_create()

epoll_ctrl(epoll 설명자, 모니터링할 모든 연결을 추가 또는 삭제)

반환된 활성 연결 == epoll_wait(epoll 설명자)

이 작업의 주요 이점은 자주 호출되는 작업과 자주 호출되지 않는 작업을 구별하는 것입니다. 예를 들어 epoll_ctrl은 덜 자주 호출되는 반면 epoll_wait는 매우 자주 호출됩니다. 이때 epoll_wait에는 매개변수가 거의 없어 select보다 훨씬 효율적입니다. 또한 동시 연결 수가 증가해도 매개변수 수를 늘리지 않아 커널 실행 효율성이 저하됩니다.

epoll은 어떻게 구현되나요? 실제로 epoll_wait가 "어떤 연결이 이미 메시지 준비에 있는지" 자주 호출할 때마다 모니터링할 모든 연결을 전달할 필요가 없다는 점에서 이 세 가지 방법에서 더 똑똑하다는 것을 알 수 있습니다. 무대". of. 이는 모니터링할 모든 연결을 저장하기 위해 커널 모드에서 데이터 구조를 유지한다는 의미입니다. 이 데이터 구조는 레드-블랙 트리이며, 해당 노드의 추가 및 감소는 epoll_ctrl을 통해 완료됩니다. 매우 간단합니다.

그림 왼쪽 하단의 빨간색-검은색 트리는 모니터링할 모든 연결로 구성됩니다. 왼쪽 상단의 연결 목록에는 현재 활성화된 모든 연결이 표시됩니다. 따라서 epoll_wait가 실행되면 왼쪽 상단 연결 리스트만 확인하고 왼쪽 상단 연결 리스트에 있는 연결을 사용자에게 반환합니다. 이런 식으로 epoll_wait의 실행 효율성이 낮아질 수 있나요?

마지막으로 epoll에서 제공하는 두 가지 게임 플레이 방법인 ET와 LT, 즉 번역된 Edge Trigger와 수평 Trigger를 살펴보겠습니다. 사실 이 두 중국어 이름은 어느 정도 적절하다. 이 두 가지 사용 방법은 여전히 ​​효율성 문제를 목표로 하고 있지만 단지 epoll_wait에서 반환된 연결을 더 정확하게 만드는 방법이 될 뿐입니다.

예를 들어, 연결의 쓰기 버퍼가 사용 가능한지 여부를 모니터링해야 합니다. "쓰기 가능"하면 사용자 모드에서 클라이언트에 응답 호출 쓰기를 보낼 수 있습니다. 그러나 연결이 쓰기 가능한 경우에도 "응답" 콘텐츠가 여전히 디스크에 남아 있을 수 있습니다. 이때 디스크 읽기가 완료되지 않은 경우 어떻게 될까요? 스레드가 차단되어서는 안 되므로 응답이 전송되지 않습니다. 다만, 다음 번 epoll_wait 시 연결이 반환될 수 있으므로 이를 처리할지 여부를 확인해야 합니다. 아마도 우리 프로그램에는 디스크 IO를 특별히 처리하는 또 다른 모듈이 있을 수 있으며, 디스크 IO가 완료되면 응답을 보낼 것입니다. 그렇다면 epoll_wait는 즉시 처리할 수 없는 "쓰기 가능한" 연결을 반환합니까? 사용자 기대에 부응합니까?

그래서 ET와 LT 모드가 탄생하게 되었습니다. LT는 예상되는 상태를 충족하는 모든 연결이 epoll_wait에서 반환되어야 하므로 모든 사람을 동등하게 취급하고 수평선에 있음을 의미합니다. 보다 정확한 리턴 연결을 선호하는 ET의 경우는 그렇지 않습니다. 위의 예에서 연결이 처음으로 쓰기 가능하게 된 후 프로그램이 연결에 데이터를 쓰지 않으면 epoll_wait는 다음 번에 연결을 반환하지 않습니다. ET를 에지 트리거라고 합니다. 이는 연결이 한 상태에서 다른 상태로 변경될 때만 epoll_wait가 트리거되어 이를 반환한다는 것을 의미합니다. ET의 프로그래밍은 훨씬 더 복잡하다는 것을 알 수 있습니다. 최소한 애플리케이션은 epoll_wait에 의해 반환된 연결이 나타나지 않도록 주의해야 합니다. 쓰기 가능한 경우 데이터는 기록되지 않지만 다음 "쓰기 가능"을 예상합니다. 읽을 수 있으면 데이터를 읽지 않고 다음 번에 "읽을 수 있게" 됩니다.

물론 일반 애플리케이션 시나리오에서는 성능에 큰 차이가 없습니다. ET의 가능한 장점은 epoll_wait에 대한 호출 수가 줄어들고 일부 시나리오에서는 연결이 해제되지 않는다는 것입니다. 필요하지 않습니다(이 Wake-up은 epoll_wait 반환을 참조함). 하지만 위에서 언급한 예와 같은 경우에는 단순한 네트워크 문제가 아니라 응용 시나리오와 관련된 경우도 있습니다. 물론 대부분의 오픈 소스 프레임워크 는 ET를 기반으로 작성되었으며 프레임워크는 순전히 기술적인 문제를 추구하며 완벽함을 위해 노력합니다

.

위 내용은 Java의 IO 재사용에 대한 자세한 그래픽 및 텍스트 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.