From 6960507c8bd883d2116147e81ba389075a9dc281 Mon Sep 17 00:00:00 2001 From: seunghye <93831492+seunghye218@users.noreply.github.com> Date: Thu, 19 Sep 2024 22:34:53 +0900 Subject: [PATCH] =?UTF-8?q?[4=EB=8B=A8=EA=B3=84=20-=20Tomcat=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=ED=95=98=EA=B8=B0]=20=EB=A7=88=ED=81=AC(=EA=B9=80?= =?UTF-8?q?=EC=8A=B9=ED=98=84)=20=EB=AF=B8=EC=85=98=20=EC=A0=9C=EC=B6=9C?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.=20(#751)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: remove implementation logback-classic on gradle (#501) * fix: add threads min-spare configuration on properties (#502) * test: FileTest 및 IOStreamTest 통과 * chore: lombok 의존성 추가 * feat: 1-1 'GET /index.html 응답하기' 기능 구현 * feat: 1-2 CSS 지원하기 기능 구현 - MimeType 열거형 추가 - 정적 URI를 못찾을 시 404 반환 * refactor: getResponse 로직 메서드 분리 * refactor: 간단한 예외처리 추가 * refactor(HttpRequest): path와 parameters 추가 및 파싱 * feat: 1-3 Query String 파싱 구현 * feat: 2-1 로그인 여부에 따라 페이지 이동 구현 * refactor: HttpResponse 객체 추가 * refactor: 메서드 인자 및 이름 변경 - getStaticResource(HttpRequest request) -> getStaticResourceResponse(String requestPath) * fix: 템플릿 엔진 미사용 * test: 휴리스틱 캐싱 제거 - CacheControlInterceptor 추가 * test: HTTP Compression 설정 * fix: 알맞은 resource 이름 반환 * test: ETag/If-None-Match 적용 - ShallowEtagHeaderFilter 사용 * test: 캐시 무효화 - 캐시 max-age 1년 설정 - ETag 적용 - url에 버전 적용 * feat: POST 방식으로 회원가입 - 요청 본문 파싱 로직 수정 * fix: 파라미터 없는 /login 접속 불가능 수정 * fix: 요청 본문 URLDecode * fix: Cookie에 JSESSIONID 값 저장 - HttpCookie 객체 구현 및 요청, 응답 객체에 추가 * feat: Session 구현 * refactor: 요청 URL 디코딩 * refactor: application/x-www-form-urlencoded MIME 타입 요청 본문 값을 파라미터로 처리 * refactor(HttpResponse): 생성자 추가 * refactor(HttpResponse): 상태 코드 및 메시지 열거형 분리 - enum HttpStatusCode * refactor: Http11Processor 에서 컨트롤러 로직 분리 - 컨트롤러 인터페이스 추가 - 정적 자원 불러오는 로직 객체 분리 - 요청 경로애 따른 컨트롤러 맵핑 * refactor: 정적 자원 캐시 정책 추가 * refactor: 요청, 응답 헤더 객체화 - 하드 코딩 값 상수화 - 세션 쿠키에 HttpOnly 설정 * refactor: 로그인 요청 메서드 POST로 변경 - DB 인스턴스 생성 및 User id 유효값 저장 - 루트 패스 인덱스 페이지로 변경 * test: 컨트롤러 및 도매인 테스트 추가 - User 검증 추가 * fix: 키밸류 값 문제 시 패스 * chore: 세션 패키지 변경 * refactor: svg 파일 처리 추가 * refactor: 변수명 변경 * chore: 패키지 수정 * feat: 스레드 학습 테스트 * feat: Executors로 Thread Pool 적용 * feat: 동시성 컬렉션 사용하기 * chore: 사용하지 않는 변수 및 import 제거 --------- Co-authored-by: Gyeongho Yang --- study/src/main/resources/application.yml | 4 ++-- .../thread/stage0/SynchronizationTest.java | 2 +- .../java/thread/stage0/ThreadPoolsTest.java | 6 +++--- .../src/test/java/thread/stage2/AppTest.java | 2 ++ .../apache/catalina/connector/Connector.java | 19 ++++++++++++------- .../catalina/session/SessionManager.java | 4 ++-- 6 files changed, 22 insertions(+), 15 deletions(-) diff --git a/study/src/main/resources/application.yml b/study/src/main/resources/application.yml index 8b74bdfd88..c566b7ab5c 100644 --- a/study/src/main/resources/application.yml +++ b/study/src/main/resources/application.yml @@ -3,8 +3,8 @@ handlebars: server: tomcat: - accept-count: 1 - max-connections: 1 + accept-count: 0 + max-connections: 2 threads: min-spare: 2 max: 2 diff --git a/study/src/test/java/thread/stage0/SynchronizationTest.java b/study/src/test/java/thread/stage0/SynchronizationTest.java index 0333c18e3b..b463c2b984 100644 --- a/study/src/test/java/thread/stage0/SynchronizationTest.java +++ b/study/src/test/java/thread/stage0/SynchronizationTest.java @@ -41,7 +41,7 @@ private static final class SynchronizedMethods { private int sum = 0; - public void calculate() { + public synchronized void calculate() { setSum(getSum() + 1); } diff --git a/study/src/test/java/thread/stage0/ThreadPoolsTest.java b/study/src/test/java/thread/stage0/ThreadPoolsTest.java index 238611ebfe..03efdabc8d 100644 --- a/study/src/test/java/thread/stage0/ThreadPoolsTest.java +++ b/study/src/test/java/thread/stage0/ThreadPoolsTest.java @@ -31,8 +31,8 @@ void testNewFixedThreadPool() { executor.submit(logWithSleep("hello fixed thread pools")); // 올바른 값으로 바꿔서 테스트를 통과시키자. - final int expectedPoolSize = 0; - final int expectedQueueSize = 0; + final int expectedPoolSize = 2; + final int expectedQueueSize = 1; assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size()); @@ -46,7 +46,7 @@ void testNewCachedThreadPool() { executor.submit(logWithSleep("hello cached thread pools")); // 올바른 값으로 바꿔서 테스트를 통과시키자. - final int expectedPoolSize = 0; + final int expectedPoolSize = 3; final int expectedQueueSize = 0; assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); diff --git a/study/src/test/java/thread/stage2/AppTest.java b/study/src/test/java/thread/stage2/AppTest.java index e253c4a249..cfc8e27a3b 100644 --- a/study/src/test/java/thread/stage2/AppTest.java +++ b/study/src/test/java/thread/stage2/AppTest.java @@ -1,5 +1,6 @@ package thread.stage2; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import java.net.http.HttpResponse; @@ -21,6 +22,7 @@ class AppTest { * - http call count * - 테스트 결과값 */ + @Disabled // 테스트를 실행시키기 위해서 thred.stage2.App을 실행하고 @Disabled를 제거하세요. @Test void test() throws Exception { final var NUMBER_OF_THREAD = 10; diff --git a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java index 3b2c4dda7c..a1b6a277fc 100644 --- a/tomcat/src/main/java/org/apache/catalina/connector/Connector.java +++ b/tomcat/src/main/java/org/apache/catalina/connector/Connector.java @@ -1,13 +1,14 @@ package org.apache.catalina.connector; -import org.apache.coyote.http11.Http11Processor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.UncheckedIOException; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.apache.coyote.http11.Http11Processor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Connector implements Runnable { @@ -15,17 +16,20 @@ public class Connector implements Runnable { private static final int DEFAULT_PORT = 8080; private static final int DEFAULT_ACCEPT_COUNT = 100; + private static final int DEFAULT_MAX_THREADS = 250; private final ServerSocket serverSocket; + private final ExecutorService executorService; private boolean stopped; public Connector() { - this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT); + this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, DEFAULT_MAX_THREADS); } - public Connector(final int port, final int acceptCount) { + public Connector(final int port, final int acceptCount, final int maxThreads) { this.serverSocket = createServerSocket(port, acceptCount); this.stopped = false; + this.executorService = Executors.newFixedThreadPool(maxThreads); } private ServerSocket createServerSocket(final int port, final int acceptCount) { @@ -67,13 +71,14 @@ private void process(final Socket connection) { return; } var processor = new Http11Processor(connection); - new Thread(processor).start(); + executorService.submit(processor); } public void stop() { stopped = true; try { serverSocket.close(); + executorService.shutdown(); } catch (IOException e) { log.error(e.getMessage(), e); } diff --git a/tomcat/src/main/java/org/apache/catalina/session/SessionManager.java b/tomcat/src/main/java/org/apache/catalina/session/SessionManager.java index 4c5a27a0d7..0c2eb4706e 100644 --- a/tomcat/src/main/java/org/apache/catalina/session/SessionManager.java +++ b/tomcat/src/main/java/org/apache/catalina/session/SessionManager.java @@ -1,7 +1,7 @@ package org.apache.catalina.session; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -9,7 +9,7 @@ public class SessionManager { private static final SessionManager INSTANCE = new SessionManager(); - private static final Map SESSIONS = new HashMap<>(); + private static final Map SESSIONS = new ConcurrentHashMap<>(); public static SessionManager getInstance() { return INSTANCE;