spring 5.0 이후 RouterFunction을 이용하여 EndPoint를 구현 할 수 있다. 이 RouterFunction을 스프링 빈으로 만들면 기존의 @Controller와 동일한 포지션의 역할을 수행하는데 어떻게 스캐닝을 하는걸 까?


Springboot 2.0 은 아직 개발중이지만, 그 안에서 확인 할 수 있다.

링크 를 보면

@ConditionalOnMissingBean(RouterFunction.class)
@ConditionalOnBean(RouterFunction.class)

즉, RouterFunction의 스프링 존재 여부에 따라 분기를 타고 AnnotationConfig, FunctionalConfig 설정을 적용하게 된다. 

즉 현재 기본 설정으로는 RouterFunction Bean, Controller Bean을 동시에 스캐닝 해주지 않는다 ('기본설정'에서')


1. List<RouterFunction<T>> routerFunctions 을 파라미터로 주입받아 하나의 RouterFunction으로 재구성한다. 

2. 오더링 하고, 기본 DefaultHandlerStrategiesBuilder, ViewResolvers를 설정한다.

3. WebHandler를 구성한다. 실제 구현 클래스는 HttpWebHandlerAdapter 이다. (만약 기존의 방식이었다면, 새롭게 WebHandler 인터페이스를 상속받는 DispatcherHandler 가 처리하게 된다)

신고

tomcat 다시 보기

분류없음 2017.04.23 14:00 posted by dev.bistro

로그

https://tomcat.apache.org/tomcat-8.0-doc/logging.html
– Apache Commons Logging에서 포킹되어 개발된 tomcat-juli를 사용한다 (위치는 /bin에 존재한다)
– 기본 로그 설정은 conf/logging.properties

로그포맷
– java.util.logging 을 상속 SimpleFormatter(톰캣7 기본 포맷) , XMLFormatter ..
– OnelineFormatter ; JULI구현체이고 8.0.36의 기본 포맷

VersionLoggerListener
– server.xml 에 Listener 으로 등록이 되어 있다.
– 실행히 여러 환경 정보를 남긴다.
– https://goo.gl/eEyI9k 을 보면 별 내용없다.

Valve
– server.xml <Host>밑에 있고, 로그를 남길 수 있다
– 기본 구현체는 AccessLogValve
– checkExists 로그 기록전에 파일의 존재 유무를 체크. 기본false이므로 운영중 파일을 삭제하면 로그는 생성안됨
https://goo.gl/ynbTVI 을 보면 true로 하면 로깅 할때마다 synchroized로 파일 찾는걸 볼 수 있다.
– directory : 로그 저장하는 디렉토리 기본은 logs
– prefix : 기본 localhost_access_log, suffix : txt

그 외 Apr
– AprLifecycleListener :Apr이 사용가능한지 안한지 체크한다.
없으면 “The APR based Apache Tomcat Native library …” 로그 남는데 보기 싫으면 주석 처리 하면 됨
만약 인식을 한다면 Connector은 APR방식으로 자동 설정된다. ( https://goo.gl/zMSDuR)

 

 
운영

– setenv.sh : 각족 환경 변수를 정의하는 파일. catalina.sh가 알아서 호출한다.
(JAVA_OPTS, CATALINA_OPTS, LD_LIBRARY_PATH 등..)
– kill 명령은 프로세스에 여러 시그널을 보내는 명령어로 진화했다.예를 들어 3은 SIGQUIT고, Thread덤프를 위한 시글이다.
SIGKILL 9 은 SIGTERM에 비해 덜 안전하지만 빠르게 중지한다. ( 시그널은 man kill로 확인하자 )
– 덤프를 떳을때
tid : Thread의 메모리 address, nid: Native Thread Id
NEW : Thread를 생성했지만 아직 시작하지 않은 상태
RUNNABLE : 실행중
BLOCKED : 다른 Thread가 소유하고 있는 Monitor를 소유하기 위해 대기 중인 상태
WAITING : Object.wait(), Thread.join() 등의 메소드에 의해서 대기중인 상태
TIMED_WATING : WAITING처럼 대기중이지만, ‘최대 대기 시간’이 지정되어 있다.
TERMINATED : 실행을 종료했거나 Exception 이 발생한 상태
– 톰캣의 중단 http://goo.gl/QU2b4a
– 톰캣의 시작 http://goo.gl/TQeJ0p


tomcat port


– 톰캣이 사용하는 포트는 http, https, ajp, Shutdown 총 4개이다.
(사용안한다면 https, ajp, Shutdown은 각각 제외 할 수 있다)
– netstat -an | grep 8080 : 이 포트가 사용중인지 확인하는 명령
– 톰캣은 기본설정은 http/8080, shutdown/8005, ajp/8009 이고 https/8443은 주석처리(사용안함) 이다.
– 0 ~ 1023 포트는 예약 포트라 root가 아닌 유저로는 사용 불가 ( https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#cite_ref-2 )
tomcat Connector
– Shutdown을 제외한 https, http, ajp는 connector에 의해 관리된다.
– connector의 옵션들에 대한 설명 https://tomcat.apache.org/tomcat-7.0-doc/config/http.html
– allowTrace : 디폴트false, 처리는 http://goo.gl/gk81DR
– enableLookups : req.getRemoteHost()는 DNS lookup을 통해 client 이름을 리턴하는데 disable이면 이 과정을 생략. 그렇게 되면 getRemoteHost, getRemoteAddr의 결과가 동일하게 IP로 나옴 http://goo.gl/nGGtKE
– maxPostSize : POST 최대 컨텐츠 크기 디폴트 2*1024^2(2MB), -1로 설정하면 무제한
– maxThreads : 최대 생성 Thread수 디폴트 200 http://goo.gl/2jJOIr 에서 ThreadPoolExecutor를 생성하는걸 확인 가능
– acceptCount : Thread Pool이 꽉 찬 경우에 queue에 대기하는데, 이 queue의 최대 수 설정
(즉 이게 크다면 많이 담을 수는 있지만 응답시간은 느리겠지?)
– maxConnections : 동시처리 가능한 Connection 수로 Connector구현에 따라 다르다.
BIO는 maxThreads값을 따른다. NIO는 10000 http://goo.gl/tuoS07
– URIEncoding : URI기본 인코딩으로 tomcat7은 ISO-8859-1, tomcat8은 UTF-8이다 (하지만 servelt 3.1표준은 ISO-8859-1이니 이건 참고만 하자)

tomcat protocol
– 톰캣8 에서는 Http11Protocol(blocking java connector)가 사라지고 nio가 기본이 되었네.
– tomcat7 bio : http://goo.gl/dNVbht , tomcat7 nio : http://goo.gl/CJB5vu
– 톰캣8에는 http-nio2를 prefix name으로 가지는 Http11Nio2가 생김 http://goo.gl/0E1JLP

http connector 의 속성
– maxKeepAliveRequests : -1은 무제한 , 1은 비 활성화
– keepAliveTimeout : 디폴트 connectionTimeout(20초) 이고 -1은 무제한 대기.
– disableKeepAlivePercentage : Thread가 maxThread대비 이 설정 퍼센트를 넘어갈때 keep-alive를 사용안함
– server : 응답 헤더의 server 속성에 노출 할 값 (기본은 Apache-Coyote…)

 

server.xml 

  • 1. Engine Namenode
    – defaultHost 속성 : Engine 하위의 Host 중에 하나이며, 어떤 Host도 처리하지 않는 요청을 처리한다.
    – 만약 2개 이상의 Host가 있다면 반드시 하나는 Engine의 defaultHost로 지정되어야한다.
  • 2. Host
    – 기본 구현은 https://goo.gl/5v7Jql (StandardHost.java)이며, 가상 호스트 기능을 제공한다.
    – appBase는 Host의 애플리케이션 디렉토리(디폴트 webapps)
    – xmlBase는 ContextXml(descript) 디렉토리. 기본은 Bconf/<Engine>/<Host> 디렉토리
    – autoDeploy : appBase, xmlBase내의 변경 사항을 주기적으로 확인하는 옵션
    – unpackWARs : WAR파일을 풀어서 사용할 지 여부
    – workDir : <host> 애플리케이션이 사용하는 Scratch 디렉토리를 설정
    – Scratch : JSP컴파일 결과 생성되는 java, class 파일등을 저장한다. 위치는 work/<Engine>/<Host>


신고
TAG tomcat, valve

aws lambda 에 대한 몇 가지

분류없음 2017.04.23 13:47 posted by dev.bistro

* lambda function 이 실행되는 storage 환경에 접근 가능하다.

1
2
3
4
5
6
var sp = require('child_process').spawn;
var ls = sp('df' , ['-h'] );
 
ls.stdout.on('data', function(data) {
console.log('stdout : ' + data);
});

Cloudwatch를 보면 아래처럼 로그가 남는다.

1
2
3
Filesystem Size Used Avail Use% Mounted on
/dev/xvda1 30G 2.8G 27G 10% /
/dev/loop5 526M 440K 514M 1% /tmp

* lambda 의 로그가 cloudwatch에 쌓이는 데 ‘즉시’ 쌓이지는 않는다.

* lambda + S3을 이용하려면 반드시 같은 리전에 있어야 하고 , S3 권한이 필요하다.

* 장시간 수행된다면 EC2에 비해 유리한 요금체계가 아니다 (1GB메모리/1달 기준 ec2는 9$, lambda는 37$)
lambda는 메모리가 낮을수록, 실행시간이 짧을수록 ec2에 비해 가격 효율이 좋다. 잘 비교해서 써야 비용 절감

* aws lambda 의 내부 구조에 대해서는 언급 된 적이 없다. https://aws.amazon.com/ko/lambda/
다만 컨테이너에 zip 소스코드가 실행 된다는 정도로 생각 해 볼 수 있는데,
대략 10~ 15분이 지나면 이 코드가 컨테이너에서 제거된다. 이후에 다시 function을 수행해면 어느 정도의 delay가 생긴다.
https://forums.aws.amazon.com/thread.jspa?threadID=181348 처럼 VM을 올려야 하는 java , upload size가 클수록 이 지연시간이 크다고 한다.

이러한 재시작 (cold start)를 효율적으로 대비 하는 방법은?
1. zip file을 가능한 작게 만든다
2. 10분 이하로 강제로 한번씩 실행하여 상태를 유지하도록 한다 (이 경우 한달에 4800번 정도가 추가로 호출된다)

* lambda 는 스케일링 세부 정책을 발표하지 않고 ‘자동으로 스케일링 요구사항에 맞춰 한다’ 라고만 한다. 즉, 개발자에게는 현재 스케일링에 관한 권한이 없다. 어플리케이션이 엄격한 지연시간등이 필요하다면 서비스 전에 많은 스트레스 테스트를 해봐야 할 것이다.
(app이 100ms의 지연시간까지 허용해야할 때 실제로 lambda가 200ms이 걸리더라도, lambda의 스케일링 조건이 아니라면 여전히 200ms가 걸릴 것이다)
* 현재 (2017/02/03) 최대 Timeout은 5분. 즉 5분 이상 넘어가는 function은 사용X

* lambda는 최대 1.5GB 메모리만 지원

* AWS lambda 를 사용하기 위해서 추가해야하는 lib들은 다음과 같다

‘com.amazonaws:aws-lambda-java-core:1.1.0’
‘com.amazonaws:aws-lambda-java-events:1.3.0’
‘com.amazonaws:aws-lambda-java-log4j:1.0.0’

출처 : https://github.com/aws/aws-lambda-java-libs (2017년 1월 기준)

* aws-lambda-java-log4j 는 이름 그대로 로깅을 위한 log4j 어댑터이다.
내부적으로는 log4j:1.2.17버전을 사용하고 있고 log4j의 AppenderSkeleton 를 확장한 LambdaAppender 와 log4j.properties를 사용한다.
http://docs.aws.amazon.com/lambda/latest/dg/java-logging.html)

* aws-lambda-java-events 는 S3, SNS, DynamoDB등을 이용하기 위한 extension library이다.
1.3.0 기준으로 aws 서비스 중 S3, SNS, kinesis, Cognito,dynamodb 등의 솔루션을 지원하고 AWS-SDK 1.11.0 에 의존성이 걸려 있다

* aws-lambda-java-core 는 기본적인 lambda를 쓰기 위한 라이브러리 이다.



2017/1월에 쓴  글을 옮겨옴

신고

springframework 5.0 webflux module

spring framework 2017.04.23 11:42 posted by dev.bistro


오늘 날자 기준으로 SpringFramework는  v5.0.0.M5 (링크) 가 나와있고, RC1 버전이 5월 예정되어 있지만 힘들지싶다 (링크)  Spring 5에는 webflux 모듈이 새로 추가되었지만,  사실 Spring5 마일스톤에서 착실히 진행 중이던  spring-web-reactive가 이름이 바뀐 모듈이다. (JIRA)

0. webflux 모듈은 webmvc를 대체하는 가?

우선 spring-mvc 모듈은 여전히 남아 있다. 기존에서 @RestController 을 사용하던 방식을 여전히 사용 할 수 있다.  (즉 webflux따위 몰라도 된다)

그리고 새로운 방법으로도 사용 할 수 있다. 링크 에서 말하는 것처럼 webflux 모듈은 완전히 새로운 방법 (Java8 람다를 이용한 라우팅/핸들링)과 @Controller와 handling을 사용하는 중간 적인 방법도 사용 가능하다.

요즘들어서 통합테스트의 어려움,  몇년간 Scala에서 느끼던 재미를 Java에서는 느끼지 못해 아쉬웠는데, webflux가 좀 해소해주지 않을까 해서 살펴볼 예정이다.

1. 프로젝트의 생성

Intellij2017.1 버전은 New Project 할때 SpringBoot 2.0.0-SNAPSHOT을 지원한다. 그러므로 쉽게 프로젝트를 생성 할 수 있다. 아니라면  링크 의 pom.xml,  Application.java를  참고한다.

2. Hello Controller 만들기

기존의 mvc모듈이 @RestController, @RequestMapping으로 이루어져 있다면, webflux는 RouterFunction, HandlerFunctions 2개 인터페이스를 알 면 된다. 그리고 HttpServletRequest,HttpServletResponse는 잊고, ServerRequest, ServerResponse를 기억 하면 된다.

가장 기본적인 ‘hello world’를 반환하는 /hello를 만들어보도록 하자.

public class HelloRouter {
    RouterFunction router() {
      return route(RequestPredicates.path("/hello"), helloHandler());
    }
 
    HandlerFunction helloHandler() {
        return request -> ServerResponse.ok().body(fromObject("Hello World"));
    }
}


작성하고 Application.java를 springboot로 실행해고 http://localhost:8080/hello 를 호출해도 404 NotFound 이다. 왜일까? 
문서 상으로 Running Server (링크)가 있지만 좀 귀찮은 방식이라 좀 더 찾아보니 링크 처럼 spring-boot-starter-webflux는 RouterFunction beans에 대해서 자동으로 디텍팅을 해준다고 한다. 

그래서 class HelloRouter 위에 @Configuration 와, router() 메소드 위에 @Bean을 붙여준 후 다시 http://localhost:8080/hello 를 실행해본다면 ‘Hello World’ 메세지를 확인 할 수 있을 것이다.

@Configuration
public class HelloRouter {
    @Bean
    RouterFunction router() {
        return route(RequestPredicates.path("/hello"),
                     request -> ServerResponse.ok()
                                              .body(fromObject(new Date())));
    }
}
처럼 좀 더 괜찮은 방식으로 써도 괜찮다. 

3. 여러개의 Router ( Controller ) 만들기 


 위의 예제를 보면 1개의 API는 1개의 Router를 만들고 Bean으로 등록해야 하는 것 같다. 하지만 그렇게 불편해지면 다 안쓰겠지
@Bean
RouterFunction router() {
    return route(GET("/hello"), request -> ServerResponse.ok().body(fromObject("Hello-World")))
        .andRoute(GET("/time"), request -> ServerResponse.ok().body(fromObject(new Date())))
        .andRoute(DELETE("/delete"), request -> {
                      System.out.println("delete");
                      return ServerResponse.ok().body(Mono.empty());});
}


처럼 andRoute를 이용해서 체이닝을 할 수도 있고 많은 static method를 이용해서 URI Mapping도 좀 더 짧게 코딩 할 수 있다. 아래 처럼 메소드 래퍼런스를 이용하여 좀 더 깔끔하게 정리할 수도 있다.

public class HelloHandler {
 
  public Mono helloWorld(ServerRequest request){
    return ok().body(fromObject("Hello-World"));
  }
 
  public Mono time(ServerRequest request){
    return ok().body(fromObject(new Date()));
  }
 
  public Mono printlog(ServerRequest request){
    System.out.println("delete");
    return ServerResponse.ok().body(Mono.empty());
  }
}
 
@Configuration
public class HelloRouter {
  HelloHandler helloHandler = new HelloHandler();
  @Bean
  RouterFunction router() {
    return route(GET("/hello"), helloHandler::helloWorld)
      .andRoute(GET("/time"), helloHandler::time)
      .andRoute(DELETE("/delete"), helloHandler::printlog);
    }
  }

4. Request에서 Parameter 받아오기 


고전적인 방식의 PathVariable이랑, Command pattern인 RequestBody의 구현에 대해서 아래 코드를 참고 하면 된다.
public Mono getName(ServerRequest request){
   String name = request.pathVariable("name");
   return ok().body(fromObject(name));
}
 
//curl -H "Content-Type: application/json" -X POST -d '{"id":"id","name":"name"}' http://localhost:8080/param/body
public Mono getBody(ServerRequest request){
   Mono notFound = ServerResponse.notFound().build();
   Mono user = request.bodyToMono(User.class);
   return user.flatMap(u -> ok().body(u).switchIfEmpty(notFound));
}
springboot가 스타트업 될때 로깅메시지를 보면 ReactiveWebServerApplicationContext와 함께 /hello 매핑 정보가 없다는 것도 볼 수 있다.
2017-04-22 21:07:59.915  INFO 1806 --- [           main] .r.c.ReactiveWebServerApplicationContext : Refreshing org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext@1dd92fe2: startup date [Sat Apr 22 21:07:59 KST 2017]; root of context hierarchy
2017-04-22 21:08:00.701  WARN 1806 --- [           main] o.h.v.m.ParameterMessageInterpolator     : HV000184: ParameterMessageInterpolator has been chosen, EL interpolation will not be supported
2017-04-22 21:08:00.909  INFO 1806 --- [           main] o.s.w.r.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.reactive.resource.ResourceWebHandler]
2017-04-22 21:08:00.910  INFO 1806 --- [           main] o.s.w.r.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.reactive.resource.ResourceWebHandler]


gitlink : https://github.com/bistros/webflux-sample


신고


티스토리 툴바