-
Notifications
You must be signed in to change notification settings - Fork 309
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[톰캣 구현하기 - 3, 4단계] 우르(김현우) 미션 제출합니다. #481
Changes from 27 commits
3af2ce9
1b3c576
f15ec86
865345e
c3d4faa
67473e0
a04d297
1aa6444
066f291
da41b0d
d1d8c11
91dceb5
728620e
d096920
ecd6ddd
f24fd29
e7b66dd
8e403ca
c9ebbb1
2184f8d
fb55927
d792f64
fa4598e
656e694
843184c
22ce601
84c9d52
d60c205
e8ec3a5
c8f3e61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,18 @@ | ||
package cache.com.example.cachecontrol; | ||
|
||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.http.CacheControl; | ||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
import org.springframework.web.servlet.mvc.WebContentInterceptor; | ||
|
||
@Configuration | ||
public class CacheWebConfig implements WebMvcConfigurer { | ||
|
||
@Override | ||
public void addInterceptors(final InterceptorRegistry registry) { | ||
final WebContentInterceptor interceptor = new WebContentInterceptor(); | ||
interceptor.addCacheMapping(CacheControl.noCache().cachePrivate(), "/*"); | ||
registry.addInterceptor(interceptor); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,29 @@ | ||
package cache.com.example.etag; | ||
|
||
import static cache.com.example.version.CacheBustingWebConfig.PREFIX_STATIC_RESOURCES; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
import javax.servlet.Filter; | ||
import org.springframework.boot.web.servlet.FilterRegistrationBean; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.filter.ShallowEtagHeaderFilter; | ||
|
||
@Configuration | ||
public class EtagFilterConfiguration { | ||
|
||
// @Bean | ||
// public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() { | ||
// return null; | ||
// } | ||
@Bean | ||
public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagHeaderFilter() { | ||
|
||
final FilterRegistrationBean<ShallowEtagHeaderFilter> bean = new FilterRegistrationBean<>(); | ||
final ShallowEtagHeaderFilter filter = new ShallowEtagHeaderFilter(); | ||
bean.setFilter(filter); | ||
bean.setUrlPatterns(List.of( | ||
"/etag", | ||
PREFIX_STATIC_RESOURCES + "/*" | ||
)); | ||
|
||
return bean; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,33 @@ | ||
package cache.com.example.version; | ||
|
||
import java.time.Duration; | ||
import java.util.List; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.boot.web.servlet.FilterRegistrationBean; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.http.CacheControl; | ||
import org.springframework.web.filter.ShallowEtagHeaderFilter; | ||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
@Configuration | ||
public class CacheBustingWebConfig implements WebMvcConfigurer { | ||
|
||
public static final String PREFIX_STATIC_RESOURCES = "/resources"; | ||
public static final String PREFIX_STATIC_RESOURCES = "/resources"; | ||
|
||
private final ResourceVersion version; | ||
private final ResourceVersion version; | ||
|
||
@Autowired | ||
public CacheBustingWebConfig(ResourceVersion version) { | ||
this.version = version; | ||
} | ||
@Autowired | ||
public CacheBustingWebConfig(ResourceVersion version) { | ||
this.version = version; | ||
} | ||
|
||
@Override | ||
public void addResourceHandlers(final ResourceHandlerRegistry registry) { | ||
registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**") | ||
.addResourceLocations("classpath:/static/"); | ||
} | ||
@Override | ||
public void addResourceHandlers(final ResourceHandlerRegistry registry) { | ||
registry.addResourceHandler(PREFIX_STATIC_RESOURCES + "/" + version.getVersion() + "/**") | ||
.addResourceLocations("classpath:/static/") | ||
.setUseLastModified(true) | ||
.setCacheControl(CacheControl.maxAge(Duration.ofDays(365)).cachePublic()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,62 @@ | ||
package thread.stage0; | ||
|
||
import org.junit.jupiter.api.Test; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.ThreadPoolExecutor; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import org.junit.jupiter.api.Test; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* 스레드 풀은 무엇이고 어떻게 동작할까? | ||
* 테스트를 통과시키고 왜 해당 결과가 나왔는지 생각해보자. | ||
* | ||
* Thread Pools | ||
* https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html | ||
* | ||
* Introduction to Thread Pools in Java | ||
* https://www.baeldung.com/thread-pool-java-and-guava | ||
* 스레드 풀은 무엇이고 어떻게 동작할까? 테스트를 통과시키고 왜 해당 결과가 나왔는지 생각해보자. | ||
* <p> | ||
* Thread Pools https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html | ||
* <p> | ||
* Introduction to Thread Pools in Java https://www.baeldung.com/thread-pool-java-and-guava | ||
*/ | ||
class ThreadPoolsTest { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(ThreadPoolsTest.class); | ||
|
||
@Test | ||
void testNewFixedThreadPool() { | ||
final var executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2); | ||
executor.submit(logWithSleep("hello fixed thread pools")); | ||
executor.submit(logWithSleep("hello fixed thread pools")); | ||
executor.submit(logWithSleep("hello fixed thread pools")); | ||
|
||
// 올바른 값으로 바꿔서 테스트를 통과시키자. | ||
final int expectedPoolSize = 0; | ||
final int expectedQueueSize = 0; | ||
|
||
assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); | ||
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size()); | ||
} | ||
|
||
@Test | ||
void testNewCachedThreadPool() { | ||
final var executor = (ThreadPoolExecutor) Executors.newCachedThreadPool(); | ||
executor.submit(logWithSleep("hello cached thread pools")); | ||
executor.submit(logWithSleep("hello cached thread pools")); | ||
executor.submit(logWithSleep("hello cached thread pools")); | ||
|
||
// 올바른 값으로 바꿔서 테스트를 통과시키자. | ||
final int expectedPoolSize = 0; | ||
final int expectedQueueSize = 0; | ||
|
||
assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); | ||
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size()); | ||
} | ||
|
||
private Runnable logWithSleep(final String message) { | ||
return () -> { | ||
try { | ||
Thread.sleep(1000); | ||
} catch (InterruptedException e) { | ||
throw new RuntimeException(e); | ||
} | ||
log.info(message); | ||
}; | ||
} | ||
private static final Logger log = LoggerFactory.getLogger(ThreadPoolsTest.class); | ||
|
||
@Test | ||
void testNewFixedThreadPool() { | ||
final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2); | ||
executor.submit(logWithSleep("hello fixed thread pools")); | ||
executor.submit(logWithSleep("hello fixed thread pools")); | ||
executor.submit(logWithSleep("hello fixed thread pools")); | ||
|
||
// 올바른 값으로 바꿔서 테스트를 통과시키자. | ||
final int expectedPoolSize = 2; | ||
final int expectedQueueSize = 1; | ||
|
||
assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); | ||
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size()); | ||
} | ||
|
||
@Test | ||
void testNewCachedThreadPool() { | ||
final var executor = (ThreadPoolExecutor) Executors.newCachedThreadPool(); | ||
executor.submit(logWithSleep("hello cached thread pools")); | ||
executor.submit(logWithSleep("hello cached thread pools")); | ||
executor.submit(logWithSleep("hello cached thread pools")); | ||
|
||
// 올바른 값으로 바꿔서 테스트를 통과시키자. | ||
final int expectedPoolSize = 3; | ||
final int expectedQueueSize = 0; | ||
|
||
assertThat(expectedPoolSize).isEqualTo(executor.getPoolSize()); | ||
assertThat(expectedQueueSize).isEqualTo(executor.getQueue().size()); | ||
} | ||
|
||
private Runnable logWithSleep(final String message) { | ||
return () -> { | ||
try { | ||
Thread.sleep(1000); | ||
} catch (InterruptedException e) { | ||
throw new RuntimeException(e); | ||
} | ||
log.info(message); | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,16 +2,17 @@ | |
|
||
import java.io.IOException; | ||
import org.apache.coyote.request.HttpRequest; | ||
import org.apache.coyote.response.HttpResponse; | ||
|
||
public interface Handler { | ||
|
||
boolean canHandle(final HttpRequest httpRequest); | ||
|
||
String handle(final HttpRequest httpRequest) throws IOException; | ||
void handle(final HttpRequest httpRequest, final HttpResponse httpResponse) throws IOException; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 주말에 알게되었는데 Request, Response가 각각 인자로 들어가는게 요구사항이었더라구요! 우르는 왜 이런 디자인을 가져야 한다고 생각하시나요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. request에 맞는 controller를 찾기 전에 response에 관한 전처리를 해줘야하는 경우가 있을까? 에 대해서 생각해봤습니다. 대표적인게 이게 맞는건지는 모르겠지만,, 톰켓이 설계한 이유 중에 아주 사소한 것 중 하나이지 않을까 싶습니다,, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그러게요 ㅎㅎ 컨트롤러에서 정한 응답 외에 오류 응답등이 있을 수 있겠네요 |
||
|
||
default String safeHandle(final HttpRequest httpRequest) { | ||
default void safeHandle(final HttpRequest httpRequest, final HttpResponse httpResponse) { | ||
try { | ||
return handle(httpRequest); | ||
handle(httpRequest, httpResponse); | ||
} catch (IOException e) { | ||
throw new IllegalArgumentException("I/O 작업 관련 에러가 발생했습니다."); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
학습 질문 중 '250개의 쓰레드가 모두 사용중일 때, 최대 100개의 연결을 대기하게 만드려면 어떻게 해야 하는가?' 라는 질문이 있었어요.
우르는 이걸 어떻게 풀어내야 한다고 생각하시나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
스레드 풀의 maxThread 의 개수를 250개로 설정하고 acceptCount를 100으로 하면 되지 않을까 싶습니다!!!
땡칠은 어떻게 생각하시나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 같게 생각했습니다.
'100개 연결 대기' 부분이 OS가 관리하는 backlog 부분이고, acceptCount로 조절 가능하다고 생각했어요!