node js 포어그라운드(foreground)에서 실행

 

nodejs로 개발 하다 보면 보통 node app.js 명령어로 실행하는데 이는 foreground에서

실행 되기 때문에 터미널을 종료하면 같이 종료되는 문제가 발생.

 

 

 

node js 백그라운드(background) 실행 & forever CLI 도구(Command line interface) 사용

 

 > 백그라운드 실행

 

nodejs로 작성된 웹 서비스를 유지하기 위해 터미널 창을 매번 띄워 놓을 수도 없는 일.

이 같은 경우에 nohup 명령어를 사용하게 되면 사용자가 SSH 세션을 로그오프 해도 프로세스는

데몬 형태로 실행되기 때문에 지속적으로 실행되게 됩니다.

 

- nodejs 백그라운드 실행

nohup node server.js > app.log &

 

& 는 백그라운드 실행을 의미하고 출력파일은 app.log에서 확인 가능합니다.

nohup로 실행할 파일은 퍼미션 755 이상이어야 합니다.

 

- nodejs 프로세스 확인

ps -ef | grep node
root      72635  71903  0 11:02 pts/0    00:00:00 node server.js
root      72963  71903  0 11:07 pts/0    00:00:00 grep --color=auto node

ps -ef 를 통해 실행중인 프로세스 아이디를 확인해보면

72635이라는 ID로 실행 중임을 확인 할 수 있습니다.

 

- nodejs 프로세스 종료

kill -9 72635

종료는 kill process id : kill -9 {id}로 가능합니다.

 

 

- nodejs 프로세스 종료를 한줄로 실행하기

kill -9 $(ps -ef | grep node | awk '{print $2}')

 

$() 표현식은 내부에 위치한 명령을 실행하고 출력 값을 다음 명령에 대한 인수로 제공합니다.

이는 backquote key로 대체 하여 사용 가능합니다.

awk '{print $2}' 는 지정된 레코드로 부터 두번째 필드의 내용을 출력합니다.

 

 

 >  npm forever 모듈 사용(링크)

 

실제로 개발 & 서비스 환경에서 서버가 자꾸 내려가면 안 될 일.

따라서 forever 모듈을 사용해보았습니다.

 

$ sudo npm install forever -g

설치한 서버 인스턴스로 접근하여 npm 명령어를 사용, -g 옵션으로 global하게 forever모듈을 설치합니다.

 

$ sudo forever start server.js

sudo 명령어를 사용하여 서버를 구동해줍니다. (80포트가 root 계정에서 접근하기 때문)

 

$ sudo forever list

forever list 명령어를 사용하면 백그라운드에서 실행 중인 프로세스 정보를 확인 할 수 있습니다.

 

$ sudo forever stop 0

forever list 명령어로 확인한 uid(식별번호)가 0번이니 

sudo forever stop 0 명령어로 실행 중인 프로세스를 종료할 수 있습니다.

 

 

 

 

 

 


소프트웨어 패턴은 목적에 따라 생성 패턴, 구조 패턴, 행위 패턴으로 나뉜다.

오늘은 생성 패턴의 싱글톤 패턴에 대해 공부해보도록 하겠다.

1. Singleton Pattern 사용 이유?


싱글톤 패턴(Singleton Pattern)

    - 프로그램내에서 하나의 인스턴스만 만들어서, 언제 어디서든 그 인스턴스에 접근할 수 있도록 하기 위한 패턴

    - 커넥션 풀이나(DBCP) 환경 설정 클래스 같은, 하나만 사용되어야 하는 객체를 만들때 유용하다.

    - 생성자를 private로 선언하여 상속이 불가능함을 지정하기도 한다. 

2. Singleton Pattern 예제


가장 기본적인 싱글톤 패턴의 구현부에서 주목할 부분은 private 제약이다. 이는 하나의 인스턴스를 유지하고 new로
생성하여 사용할 수 없게한다. 


Lazy initialization

public class Singletone1 {
	
	private static Singletone1 singletone;
	
	public static Singletone1 getInstance() {
		
		if ( null == singletone ) {
			singletone = new Singletone1();
			return singletone;
		}
		
		return singletone;
	}
}


해당 laze initialization은 필요할때 인스턴스를 생성시는 것이 핵심이다.

Singletone1 객체는 getInstance method 내, if문으로 null인 경우에만 생성하였다.

이는 최초 사용할때만 인스턴스화 시키기때문에 객체가 heap으로 올라갈때의 시간과 비용을

줄일 수 있다고 한다. 하지만 문제는 프로그램이 multi thread 환경일때, 위와 같은 방법은 잠재적인 문제를 가지고 있다.

그 이유는 getInstance() method를 호출하면 인스턴스가 두번 생성될 위험이 있기 때문이다.


그렇다면 위와 같은 잠재적인 위험에서 안전한 코드를 작성하려면 어떻게 해야될까?

첫번째로는 getInstance() method를 동기화(synchronized) 시키는 것이다. 



Synchronized(해결1)

public class Singletone2 {
	
	private static Singletone2 singletone;
	
	public synchronized static Singletone2 getInstance() {
		
		if ( null == singletone ) {
			singletone = new Singletone2();
			return singletone;
		}
		
		return singletone;
	}
}

이로써 여러개의 thread들이 동시에 접근해서 인스턴스를 생성하는 위험은 피했지만, 

여러 thread들이 getInstance() method를 호출하게 되면 높은 비용(cost)로 인해 프로그램 전반적으로

성능 저하가 발생한다.



LazyHolder(해결2)

public class Singletone3 {
	
	public Singletone3() {}
	
	private static class LazyHolder {
		static final Singletone3 SINGLETONE3 = new Singletone3();
	}
	
	public static Singletone3 getInstance() {
		return LazyHolder.SINGLETONE3;
	}
}

위 코드는 LazyHolder를 통해 싱글톤의 동시성 문제를 해결하는 방법이다.

이는 자바 버전에 상관없으며, 상기 synchronized 키워드도 사용하지 않는다

여기선 Singletone3 class는 클래스 로드 시점에서 초기화 되지만, static class로 정의된 LazyHolder는 초기화 되지않고

static method인 getInstance()가 호출될때에만 실행된다. 이는 JVM에서 클래스를 로딩하고 초기화할때 원자성이

보장되기 때문에 thread safety한 방법이다.




참고 사이트 : https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpServletRequest.html


클라이언트의 요청 정보를 확인하기 위해서  Interface HttpServletRequest 객체를 사용합니다.

보통 Spring MVC 구조에서는 컨트롤러나 인터셉터에서 parameter를 추가하여 사용하는 편인데 그 내용은 아래 코드에서

확인하도록 하겠습니다.


  예제코드


sampleContorller.java

@RequestMapping(value = "/getDomainInfo/example/test", method = RequestMethod.GET) public ModelAndView getDomainInfo(HttpServletRequest request) { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("RequestURL", request.getRequestURL()); modelAndView.addObject("RequestURI", request.getRequestURI()); modelAndView.addObject("ContextPath", request.getContextPath()); modelAndView.addObject("ServletPath", request.getServletPath()); modelAndView.addObject("QueryString", request.getQueryString()); modelAndView.addObject("ServerName", request.getServerName()); modelAndView.addObject("ServerPort", request.getServerPort()); modelAndView.addObject("Method", request.getMethod()); modelAndView.addObject("Referer", request.getHeader("referer")); modelAndView.setViewName("test"); return modelAndView; }

test.jsp

<div> <h3>RequestURL</h3> <ul> <li> 요청정보 : ${RequestURL } </li> <li> 리턴정보 : ${RequestURL } </li> </ul> <h3>RequestURI</h3> <ul> <li> 요청정보 : ${RequestURI } </li> <li> 리턴정보 : ${RequestURI } </li> </ul> <h3>ContextPath</h3> <ul> <li> 요청정보 : ${ContextPath } </li> <li> 리턴정보 : ${ContextPath } </li> </ul> <h3>ServletPath</h3> <ul> <li> 요청정보 : ${ServletPath } </li> <li> 리턴정보 : ${ServletPath } </li> </ul> <h3>QueryString</h3> <ul> <li> 요청정보 : ${QueryString } </li> <li> 리턴정보 : ${QueryString } </li> </ul> <h3>ServerName</h3> <ul> <li> 요청정보 : ${ServerName } </li> <li> 리턴정보 : ${ServerName } </li> </ul> <h3>ServerPort</h3> <ul> <li> 요청정보 : ${ServerPort } </li> <li> 리턴정보 : ${ServerPort } </li> </ul> <h3>Method</h3> <ul> <li> 요청정보 : ${Method } </li> <li> 리턴정보 : ${Method } </li> </ul> <h3>Referer</h3> <ul> <li> 요청정보 : ${Referer } </li> <li> 리턴정보 : ${Referer } </li> </ul> </div>


  결과화면


http://localhost:8080/getDomainInfo/example/test?ver=2017&test_id=test 로 요청하여 jsp페이지에 출력된 결과화면입니다.


RequestURL :

 쿼리스트링을 제외한 Protocol(http)+ServerName(도메인)+Port(8080)+ContextPath+ServletPath를 반환합니다.

- 요청정보 : http://localhost:8080/getDomainInfo/example/test

- 리턴정보 : http://localhost:8080/getDomainInfo/example/test


RequestURI: 

 프로젝트의 Context Path + Servlet Path, 즉 전체 경로를 반환합니다.

- 요청정보 : /getDomainInfo/example/test

- 리턴정보 : /getDomainInfo/example/test


ContextPath :

 프로젝트의 Context path를 반환합니다.

- 요청정보 : /getDomainInfo

- 리턴정보 : /getDomainInfo


ServletPath :

 프로젝트의 Servlet path를 반환합니다.

- 요청정보 : /example/test

- 리턴정보 : /example/test


QueryString :

 쿼리 스트링(Query String)의 문자열을 반환합니다.

- 요청정보 : ver=2017&test_id=test

- 리턴정보 : ver=2017&test_id=test


ServerName :

 ServerName(도메인)을 반환합니다.

- 요청정보 : localhost

- 리턴정보 : localhost


ServerPort :

 ServerPort 정보를 반환합니다.

- 요청정보 : 8080

- 리턴정보 : 8080


Method: 

 get / post 정보를 반환합니다.

- 요청정보 : GET

- 리턴정보 : GET


Referer :

링크를 이동할 경우 이동하기 전에 링크를 포함하고 있던 페이지의 URL을 반환합니다.(링크를 클릭한 경우에만 출력)

- 요청정보 : (공백)

- 리턴정보 : (공백)