해당 강의를 보고 정리한 내용입니다.
여러 작업들을 순차적으로 실행하도록 개발
여러 작업들을 독립적으로 실행하도록 개발
여러 작업을 동시에 실행하는 프로그래밍 방법론
asynchronous programming의 한 종류
멀티스레딩은 하나의 프로세스에서 여러 개의 스레드를 생성하여 각 스레드가 병렬적으로 동작하도록 하는 기술입니다. 각 스레드는 독립적으로 실행되며, 멀티코어 CPU에서 병렬 처리를 활용할 수 있어 성능을 향상시킬 수 있습니다.
그러나 쓰레드를 너무 많이 만들게 될 경우, context-switching으로 인한 비용이 발생하고
스레드 간의 동기화, 공유 자원에 대한 안전한 접근을 보장하기 위한 처리가 필요합니다.
논블록 I/O는 I/O 작업이 진행되는 동안 다른 작업을 중단하지 않고 다른 작업을 처리할 수 있는 방식을 의미합니다. 이는 I/O 작업을 기다리는 동안 CPU가 유휴 상태가 되지 않도록 해주어 전체적인 처리량을 향상시킵니다. 주로 비동기적인 이벤트 기반 아키텍처에서 활용되며, 콜백 함수나 Promise와 같은 패턴을 사용하여 비동기 작업을 처리합니다.
non-block I/O, multithreading을 잘 조합한다면, 적은 쓰레드로도 좋은 성능을 낼 수 있습니다.
그렇기 때문에 요즘 백엔드 프로그래밍에 추세는 쓰레드는 적게 쓰면서도 non-block I/O를 통해 전체 처리량을 늘리는 방향으로 발전 중입니다.
I/O 관점에서 비동기는 작업 완료에 대해 작업 요청자가 직접 대기하거나 확인하는 대신, 운영체제(OS)로부터 알림을 받거나 작업을 시작할 때 콜백(callback)을 전달하여 작업이 완료되면 해당 콜백이 자동으로 실행되는 방식을 의미합니다.
block I/O를 다른 쓰레드에서 실행할 때 I/O 관점에서 비동기라고 할 수 있습니다.
예를 들어, 쓰레드 A가 코드를 실행하다가 블로킹 I/O 작업을 실행해야 한다면, 이 작업을 수행하기 위해 별도의 쓰레드 B를 생성하고 그곳에서 블로킹 I/O 작업을 처리합니다. 이렇게 하면 쓰레드 A는 블로킹되지 않고 계속해서 다른 작업을 실행할 수 있게 됩니다. 그리고 쓰레드 B에서 블로킹 I/O 작업이 완료되면 그 결과를 취합하여 처리할 수 있습니다.
하나의 서비스는 기능과 역할에 따라 여러 개의 마이크로 서비스로 구성되고 이들 사이에는 빈번하게 커뮤니케이션이 발생합니다.
Synchronous communication에서는 A가 작업을 수행하다가 B가 관심 있는 이벤트가 발생하면, A는 B에게 직접 요청을 보내고 해당 작업이 완료될 때까지 기다립니다. 이 때 A는 B의 작업 완료를 기다리는 동안 다른 작업을 수행할 수 없습니다. 마찬가지로 B가 다시 C에게 작업을 요청하면 B 역시 C의 작업 완료를 기다려야 하며, 이러한 방식으로 직렬적으로 작업이 수행됩니다.
Synchronous communication의 단점 중 하나는 장애 전파에 대한 취약성입니다. 여러 개의 서비스가 동기적으로 의존하고 있는 상황에서 하나의 서비스가 장애를 겪으면 이로 인해 전체 시스템에 장애가 전파될 수 있습니다.
예를 들어, A와 B라는 두 개의 서비스가 있고 A는 B의 데이터를 필요로 할 때, A는 B에게 요청을 보내고 B는 해당 요청에 응답하여 데이터를 제공합니다. 이 때 B가 장애를 겪게 되면 A는 데이터를 받을 수 없게 되며, A가 B의 데이터에 의존하는 다른 작업 역시 중단될 수 있습니다. 이로 인해 A의 작업 또한 중단되며, 이 장애가 A를 호출하는 다른 서비스에도 영향을 미칠 수 있습니다. 결과적으로 전체 시스템의 가용성과 안정성이 저하될 가능성이 있습니다.
Asynchronous communication에서는 A가 작업을 수행하다가 B가 관심 있는 이벤트가 발생하면, 해당 이벤트를 Message Q에 넣어둡니다. A는 이후 자신의 작업을 계속 수행하며 B는 Message Q를 주기적으로 확인하여 새로운 메시지가 있을 경우 작업을 수행합니다. 이와 같은 방식으로 각 서비스는 독립적으로 작업을 수행하면서 중간에 Message Q를 통해 정보를 교환합니다.
이런식으로 Asynchronous communication하게 됬을 경우 장점은 장애가 발생해도 다른 서비스에 영향을 미치지 않을 가능성이 높아집니다.
예를 들어 A가 작업을 수행하는 도중 B에서 문제가 발생해도, A는 그 영향을 받지 않고 자신의 작업을 계속할 수 있습니다. 메시지 큐를 통해 각 서비스는 느슨하게 결합되며, 장애가 발생해도 다른 서비스에 영향을 미치지 않을 가능성이 높아집니다.
하지만 모든 상황에서 비동기 통신만을 사용하는 것은 항상 옳은 선택은 아닙니다. 동기적인 작업이 필요한 경우나 즉각적인 응답이 필요한 경우에는 API 호출과 같은 동기적인 방식을 사용하는 것이 더 적절할 수 있습니다.
이벤트를 한쪽으로 전달만 하면 되는 경우에는 Message Q를 두는 것이 더 안정적인 서비스를 운영을 위한 서버 아키텍쳐가 될 수 있습니다.
결국 상황에 따라 비동기 통신과 동기 통신을 조합하여 최적의 아키텍처를 구성하는 것이 중요합니다.