Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions study/src/main/java/thread/stage2/SampleController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.concurrent.atomic.AtomicInteger;
Expand All @@ -23,11 +24,11 @@ public SampleController(final HelloWorldService helloWorldService) {
this.helloWorldService = helloWorldService;
}

@GetMapping("/test")
@GetMapping("/test/{threadNumber}")
@ResponseBody
public String helloWorld() throws InterruptedException {
public String helloWorld(@PathVariable int threadNumber) throws InterruptedException {
Thread.sleep(500);
log.info("http call count : {}", count.incrementAndGet());
log.info("Thread-[{}] : http call count : {}", threadNumber, count.incrementAndGet());
return helloWorldService.helloWorld();
}
}
2 changes: 1 addition & 1 deletion study/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ handlebars:
server:
tomcat:
accept-count: 1
max-connections: 1
max-connections: 2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각 accept-count, max-connections, min-spare, max는 무엇을 의미할까요!?
기존에 max-connections: 1 일때는 왜 테스트가 성공하지 않았을까요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • max-connections : 톰캣이 처리할 수 있는 연결의 최대 개수
  • accept-count : max-connections 만큼의 연결을 처리하고 있을 때 대기할 수 있는 최대 요청의 개수
  • thread.max : 스레드풀에서 사용할 최대 스레드 개수
  • thread.min-spare : 스레드에서 유지할 활성 스레드의 최소 개수
    • 라고 하는데 이 친구는 조금 감이 안오네요. 어짜피 FixedThreadPool 을 쓸 건데 의미가 있는 값인가.. 하는 생각이 듭니다.

+) 최종적으로 accept-count + max-connections 만큼의 TCP 연결이 가능하고, 그 이상의 TCP 연결 요청에 대해선 time out 이 발생하겠네용

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스레드풀이라도 스레드는 생성과 소멸이 존재합니다. 그렇기에 min-spare가 존재하고 중요한 역할을 한다고 생각합니다. 적어도 소멸하지 않고 계속 재사용되는 스레드의 개수니까요!

threads:
min-spare: 2
max: 2
Expand Down
11 changes: 8 additions & 3 deletions study/src/test/java/thread/stage2/AppTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package thread.stage2;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.http.HttpResponse;
import java.util.concurrent.atomic.AtomicInteger;
Expand All @@ -10,6 +12,7 @@
class AppTest {

private static final AtomicInteger count = new AtomicInteger(0);
private static final Logger log = LoggerFactory.getLogger(AppTest.class);

/**
* 1. App 클래스의 애플리케이션을 실행시켜 서버를 띄운다.
Expand All @@ -27,7 +30,8 @@ void test() throws Exception {
var threads = new Thread[NUMBER_OF_THREAD];

for (int i = 0; i < NUMBER_OF_THREAD; i++) {
threads[i] = new Thread(() -> incrementIfOk(TestHttpUtils.send("/test")));
final int threadNumber = i;
threads[i] = new Thread(() -> incrementIfOk(TestHttpUtils.send("/test/" + threadNumber)));
}

for (final var thread : threads) {
Expand All @@ -36,13 +40,14 @@ void test() throws Exception {
}

for (final var thread : threads) {
thread.join();
thread.join(0);
}

System.out.println("end");
assertThat(count.intValue()).isEqualTo(2);
}

private static void incrementIfOk(final HttpResponse<String> response) {
private synchronized static void incrementIfOk(final HttpResponse<String> response) {
if (response.statusCode() == 200) {
count.incrementAndGet();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.apache.coyote.http11.httpmessage.HttpHeaders.JSESSIONID;

import java.util.Objects;
import java.util.Optional;

import org.apache.catalina.servlet.AbstractController;
import org.apache.coyote.http11.httpmessage.request.HttpRequest;
Expand All @@ -21,7 +22,14 @@ protected void doPost(HttpRequest request, HttpResponse response) throws Excepti
HttpRequestParameters requestParams = HttpRequestParameters.parseFrom(request.getBody());
String account = requestParams.getParam("account");
String password = requestParams.getParam("password");
User user = InMemoryUserRepository.fetchByAccount(account);

Optional<User> optionalUser = InMemoryUserRepository.findByAccount(account);
if(optionalUser.isEmpty()) {
response.setStatusFound("/401.html");
return;
}

User user = optionalUser.get();
if (user.checkPassword(password)) {
Session session = request.getSession(true);
session.setAttribute("user", user);
Expand All @@ -35,6 +43,7 @@ protected void doPost(HttpRequest request, HttpResponse response) throws Excepti
response.setStatusFound("/401.html");
}


@Override
protected void doGet(HttpRequest request, HttpResponse response) throws Exception {
if (isLoggedIn(request)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
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.ServletContainer;
import org.apache.coyote.http11.Http11Processor;
Expand All @@ -16,20 +18,23 @@ 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_THREAD_COUNT = 5;

private final ServerSocket serverSocket;
private final ServletContainer servletContainer;
private final ExecutorService executorService;

private boolean stopped;

public Connector(ServletContainer servletContainer) {
this(DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, servletContainer);
this(servletContainer, DEFAULT_PORT, DEFAULT_ACCEPT_COUNT, DEFAULT_MAX_THREAD_COUNT);
}

public Connector(int port, int acceptCount, ServletContainer servletContainer) {
public Connector(ServletContainer servletContainer, int port, int acceptCount, int maxThreads) {
this.servletContainer = servletContainer;
this.serverSocket = createServerSocket(port, acceptCount);
this.stopped = false;
this.executorService = Executors.newFixedThreadPool(maxThreads);
}

private ServerSocket createServerSocket(int port, int acceptCount) {
Expand Down Expand Up @@ -71,7 +76,7 @@ private void process(Socket connection) {
return;
}
var processor = new Http11Processor(servletContainer, connection);
new Thread(processor).start();
executorService.submit(processor);
}

public void stop() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

public class Http11Processor implements Runnable, Processor {

private static final Logger log = LoggerFactory.getLogger(Http11Processor.class);
private static final Logger log = LoggerFactory.getLogger("ConsoleTraceLogger");
private final ServletContainer servletContainer;
private final Socket connection;

Expand All @@ -39,7 +39,7 @@ public void process(Socket connection) {
var outputStream = connection.getOutputStream()) {

HttpRequest httpRequest = HttpRequest.readFrom(requestBufferedReader);
log.info("request : {}", httpRequest);
log.trace("request : {}", httpRequest);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 trace로 변경했네요! 제가 궁금한건데, 그러면 혹시 이제 request는 어떻게 볼 수 있나요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trace 레벨의 request 를 보기 위해선 logback 설정을 변경해 주어야 해요.
새로 올라간 커밋의 logback.xml 을 참고해 주세요!

HttpResponse httpResponse = new HttpResponse(httpRequest);

service(httpRequest, httpResponse);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 코드에 리뷰하는 것은 아니고 Http11Processor의 58번째줄 리뷰입니다!

Cache-Control 붙인 이유가 궁금합니다! 각 no-cahe와 private은 무슨 역할을 하고 있나요? 짱수 덕분에 저도 공부하고 갑니다!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이게 로그인이 된 경우에 자꾸 /index.html 로 리다이렉트를 시켜서 그런지 로그아웃을 해 쿠키를 지워도 휴리스틱 캐시가 적용되는 것 같더라고요.
요청이 어플리케이션까지 도착하지 않고 disk cache 를 사용했다고(?) 떠서 휴리스틱 캐시를 적용시키지 않으려고 해당 코드를 작성해 주었습니다!

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.catalina.Manager;

public class SessionManager implements Manager {
// static!
private static final Map<String, Session> SESSIONS = new HashMap<>();
private static final Map<String, Session> SESSIONS = new ConcurrentHashMap<>();
private static SessionManager instance;

private SessionManager() {}
Expand Down
12 changes: 12 additions & 0 deletions tomcat/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="30 seconds">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
</encoder>
</appender>

<root level="trace">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>