반응형

 이번 시간에는 RestController에서 Request를 받을 때 마다 그것을 처리하는 쓰레드가 같은지, 다른지. 다르다면 어떤 식으로 쓰레드 들이 request를 처리하는지 실험해 보도록 하겠습니다. 이 글에서는 내장 톰캣을 이용하였습니다.

 


 먼저, 프로젝트 구조는 위와 같습니다. 컨트롤러 역할을 하는 testController가 있습니다. 그리고 설정 값을 저장해 놓은 properties 파일이 하나 있습니다.

 

 포트는 7780으로 설정해 놓았습니다.

 

 

 testController에는, /test로 get 요청을 받으면, 처리할 메소드인 test가 있습니다. 현재 쓰레드의 이름만 리턴하게 됩니다. 예를 들자면, Main과 같은 이름을 말합니다. Thread.currentThread()는 현재 실행중인 쓰레드 객체를 리턴한다고 되어 있어요.

 

 이 논리대로 라면, test() 메소드에서 Thread.currentThread()를 호출한 경우는 어떨까요? test() request를 처리하는 쓰레드 객체를 돌려준다는 의미와 같지 않을까요? 많이 쓰일지는 모르겠지만, 알아두면 좋을법한 메소드가 아닐까 싶습니다.

 


 이제, 7780 포트를 172.30.1.0/24 대역에서 접근 가능하게끔 열어줘야 합니다.

 

 

 ufw numbered를 보면, 7780이 없네요. 이는, 7780은 자기 자신에서 접근하는 거 빼고는 ALLOW 하지 않는다는 것을 의미해요.

 

 

 ufw allow from 172.30.1.0/24 to any port 7780을 입력하면, 7780 포트에 대해서, 172.30.1.0/24로부터 들어오는 것에 대해서 허용한다는 것을 의미해요. 그 다음에 ufw reload를 입력해서 방화벽 세팅 값을 다시 읽어들이고, ufw numbered로 열린 포트 목록들을 확인하면 7780이 들어왔음을 알 수 있어요.

 

 이제 postman으로 찔러 보면서 어떤 식으로 response가 오는지 보도록 하겠습니다.

 


 /test로 GET 메서드로 요청하면 되니까, GET http://address:port/test로 호출하겠습니다.

 

 

 http-nio-7780-exec-10이 나오네요. 현재 request를 받아서 처리한 쓰레드의 이름이 http-nio-7780-exec-10이였다는 것을 의미합니다. 또 호출해 보겠습니다.

 

 

 그러면 이번에는 http-nio-7780-exec-1이 나옵니다. request를 처리한 쓰레드 이름이 http-nio-7780-exec-1이였음을 의미합니다. 매 요청마다 처리하는 쓰레드 객체가 다르다는 것을 의미하는 거 같긴 합니다만, 찝찝한 것은 Thread는 setName 메서드가 있습니다. 실행 중인 쓰레드 이름을 Thread.currentThread().setName("123"); 이런 식으로 바꿔버릴 수 있습니다.

 

 조금 더 명확한 방법이 없을까요?

 


 hashCode면 조금 더 명확할까요? 

 

 Thread 클래스는 내부에 hashCode를 재정의 하지는 않았으므로, hashCode를 호출하면 naive hashCode를 호출하게 됩니다. 이것은 hashCode가 같다고 100% 같은 객체는 아니지만, 그 값이 다르다면 다른 객체입니다.

 

 

 test를 호출하면, 1024275738이 나옵니다. 또 호출해 봅시다.

 

 

 이번에는 311867115가 나오네요.

 

 

 몇 번 더 호출해 보니, 1024275738이 나왔습니다. 일정 갯수의 Thread 객체를 미리 생성해 놓고, 요청이 들어오면 이들을 이용해서 처리하는 구조인 듯 합니다. 그런데, 다른 객체라도 hashCode 값은 같을 수 있다는 것이 걸리네요.

 

 


 == 연산자는 다른 객체라면 false를 리턴합니다.

 

 프로젝트에 testInit 클래스를 추가해 보았습니다. 이것은 하는 것이 별로 없습니다.

 

 

 단지, 처음에 springBoot 어플리케이션이 올라갔을 때 초기화가 되고, ConcurrentHashMap hm은 static으로 선언이 되어 있습니다. testInit 객체가 여러 개 선언 되어도 클래스 변수인 hm은 같은 속성 값을 공유하게 됩니다.

 

 

 /test get API를 호출할 때 이 testInit의 hm 필드를 써먹게 됩니다. 여기서 t == Thread.currentThread()를 볼 필요가 있는데요. t는 이전에 testInit.hm에 저장 되어 있었던 정보입니다. 다음에, Thread.currentThread()는 현재 request를 처리하는 쓰레드를 의미해요. 이 둘이 같다는 의미는 같은 객체라는 것을 의미해요.

 

 

 처음에 호출했을 때에는 no가 찍혔습니다.

 

 

 그런데 몇 번 더 호출했을 때에는 yes가 찍힙니다. 이는 풀장 처럼 요청을 처리할 worker를 몇 개 만들어 놓고 처리했다는 것을 의미합니다.

 

 예를 들어서 1번째 request를 날렸을 때에는 1번이 처리합니다.

 

 

 다음 요청을 날렸을 때에는 1번이 아닌 3번이 처리하는 식으로요. 정리해 보면 http 요청을 보냈을 때, (무언가에 의해서) 1번 요청과 2번 요청을 처리하는 쓰레드는 다를 수도 있다. 그리고 일정 량의 쓰레드를 만들어서 관리된다 정도만 짚고 넘어갈 수 있겠네요. 사실 저는 Thread의 currentThread를 이용해서 결론을 얻었고요. 쓰레드 풀이라고 생각하면 조금 더 쉬울지는 모르겠네요.

 

 이상한 실험을 했지만, 나름 의미 있는 1줄짜리 결론을 얻긴 했네요.

반응형

댓글을 달아 주세요

  1. 안녕맨

    이 글은 스프링과는 연관짓지 않아야 하지 않나요? 단순히 톰캣의 스레드 풀을 재사용한다는 부분에 대한 내용인 것 같네요. 굳이 스프링 사용하지 않아도 톰캣을 사용한 서블릿이라면 똑같은 내용인거죠. 조금 더 덧붙이자면 초보자가 보면 스프링이 스레드 풀을 재사용하는 것으로 오해할 수 있겠네요.

    • 코딩강아지
      2021.07.02 21:03 신고

      스프링 보다는 Thread.currentThread 메서드 + (boot에 내장된) tomcat에
      조금 더 가까운 내용일 듯 싶네요. ㅠㅠ;;

      사실 저걸 굳이 저리 분류했던 이유 중 하나는.
      정말 바보같이 request는 같은 쓰레드가 받는 거 아니야? 라고 생각했던 적이 있었기 때문입니다.

      사실 이런 log를 보면 대략적으로 유추할 수 있는 내용이였을 텐데요. ㅠㅠ
      o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService ...

  2. 신선의 즐거운세상

    안녕하세요 잘 보고 갑니다. ^^*

  3. 승민79126

    안녕하세요 ! 스토리에서 보고 왔어요ㅋㅋ} 온김에 구독꾹 하고가요 ;) 맞구독 부탁드려요 ㅋㅋ놀러오세요!^^ 그럼 행복가득한 오후 되십숑^^