오류 조건을 어떻게 재현하고 응용 프로그램이 실행될 때 어떤 일이 발생하는지 확인합니까?
응용 프로그램의 서로 다른 동시 부분 간의 상호 작용을 어떻게 시각화합니까?
내 경험을 바탕으로이 두 가지 측면에 대한 답은 다음과 같습니다.
분산 추적
분산 추적은 시스템의 각 개별 동시 구성 요소에 대한 타이밍 데이터를 캡처하여 그래픽 형식으로 표시하는 기술입니다. 동시 실행의 표현은 항상 인터리브되어 실행중인 것과 그렇지 않은 것을 확인할 수 있습니다.
분산 추적은 원래 분산 시스템에서 발생하며, 정의상 비동기식이며 동시성이 높습니다. 분산 추적 기능이있는 분산 시스템을 통해 다음을 수행 할 수 있습니다.
a) 중요한 병목 현상을 식별합니다. b) 응용 프로그램의 이상적인 '실행'에 대한 시각적 표현을 얻습니다 .c) 동시 동작이 실행되고 있는지 파악하고 d) 시스템 (SLA가 강한 경우 매우 중요).
그러나 분산 추적의 결과는 다음과 같습니다.
네트워크를 통해 잠재적으로 실행 및 제출하기 위해 더 많은 코드로 변환되므로 모든 동시 프로세스에 오버 헤드가 추가됩니다. 경우에 따라 이러한 오버 헤드는 매우 중요합니다. Google조차도 사용자 경험을 망치지 않기 위해 모든 요청의 작은 하위 집합에서만 추적 시스템 인 Dapper 만 사용합니다.
서로 다른 많은 도구가 존재하지만 모두 서로 상호 운용 가능한 것은 아닙니다. 이것은 OpenTracing과 같은 표준으로 다소 개선되었지만 완전히 해결되지는 않았습니다.
공유 리소스 및 현재 상태에 대해서는 아무 것도 알려주지 않습니다 . 응용 프로그램 코드와 그래프에 표시되는 내용에 따라 추측 할 수 있지만이 점에서 유용한 도구는 아닙니다.
현재 도구는 여분의 메모리와 스토리지가 있다고 가정합니다. 제약 조건에 따라 시계열 서버 호스팅 비용이 저렴하지 않을 수 있습니다.
오류 추적 소프트웨어
Sentry는 주로 가장 널리 사용되는 도구이기 때문에 Sentry와 주로 연결되며 Sentry 하이 재크 런타임 실행과 같은 오류 추적 소프트웨어는 중앙 서버에 발생한 오류의 스택 추적을 동시에 전달합니다.
동시 코드에서 이러한 전용 소프트웨어의 장점 :
- 중복 오류는 중복되지 않습니다 . 즉, 하나 이상의 동시 시스템에 동일한 예외가 발생하면 Sentry는 사고 보고서 를 증가 시키지만 사고 사본 2 부를 제출하지는 않습니다.
이는 수많은 동시 오류 보고서를 거치지 않고 어떤 종류의 동시 시스템에서 어떤 종류의 오류가 발생하는지 파악할 수 있음을 의미합니다. 분산 시스템에서 전자 메일 스팸을 겪은 적이 있다면 기분이 어떻습니까?
동시 시스템의 여러 측면에 '태그'를 지정할 수도 있습니다 (이것은 정확히 하나의 스레드에 인터리브 된 작업이 없다고 가정하지만 스레드는 단순히 작업간에 효율적으로 점프하지만 여전히 이벤트 핸들러를 처리해야하기 때문에 기술적으로는 동시 적이 지 않습니다. 태그로 오류를 분석하십시오.
- 이 오류 처리 소프트웨어를 수정하여 런타임 예외에 대한 추가 세부 사항을 제공 할 수 있습니다. 프로세스에는 어떤 오픈 리소스가 있습니까? 이 프로세스가 보유한 공유 리소스가 있습니까? 어떤 사용자가이 문제를 경험 했습니까?
이를 통해 꼼꼼한 스택 추적 (및 파일의 축소 버전을 제공해야하는 경우 소스 맵)을 통해 많은 시간이 걸리는 문제를 쉽게 확인할 수 있습니다.
- (엔트리 특정) 시스템의 테스트 실행을 위해 별도의 Sentry보고 대시 보드를 가질 수있어 테스트시 오류를 포착 할 수 있습니다.
이러한 소프트웨어의 단점은 다음과 같습니다.
모든 것과 마찬가지로 대량으로 추가합니다. 예를 들어 그러한 시스템을 내장 하드웨어에 원하지 않을 수 있습니다. 유휴 시스템에서 수백 번의 실행을 샘플링하거나 수행하지 않고 간단한 실행을 비교하여 이러한 소프트웨어를 시범 적으로 실행하는 것이 좋습니다.
이러한 시스템 중 상당수가 암시 적으로 예외를 포착하는 데 의존하고 모든 언어에 강력한 예외가있는 것은 아니므로 모든 언어가 동일하게 지원되는 것은 아닙니다. 즉, 많은 시스템에 대한 클라이언트가 있습니다.
이러한 시스템 중 상당수가 본질적으로 비공개 소스이므로 보안 위험이 발생할 수 있습니다. 그러한 경우, 조사 할 때 실사를 수행하거나 원하는 경우 직접 조사하십시오.
필요한 정보를 항상 제공하지는 않습니다. 이것은 가시성을 추가하려는 모든 시도에서 위험합니다.
이러한 서비스는 대부분 동시 웹 응용 프로그램 용으로 설계되었으므로 모든 도구가 사용 사례에 완벽하지는 않습니다.
요약 : 가시성을 갖는 것은 동시 시스템에서 가장 중요한 부분입니다. 위에서 설명한 두 가지 방법은 하드웨어 및 데이터에 대한 전용 대시 보드와 함께 특정 시점에 시스템을 획기적인 그림으로 볼 수 있도록 업계 전반에 걸쳐 해당 측면을 해결하는 데 널리 사용됩니다.
몇 가지 추가 제안
끔찍한 방법으로 동시 문제를 해결하려고 시도한 사람들이 코드를 수정하는 것보다 더 많은 시간을 보냈습니다. 매번 다음 사항이 개발자 경험을 크게 향상시킬 수있는 사례를 발견했습니다 (사용자 경험만큼 중요합니다).
적절한 링크 테스트 는 한 구성 요소가 다른 구성 요소 와 격리 되어 통신 할 때 받은 메시지와 보낸 메시지가 예상 한 것과 같은지 확인합니다. 통신을 위해 공유 서비스에 의존하는 둘 이상의 구성 요소가있는 경우 구성 요소를 모두 스핀 업하고 중앙 서비스를 통해 메시지를 교환하게하고 결국에는 예상 한 결과를 얻는 지 확인하십시오.
많은 구성 요소를 포함하는 테스트를 구성 요소 자체 테스트와 각 구성 요소가 어떻게 통신하는지 테스트하면 코드의 유효성에 대한 신뢰도가 높아집니다. 이러한 엄격한 테스트를 통해 서비스 간 계약을 시행 할 수있을뿐만 아니라 서비스가 한 번에 실행될 때 발생하는 예기치 않은 오류를 포착 할 수 있습니다.
- 올바른 알고리즘을 사용하여 응용 프로그램 상태를 확인하십시오. 모든 작업자가 작업을 완료하기를 기다리는 마스터 프로세스가 있고 모든 작업자가 완전히 완료된 경우에만 다음 단계로 이동하려는 경우와 같은 간단한 것들에 대해 이야기하고 있습니다. Safra의 알고리즘과 같은 알려진 방법론이 존재하는 종료.
이러한 도구 중 일부는 언어와 함께 번들로 제공됩니다. 예를 들어 Rust는 코드가 컴파일 타임에 경쟁 조건을 갖지 않도록하고 Go는 컴파일 타임에도 실행되는 내장 교착 상태 탐지기를 갖추고 있습니다. 문제가 발생하기 전에 문제를 잡을 수 있다면 항상 승리입니다.
일반적인 경험 법칙 : 동시 시스템의 장애에 대한 설계 . 공통 서비스가 중단되거나 중단 될 것으로 예상하십시오. 단일 시스템의 동시 코드는 공유 로그 파일, Redis 서버, 망할 MySQL 서버와 같은 외부 종속성을 사용하여 언제라도 사라지거나 제거 할 수 있습니다. .
이 작업을 수행하는 가장 좋은 방법은 응용 프로그램 상태를 수시로 확인하는 것입니다. 각 서비스에 대한 상태를 확인하고 해당 서비스 소비자에게 상태가 잘못되었음을 알리는 것입니다. Docker와 같은 최신 컨테이너 도구는이 작업을 잘 수행하므로 사물을 샌드 박스로 사용해야합니다.
무엇을 동시에 만들 수 있고 무엇을 순차적으로 만들 수 있는지 어떻게 알 수 있습니까?
동시성이 높은 시스템에서 작업하면서 배운 가장 큰 교훈 중 하나는 다음과 같습니다 . 충분한 메트릭을 가질 수 없습니다 . 측정 항목은 애플리케이션에서 모든 것을 절대적으로 구동해야합니다. 모든 것을 측정하지 않으면 엔지니어가 아닙니다.
측정 항목이 없으면 몇 가지 매우 중요한 작업을 수행 할 수 없습니다.
시스템 변경으로 인한 차이점을 평가하십시오. 튜닝 노브 A로 인해 메트릭 B가 올라가고 메트릭 C가 내려 졌는지 알 수없는 경우, 사람들이 시스템에서 예기치 않게 악성 코드를 푸시 할 때 시스템을 수정하는 방법을 모릅니다 (및 시스템에 코드를 푸시 함) .
일을 개선하기 위해 다음에해야 할 일을 이해하십시오. 응용 프로그램의 메모리가 부족하다는 것을 알기 전까지는 더 많은 메모리를 확보해야하는지 또는 서버용으로 더 많은 디스크를 구입해야하는지 식별 할 수 없습니다.
측정 항목은 매우 중요하고 필수적이므로 시스템에 필요한 사항에 대해 생각하기 전에 측정 할 대상을 계획하기위한 의식적인 노력을 기울였습니다. 사실, 통계는 내가 믿는 너무 중요하다 그들은 이 질문에 대한 정답입니다 만 순차적 또는 당신이 때 동시에 할 수 무엇인지 측정 프로그램의 비트가 무엇을하고 있는지를. 적절한 디자인은 추측이 아닌 숫자를 사용합니다.
그러나 몇 가지 규칙이 있습니다.
순차적은 의존성을 의미합니다. 하나가 어떤 방식으로 다른 것에 의존하는 경우 두 프로세스는 순차적이어야합니다. 종속성이없는 프로세스는 동시에 진행되어야합니다. 그러나 다운 스트림 프로세스가 무기한 대기하지 못하게하는 장애 조치 스트림을 처리하는 방법을 계획하십시오.
I / O 바운드 작업을 동일한 코어의 CPU 바운드 작업과 혼합하지 마십시오. 예를 들어, 동일한 스레드에서 10 개의 동시 요청을 시작하고 들어오는 즉시 요청을 스크랩하며 500 개로 확장 할 것으로 예상하는 웹 크롤러를 작성하지 마십시오. I / O 요청은 대기열에 병렬로 이동하지만 CPU는 여전히 직렬로 통과합니다. (이 단일 스레드 이벤트 중심 모델은 널리 사용되는 모델이지만이 측면으로 인해 제한되어 있습니다.이를 이해하기보다는 사람들이 단순히 손을 잡고 노드가 확장되지 않는다고 설명합니다.)
단일 스레드는 많은 I / O 작업을 수행 할 수 있습니다. 그러나 하드웨어의 동시성을 완전히 사용하려면 모든 코어를 함께 차지하는 스레드 풀을 사용하십시오. 위의 예에서 CPU 작업을 위해 5 개의 Python 프로세스 (각각 6 코어 시스템에서 코어를 사용할 수 있음)를 시작하고 I / O 작업을위한 6 번째 Python 스레드는 생각보다 훨씬 빠르게 확장됩니다.
CPU 동시성을 활용하는 유일한 방법은 전용 스레드 풀을 사용하는 것입니다. 단일 스레드는 종종 많은 I / O 바운드 작업에 충분합니다. 이것이 Nginx와 같은 이벤트 중심 웹 서버가 Apache보다 I / O 바운드 작업을 강화하고 요청 당 프로세스를 시작하는 Apache보다 더 나은 (순수하게 I / O 바운드 작업을 수행하는) 이유이지만 Node를 사용하여 수행하는 이유 병렬로받은 수만 개의 GPU 계산은 끔찍한 아이디어입니다.