backend-tech-forge/benchmark

Agent 에서 vuser 만큼 동시 HTTP 로드 전송 방법

Closed this issue · 1 comments

저희가 구상한 HTTP 로드 전송 방법은 vuser 수 만큼 Thread 를 생성하고 각각 Reactive Stream 을 지원하는 WebClient 를 통해 HTTP 를 타겟 서버에 전송하는 것이었습니다. 여기서 발생한 문제점이나 제안을 정리해볼까 합니다. 아래는 WebClient 내부 동작 과정입니다.

  • 참고자료 : https://medium.com/geekculture/a-tour-of-netty-5020ecee5494

  • WebClient 동작 youtube : https://www.youtube.com/watch?v=okX6uLD8lb8
    image

  • 제안 1 : 퍼포먼스 테스트 요청 시 단일 WebClient 생성

    저는 "VUSER Thread 내부에서 WebClient 를 따로 생성해서 전송해야하나?" 에 대해 고민이 있었습니다. 그리고 저는 굳이 따로 생성할 필요 없이 Agent 가 bm-controller 로부터 퍼포먼스 테스트 요청 받을 때 단일 WebClient 를 생성하는게 리소스 소모량이 더 적다고 생각합니다.

  • 제안 2 : WebClient Netty 구성 시 workerGroup 개수를 4개로 설정? 아니면 vuser 수 만큼 설정?
    WebClient 는 default 로 Netty 아키텍처를 사용합니다. 전체 과정을 볼까요? 타겟 서버로 HTTP 요청을 전송할 때 먼저 Channel 을 생성합니다. 그리고 Channel 은 NioEventLoop 에 바인딩 됩니다. 이제 HTTP 요청은 WorkerGroup 을 거치면서 NioEventLoop 의 Task Queue 로 들어가게 되는데요. 이 큐에서 자기 차례가 되면 채널로 빠르게 전송되고 Http 요청을 패키징해서 전송하는 ChannelHandler 을 거쳐서 결국 서버에 전달됩니다. 이후 응답또한 ChannelHandler 거쳐서 진행됩니다.

    정확히는 ChannelInboundHandlerAdapter, ChannelOutboundHandlerAdapter.

    과정을 요약하면 아래와 같습니다.

    webClient.post(url) 전송 -> url 서버와 통신 channel 생성 -> eventLoop 바인드 -> workerGroup 이 post() 요청을 eventLoop 큐에 전달 -> eventLoop 는 계속 바인딩된 채널에 전달 -> 채널과 연결된 요청/응답 처리 핸들러 거침 -> 서버에 전달/응답반환

    여기서 NioEventLoopGroup (WorkerGroup) 개수 : NioEventLoop 개수 비율을 생각해야 합니다.

    1. 1:N
    2. N:N

    일반적으로 1:N 의 매칭은 권장되지 않는다고 합니다. Netty 내부 기본 동작방식과는 조금 다르기 때문이죠. 따라서 N;N 으로 설정하는게 좋아보입니다. 즉, 하나의 workerGroup 이 하나의 NioEventLoop 와 매칭되는거죠. 그리고 이 workerGroup 의 개수는 일반적으로 코어 개수로 설정하는것이 좋다고 합니다. async/non-blocking 이기 때문에 컨텍스트 스위칭 비용을 최대한 낮출 수 있기 때문입니다.

    본 프로젝트는 gcp e2-standard-4 노드 3개를 사용합니다. cpu 코어가 4개가 존재하죠. 따라서 저는 webClient 의 workerGroup 개수를 4개로 설정하는 것이 성능상으론 좋아보입니다. 하지만 여기엔 vuser 만큼 동시성을 적용해야하는 부분이 빠져있죠... 즉, 동시접속 유저를 제어할 수 없습니다.

결론

  1. 여러 스레드를 생성해서 WebClient 를 통해 요청을 전달하게 되면 nGrinder 과도한 컨텍스트 스위칭 이슈 가 본 프로젝트에도 발생할 것입니다.

  2. 반면에 WebClient 를 내부적으로 변경해도 동시성을 제어할 수 없습니다. Reactor 프로젝트는 자체적으로 최적화 되어있기때에 무조건 풀로 동시성을 가져가기 때문입니다ㅜㅜ.

일단 먼저 네이티브 스레드로 1번을 진행한 뒤, 이후 Java 21 virtual thread 를 사용하여 진행할 것입니다 :)

말씀주신 내용으로는 agent 에서 단일 WebClient 를 생성 한다는 말로 이해해도 될까요?

현재 WebClient 는 bm-controller 에 bean 으로 등록해서 개발 진행하고 있었습니다!