Oct 15, 2018 - 람다와 스트림

람다식

람다식은 메서드를 하나의 식(expression)으로 표현하는 것을 의미합니다.

- 객체 지향 언어보다는 함수 지향 언어에 가깝습니다.  
- 함수를 간략하면서도 명확한 식으로 표현할 수 있도록 해줍니다.  
- 메서드를 람다식으로 표현하면 메서드의 이름 및 반환 값이 없어지므로 익명 함수 라고도 합니다.   
- 람다식의 형태는 매개 변수를 가진 코드 블록이지만 런타임 시에는 익명 구현 객체를 생성합니다.  

함수형 인터페이스

함수형 인터페이스는 람다식을 다루기 위한 인터페이로 하나의 추상 메서드만 정의되어 있어야 합니다. (단, static 메서드와 default 메서드의 개수에는 제약이 없음) 단순하게 하나의 interface 를 만들고, 거기에 추상화 메소드를 만들면, 람다식이 가능하게 됩니다. 보통 new 객체를 만들 었을 때 추상화 객체에 대한 Anonymous 객체가 생성되었던 부분을 조금더 간결하게 만들어지는 것입니다.

- 함수형 인터페이스 타입의 매개변수 및 반환 타입이 함수형 인터페이스 타입이라면 람다식을 참조하는 참조변수를 매개변수로 지정하고 람다식을 가리키는 참조변수를 반환하거나 또는 람다식 자체를 반환할 수 있습니다.  
- 람다식은 Object 타입으로 형변환 할 수 없으며, 오직 함수형 인터페이스로만 형변환이 가능합니다.  
- 람다식 내에서 참조하는 지역변수는 final이 붙어 있지 않아도 상수로 간주되며, 외부 지역변수와 같은 이름의 매개변수를 허용하지 않습니다.  
- 함수형 인터페이스는 @FuntionalInterface 라는 어노테이션을 붙일 수 있습니다. (컴파일러에서 추상메서드를 갖춘 인터페이스인지 검사, javadoc 페이지에서 해당 인터페이스가 함수형 인터페이스임을 알 수 있도록 한다)

메서드 참조

저는 위의 함수형 인터페이스는 업무에서 거의 사용하고 있지는 않습니다. 오히려 static method를 호출하는 용도로 메서드 참조를 자주 사용합니다. 아직 실업무보다는 개인업무 혹은 배치쪽 작업할때 간혈적으로 사용합니다.

(a, b) -> Math.max(a, b);
Math::max

메서드를 참조해서 매개변수의 정보 및 리턴 타입을 알아내어 람다식에서 불필요한 매개 변수를 제거하는 것이 목적으로,
람다식의 매개 변수는 메서드의 매개값을 전달하는 역할만 하기 때문에 메서드 참조를 이용하면 깔끔하게 처리할 수 있습니다.

스트림

String[] strArr = {"aaa", "ddd", "ccc"};
List<String> strList = Arrys.asList(strArr);
Stream<String> strStreamArr = Arrays.stream(strArr);

스트링배열을 List배열로 바꿔주는 방법입니다. 보통 굳이 저렇게 객체를 두개나 만들 필요없이,

Stream<String> strStreamArr = Stream.of("aaa", "ddd", "ccc");

Stream.of 로 Stream 처리를 하는게 저는 편합니다. 물론 Arrays를 사용하여 java.util.Arrays.ArrayList(java.util.ArrayList 클래스와는 다른 클래스)를 리턴하여 사용할 이유가 있다면 위와같이 처리해야하지만, 보통 그냥 sort 하거나 강제적으로 변경되는 로직을 처리할 용도로만 사용하기 위해서 사용하는 부분이라 그렇게까지 처리할 이유가 없습니다.

String[] sortList = { "01", "02", "03", "04", "05" };
Stream.of(sortList).map(CodeUtils::getAdVrtsTpCode);

CodeUtils 의 static 메소드를 통해, 내가 원하는 코드값으로 변경하는 로직입니다. 예전에는 for문으로 돌리면서 일일이 mapping 처리를 해야했지만, 정말 편하게 변경이 가능합니다. 성능이라든가, 효율적인 부분에 있어, 많은 부분을 람다로 바꿔 개발하진 않겠지만, Stream 은 간혹 사용할 거 같아 약간 정리해봅니다.


참조


Oct 12, 2018 - nohup이란

가끔 crontab 에 등록된 shell script 가 동작을 하지 않고 에러가 났을 경우, 서버내에서 다시 실행해야할때, nohup 을 씁니다.

nohup이란 리눅스, 유닉스에서 쉘스크립트파일을 데몬형태로 실행시키는 프로그램으로, 터미널 세션이 끊겨도 실행을 멈추지 않고 동작하도록 합니다. shell script 를 실행하고 터미널을 종료하면 프로세스 또한 종료되므로, 터미널 세션을 끊겨도 사용해야할 경우에 사용합니다.

보통 명령어는 이렇습니다.

$ nohup shell.sh &
$ nohup sh -- ./shell.sh &

실제 full 경로를 입력하거나 해당 경로에 가서, ./shell 을 실행합니다. 종료하는 방법은 강제로 프로세스를 kill 하는 방법밖에 없습니다. (kill -9 PID)

참고로 위와 같이 nohup과 &을 같이 쓰는 형태로 암기하고 있었는데, &는 프로세스를 실행할 때 백그라운드에서 동작하도록 만드는 명령어로 비슷하지만 다른 명령어입니다.

nohup 으로 실행하면 hang-up signal 이 와도 동작하기 때문에 터미널 연결이 끊어져도 실행을 멈추지 않습니다. & 으로만 실행해도 터미널이 끊어져도 실행이 멈추지는 않는 데, & 은 백그라운드로 돌린다는 의미이며, 기본적으로는 nohup 이 아닐 경우 터미널이 끊어지면 실행도 끊어집니다. 하지만 요즘 옵션에 nohup 과 같은 동작을 하게 설정이 되어 있어서 & 만으로도 nohup 과 같은 동작을 보인다고 합니다.

$ nohup shell.sh > /dev/null

그래서 위와같이 & 대신에 > /dev/null 을 더 자주 쓰게 됩니다. /dev/null 은 주로 불필요한 출력 스트림을 버리는 곳으로 사용됩니다. /dev/null 을 사용하지 않을 경우, nohup.out 이라는 파일이 호출한 위치에 파일을 생성시키는데, 해당 파일을 생성하지 않게 합니다.

행의 극복

nohup으로 백그라운드 처리된 작업들은 원격 SSH 세션을 로그오프할 때 일반적으로 이들의 종료를 막기 위해 사용되며, 이 상황에서 발생할 수 있는 또 다른 문제는 ssh가 로그오프(행)을 거부하는 것인데, 그 이유는 백그라운드 작업들에 대한 데이터 손실을 거부하기 때문이다. 이 문제는 3개의 모든 입출력 스트림을 리다이렉션 처리함으로써 극복할 수 있다고 합니다.

$ nohup ./myprogram > foo.out 2> foo.err < /dev/null &

SSH 세션을 닫으면 무조건 의존 프로세스들에 HUP 신호를 보내지 않게 된다. 이는 의사 터미널의 할당 여부에 따라 달라진다고 하는데,

program < /dev/null program

형태일 때, file descriptor 0 즉 STDIN을 통해 입력인수(옵션에 입력 매개 변수가 있거나 입력 파일이 될 수 있음)를 취하는 것을 의미하는데, /dev/null은 아무것도 포함되어 있지 않으므로, 읽을 때 EOF (End of File)에 알릴 것이므로 /dev/null에서 입력을받는 프로그램은 기본적으로 아무 것도 입력 인수로 리디렉션하지 않습니다.

alias start='nohup java -jar healthcheck.jar /tmp 2>> /dev/null >> /dev/null &'

/tmp 란 폴더를 두어서 별도의 로그관리를 하겠다는 의미이나 에러로그를 /dev/null 에 받게 하기 때문에, 결국 /tmp 폴더에는 별다른 로그가 쌓이지 않게 됩니다.

alias start='nohup java -jar healthcheck.jar >> /dev/null &'

만약 이렇게 한다면 jar 파일 자체의 모든 out 스트림을 /dev/null 에 보내기 때문에 , 아예 동작을 하지 않거나 한번만 동작하고 더이상 실행이 안될 수 있습니다. (nohup을 했기 때문에 프로세스는 계속 떠있습니다. ㅠㅜ)


참조


Oct 8, 2018 - Scouter APM 개발환경 구성하기

2018년 10월 2일 기준,

이슈사항에 대해서 처리해보고 싶어서, eclipse-jee-photon-R-win32-x86_64 이클립스 사용하여,

git으로, https://github.com/youngclown/scouter.git 에서 import 로 받아 처리했습니다.

추출된 쿼리의 "/" (나누기 연산자 표현 누락현상)

현상으로,

스카우터 쿼리 파서를 만들어서 쓰다가, 해당 이슈로 인해, 강제로 “/”를 밀어넣는 로직을 구현할 수 밖에 없었습니다. (회사전용으로 특정 패턴시에 “/”를 강제로 삽입.)

간단하게 소스만봤을때 별도의 HttpEncoding을 하거나 특수문자를 삽입하는 부분이 없어, 그냥 넘기다가, 한번 기여해보고 싶어 제대로 된 분석을 하기 위해 셋팅을 하면서 기록을 남깁니다.

  1. Scouter Github 저장소에서 내 저장소로 Fork 한다.
  2. fork 된 projects 를 내려받는다.
  3. lombok 을 설치한다.
  4. scala flugin 을 설치한다. Scala IDE (http://download.scala-ide.org/sdk/lithium/e44/scala211/stable/site)
  5. build path를 확인하고, 만약 source 디렉토리에 /src/main/scala 가 포함되지 않았다면, 이를 포함시킨다.
  6. scouter.server 프로젝트에 scala nature가 포함되지 않았다면, 이를 프로젝트에 포함시킨다. (configure > scala nature)

여기까지 처리해서 소스를 봤더니, 해당 이슈는 클라이언트쪽에서 제대로 파싱을 하지 못하는 이슈로 보여서, 분석을 해보려고했는데, 이미 3일 전에 해결했네요.

우선 분석하고 다음에 기여할 수 있도록 해야겠습니다.