#concurrency
글
-
Gradle 소스 파헤치기 9: 태스크 그래프와 실행 계획 — 무엇을, 어떤 순서로, 몇 개씩
gradle pA 의 pA 가 packageApp 이 되고, dependsOn·mustRunAfter·shouldRunAfter·finalizedBy 가 실행 계획의 어떤 관계가 되어 순서를 정하는지, 실행 중에 다음 노드는 누가 어떻게 고르는지, --parallel 과 --max-workers 가 정확히 무엇을 제한하는지, Worker API 가 태스크 안의 일을 어떻게 나눠 돌리는지를 기준 커밋의 소스와 직접 돌린 예제(whenReady, --dry-run, 스레드 이름, jstack)로 확인합니다.
-
Kafka 소스 파헤치기 4: 브로커의 네트워크 계층 — Acceptor, Processor, 요청 처리 스레드
브로커의 jstack 에 보이는 socket-acceptor, network-thread, request-handler 세 종류의 스레드가 요청 하나를 어떻게 나눠 맡는지 SocketServer.scala 로 따라갑니다. 리스너마다 Acceptor 하나, Processor 가 바이트를 읽어 요청 큐에 넣고 그 연결을 mute 하면, 요청 처리 스레드가 KafkaApis.handle() 의 match 로 갈래를 나누고, 응답은 요청을 읽은 Processor 로 돌아갑니다. 한 연결의 요청을 하나씩만 처리해 순서를 지키는 mute/unmute, num.network.threads·num.io.threads·queued.max.requests 가 걸리는 자리를 jstack·맨 소켓·요청 로그 예제로 확인하고 Tomcat 의 NIO 커넥터와 비교합니다.
-
Kafka 소스 파헤치기 5: 프로듀서 내부 — send()에서 배치, 그리고 Sender 스레드까지
KafkaProducer.send() 는 네트워크로 아무것도 보내지 않습니다. 레코드를 직렬화하고 파티션을 고른 뒤 RecordAccumulator 의 파티션별 배치에 넣고 Future 를 돌려줄 뿐이고, 실제로 보내는 것은 kafka-producer-network-thread 하나입니다. linger.ms·batch.size 가 걸리는 ready() 의 if, 키 없는 레코드를 한 파티션에 붙여 두는 내장 파티셔너, 배치 메모리를 나눠 주는 BufferPool 과 max.block.ms, 재시도와 delivery.timeout.ms, max.in.flight.requests.per.connection 을 소스로 따라가고, kafka-dump-log.sh 로 배치 수를 세는 예제와 콜백 스레드를 찍는 예제로 확인합니다.
-
MariaDB 소스 파헤치기 4: 연결과 스레드 — 연결마다 스레드 하나, 그리고 스레드 풀
mariadbd 의 메인 스레드가 poll() 로 접속을 받아 CONNECT 를 만들고, thread_handling 이 고른 스케줄러(함수 포인터 묶음)에 넘기기까지를 소스로 따라갑니다. 연결마다 스레드 하나를 두는 기본 모드와 스레드 캐시, THD 를 만들고 인증하는 로그인 단계, 그리고 스레드 그룹·리스너·워커·timer_thread 로 이루어진 스레드 풀이 놀고 있는 연결을 스레드에서 떼어 내는 방법을 ps·information_schema·gdb 실행 결과로 확인하고, httpd 의 MPM·Tomcat 의 엔드포인트와 비교합니다.
-
nginx 소스 파헤치기 6: 마스터와 워커 — 프로세스, 시그널, 무중단 reload
nginx -s reload 한 줄이 어떻게 마스터의 sigsuspend() 루프를 깨우고, 새 설정으로 새 워커를 띄운 뒤 옛 워커를 하던 일을 마치게 하고 내보내는지 src/os/unix/ngx_process_cycle.c 를 따라 읽습니다. 시그널 핸들러가 플래그만 세우는 이유, 마스터와 워커를 잇는 socketpair 채널, ngx_processes[] 표의 respawn·just_spawn·exiting 비트, kill -9 당한 워커를 다시 띄우는 길, USR2 바이너리 교체까지 strace·디버그 로그·ps 로 확인합니다.
-
nginx 소스 파헤치기 7: 이벤트 루프 — epoll, 타이머, posted 이벤트, accept
워커 하나가 스레드 하나로 수천 연결을 돌리는 ngx_process_events_and_timers() 한 바퀴를 소스로 따라갑니다. epoll 을 함수 포인터 표(ngx_event_actions)로 끼우는 법, worker_connections 만큼 미리 만든 연결 배열과 빈 목록, EPOLLET 와 낡은 이벤트를 거르는 instance 비트, 레드블랙 트리 타이머가 epoll_wait 의 타임아웃이 되는 길, posted 큐, 그리고 accept_mutex(기본 off)·EPOLLEXCLUSIVE·reuseport 가운데 누가 새 연결을 받는지를 strace·gdb·디버그 로그로 확인합니다.
-
Apache httpd 소스 파헤치기 8: MPM — prefork, worker, event는 연결을 어떻게 나눠 맡나
httpd.conf 의 LoadModule mpm_event_module 한 줄이 무엇을 바꾸는지 소스로 따라갑니다. MPM 이 모듈 하나로 갈아 끼워지는 원리(mpm 훅과 ap_mpm_query), 프로세스만 쓰는 prefork, 리스너 스레드와 큐로 일을 나누는 worker, keep-alive 연결을 스레드에서 떼어 epoll 에 맡기는 event, 1초마다 자식을 늘리고 줄이는 부모의 루프와 공유 메모리 스코어보드, 그리고 StartServers·MaxRequestWorkers·ThreadsPerChild 가 코드의 어디서 쓰이는지를 ps·mod_status·gdb 실행 결과로 확인합니다.
-
Tomcat 소스 파헤치기 5: 커넥터와 NIO 엔드포인트 — 연결은 어떤 스레드가 받나
server.xml 의 <Connector port="8080"> 한 줄이 Connector, Http11NioProtocol, NioEndpoint 라는 세 객체가 되고, 그 안에서 Acceptor·Poller·작업 스레드가 연결 하나를 나눠 맡는 과정을 소스로 따라갑니다. keep-alive 연결이 요청 사이에 스레드를 놓아주는 길, JDK 와 다르게 동작하는 Tomcat 의 스레드 풀(TaskQueue), maxConnections·acceptCount·maxThreads 가 걸리는 자리를 jstack 과 소켓 예제로 확인하고, main 브랜치에서 NIO2 커넥터가 빠진 것도 11.0.x 와 비교합니다.
-
Tomcat 소스 파헤치기 12: 비동기 서블릿과 논블로킹 I/O — 스레드를 놓아주는 법
startAsync()를 부른 서블릿은 응답을 끝내지 않고 돌아가고, 요청 처리 스레드는 풀로 돌아갑니다. 그 사이 요청을 붙들고 있는 AsyncStateMachine의 상태 전이, complete()와 dispatch()가 컨테이너 스레드로 돌아오는 길, 1초마다 도는 타임아웃 검사, ReadListener가 Poller와 함께 동작하는 방식을 Tomcat 소스와 스레드 이름을 찍는 예제로 따라갑니다.