- 초간단 요약 -
    기존의 자바 서비스하고 있던 부분의 일부를 node.js 로 포팅해서 두개를 벤치마크 해 봄. 자바는 멀티코어 환경에서 테스트하고 node.js 는 cluster 를 안쓰고 단일 코어에서 (아무것도 안하면 단일코어에서 도니 ㅎㅎ) 동작시켜봄 결과는 아래와 같음 



    씨피유와 메모리는 적게 쓰지만 (훨씬 적게..) 성능은 그리 낫지 않음. 이거저거 바꿔가며 테스트를 하던중 ORM 을 지원하는 Sequelizer 가 느리다는 것을 알아냄, node-mysql 로 바꾸고 나니 바뀐 내용입니다.



단일 코어를 씀에도 불구하고 자바와 비슷하게 성능이 나오고 있습니다. 같은 하드웨어라 가정하면 node.js 에 클러스터 (cluster)를 적용한다고 한다면 자바의 거의 5배에 해당하는 효율을 보일 것으로 예상됩니다. 

   글쓴이가 올린 다음 글을 보면 , node.js 는 성능이 워낙 뛰어나고, 여러사람들이 참여하기 때문에 무섭게 성장하는 개발환경이 될것이기 때문에 주의깊게 보고 있기는 하나, 기업용으로 쓰기에는 아직 치명적인 약점이 있다고 합니다. 바로 node.js 자체의 개발 주기가 너무 짧기 때문에 오랜 기간을 개발해야 하는 기업용 환경에서는 좋지 않다는 것입니다. (개발 하는 중간에 메인 언어가 업데이트 된다고 생각해 보세요 ㅎㅎ) 그렇기 때문에 1.xx 대로 올라가서 안정화가 된다면 그 때부터 기업용으로 쓰기에 무리 없지 않을까 라고 본답니다. 물론 저자는 아주 만족한 거 같다고 하더군요. 

  저도 글쓴이의 의견에 어느정도 동의합니다. 다만 지금 버젼으로도 정말 기업용 커다란 사이트가 아닌, 간단한 기능을 제공하는 사이트를 제작하는 데에는 (특히나 App 서버로) 최고의 효율을 보이지 않을까 싶습니다. 

Node.js express 를 이용해서 개발하다 보면 아쉬운 것이 한가지 있습니다. 본의 아니게 서버가 죽어버리는 일입니다. 물론 이런일이 발생하지 않게 에러 핸들링을 속된 말로 빡세게 해야 하지만 뭐 어떻게 알아서 그걸 다 하고 있습니까... (후다다다닥) 

만약 서버가 죽는다고 하더라도 우아하게 재시작 된다면 (gracefully restart 라는 표현을 쓰더군요) 추후에 비슷한 에러가 발생하지 않도록 조치를 취해줄 수가 있습니다.  

역시나 설치법은 무지 쉽습니다. npm 이 설치되어 있다고 가정한다면

$ sudo npm install forever -g 



이러면 설치되고, 원하는 스크립트 서버를 띄워주고 싶다면

$ forever start app.js


하면 데몬 형식으로 구동됩니다. 아! 로그도 봐야 하지요?

$ forever logs



하면 현재 띄워져 있는 스크립트랑 거기에 관한 로그에 대한 내역이 나옵니다.  내용을 보고 싶다면 , 예를 들어 한개의 스크립트 서버를 띄웠다고 가정한다면

$ forever logs 0



하면 tail 로 로그를 뒤지는 효과가 나옵니다. 자세한 설명은 원문을 찾아보시면 됩니다.

원문링크 : https://github.com/nodejitsu/forever 
최근에 서버가 사망하는 일이 발생했습니다. 내 다시는 LVM 에 데이터랑 OS 를 공존시키지 않으리란 다짐을 하게 만드는 사건이였습니다. 인프라를 다시 갖추는 작업을 한번쯤 정리해 볼 필요가 있을 것 같아서 정리해봤습니다. 

12.04 LTS 버젼으로 설치해 주는 것이 편합니다. 12.10 버젼은 Remote Desktop 으로 접속시 D 를 누르면 발생하는 문제가 있습니다. (모든 창이 미니마이즈 가 됩니다)

설치하자 마자 접속하면 '소프트웨어 업데이트'가 뜹니다. 이때 '설정'을 눌러서 다운로드 받는 서버를 'ftp.daum.net' 으로 수정해 줍니다. 

 $ sudo apt-get install ssh 


shell 접속이 가능하게 ssh 관련 소프트웨어를 설치해 줍니다. 특정 망회사는 ssh 기본 포트를 막아놓는 테러를 저지르기 때문에 /etc/ssh/sshd_config 에서 port 를 바꾸어 줍니다. 

$ sudo apt-get install xrdp


원격 접속이 가능하도록 xrdp 관련 소프트웨어를 설치해 줍니다. 

$ sudo apt-get install emacs23-nox 


주로 shell 로 접속해서 쓸꺼기 때문에 emacs 를 nox 버젼으로 설치해 줍니다. 

 $ sudo apt-get install gnome-session


원격 데스크탑을 원활히 돌리기 위해서 놈-세션을 설치해 줍니다. 
원격 데스크탑이 잘 돌아갈 수 있도록 조치를 취해줍니다.  예전에 올려둔 포스트 참조 

이제 쓸데 없는 하드들을 전부 한개로 묶어서 데이터 디스크를 만들어 줍니다. LVM 을 활용하는 것입니다. 
예전에 정리해 뒀습니다. 

 

 $ sudo vgchange -a n data_vg
 $ sudo vgreduce --removemissing data_vg
 $ sudo vgchange -a n data_vg
 $ sudo vgremove data_vg


LVM 을 운용하는 중에 하드가 하나 날라가 버린 가슴 아픈 사람들 (저 같은 경우..)의 경우에는 그 부분을 제거해 줘야 합니다. 이거를 먼저 실행해주고 LVM 을 생성해 주면 됩니다. 
(다시는 OS 와 Data 를 LVM 으로 묶지 않으리란 다짐을 했습니다. )

이제 다시 git 레파지토리를 생성해주면 됩니다. 제 노트북에 로컬 레파지토리는 전부 남아 있으니 서버에서 재 구성해주고 이쪽에서 그쪽으로 push 만 해주면 될 듯합니다. 

예전 정리 참조 

이렇게 또 퇴근 후 자유시간을 잡아먹게 되는 군요. 
NodeJS 는 예전부터 C/C++ 이 차지하던 위치를 (최근에 Python 이 차지한 것 같은 이야기가  있습니다) 차지한 것 같은 언어 입니다. (C/C++ 에 비하여) 어렵지도 않고 아주 쉽고 편하게 서버 어플리케이션을 만들 수 있는 쉬운 언어 입니다. 그 쉬운 NodeJS 를 살펴볼 일이 있어서 잠깐  살펴보게 됐습니다.

 설치법

여러가지 해줘야 하는 것이 있지만, 우리는 Ubuntu 를 쓰지 않겠습니까? 초 간단하게 설치가 가능합니다. (당연히 Mint 도 동일합니다)
   
    sudo apt-get install python-software-properties
    sudo apt-add-repository ppa:chris-lea/node.js
    sudo apt-get update
    sudo apt-get install nodejs npm   


 구동 테스트

간단하게 파일을 한개 편집해줍니다.

   
$ emacs example.js

그리고 다음과 같은 내용을 써 줍니다.
	
    var http = require('http');

    http.createServer(function (request, response) {
    response.writeHead(200, {'Content-Type': 'text/plain'});
    response.end('Hello World\n');
    }).listen(8124);

    console.log('Server running at http://127.0.0.1:8124/');

이제 다 됐습니다. 바로 확인해 보기로 합니다.

 
   $ node example.js

브라우져에서 http://localhost:8124 를 입력해서 제대로 동작하는지 테스트 합니다.

사실 이 정도 쉽게 해주는 방법은 많이 나왔지만, 이 언어가 각광 받는 이유는 여러가지가 있겠습니다. 일단 가볍고, 문법 자체가 JavaScript 기 때문에 배우기도 쉽고 (이거 될까? 하는게 다 되는게 자바 스크립트 입니다 ㅎㅎ) 그리고 만들어진 결과물 자체의 효율도 좋습니다.



http://maestric.com/doc/mac/fix_ssh_connection_delays

원본은 위를 참조하시면 되고요.  이 현상은 OSX 에서만 발생하는 것 같습니다. 제 OSX 는 Lion 최신 입니다. 

클라이언트 (제 경우로 말하자면 OSX Lion 입니다)

$ sudo emacs /etc/ssh_config


위 파일을 열어서 

# GSSAPIKeyExchange yes

라고 되어 있는 부분을 

GSSAPIKeyExchange no

로 바꿔 주시면 됩니다.  

서버 (제 경우로 말하자면  Ubuntu 11.10 입니다)

$ sudo emacs /etc/sshd_config


위 파일을 열어서 

#UseDNS yes

(혹시라도 ) 이런 부분이 있다면 

UseDNS no
 
로 바꾸거나 추가해 주시면 됩니다.  (대소문자 주의)


ps.
  이렇게 했는 데도 사실 조금만 빨라지는 것 같았습니다. 내부 네트워크에서는 빠른데, 외부에서 접속만 하면 느리더군요. OSX 만 그러니 미치겠더군요.  심지어 제가 가지고 있는 안드로이드 폰보다 느립니다. ㅎㅎ 

 



Ubuntu version - 10.04 LTS 루시드 링스(Lucid Lynx)

svn (뿐만 아니라 다른 소스 관리 툴)은 개발자 끼리 협업을 하기 위해서 뿐만 아니라 혼자서 연습 삼아서 개발한다고 해도 꼭 익혀야만 하는 개발자들만의 프로토콜 이라고 볼 수 있습니다.

이글은 우분투에 svn 서버 버젼을 세팅하는 데 관련된 팁입니다. 집에서 혼자 연습하는 경우라 하더라도 소스 레파지토리(Source Repository)를 쓰는 버릇을 들이는 것이 여러모로 좋습니다. 어쩌다가 외부에 나가서 협력하게 되는 경우가 생길 때 태연하게 '그냥 혼자 개발해서 합칠때 zip 으로 압축해서 한명이 합치면 되는 거지 뭘 귀찮게 그런걸 쓰나요? ' 라는 충격적인 질문을 받을 때가 많습니다. 중소기업 대기업을 가리지 않고 말이죠. 

1. 아파치, 서브버젼, 아파치에 연동되는 svn 모듈 설치

sudo apt-get install apache2 libapache2-svn subversion

2. 저장소 생성

sudo mkdir /svn

3. 아파치 관련 설정 파일 번경

sudo emacs /etc/apache2/mods-available/dav_svn.conf

개개인의 취향에 따라서 에디터 는 바뀔 수 있습니다. (emacs -> vim 이나 gedit)

다음에 나오는 것처럼 코멘트 기호를 삭제합니다. 

#<Location /svn>
..
#</Location>

를 

<Location /svn>
..
</Location>


# DAV svn

를 

DAV svn

로 

# SVNPath /var/lib/svn


SVNPath /svn


  
#AuthType Basic
#AuthName "Subversion Repository"
#AuthUserFile /etc/apache2/dav_svn.passwd


  AuthType Basic
  AuthName "Subversion Repository"
  AuthUserFile /etc/apache2/dav_svn.passwd


  #<LimitExcept GET PROPFIND OPTIONS REPORT>
    #Require valid-user
  #</LimitExcept> 


  <LimitExcept GET PROPFIND OPTIONS REPORT>
    Require valid-user
  </LimitExcept> 


4. 아파치 재시작 (restart)

sudo /etc/init.d/apache2 restart

만약 문제가 생긴다면 3번을 자세히 살펴봐서 틀린 것이 있나 확인합니다. 

5. 프로젝트에 접속할 계정 생성 

3번에서 AuthUserFile 로 지정된 파일에 생성해 줘야 하겠지요? 

sudo htpasswd -c /etc/apache2/dav_svn.passwd crazia
 - 비밀번호 입력 처리

crazia 는 제 아이디 입니다. 본인의 아이디로 바꾸서 만들면 됩니다. 입력을 하면 비밀번호 세팅하는 과정이 나옵니다. 만약 처음으로 저 파일에 계정을 추가하는 것이라면 '-c' 옵션을 쓰는 것이고 다음부터 사람을 추가하는 것이라면 '-m' 으로 옵션을 바꿔서 입력하시면 됩니다. 계정 생성 잘못 했는데 수정하고 싶으면 간단하게 dav_svn.passwd 파일을 삭제하는 것을 추천합니다. 

6. 프로젝트 디렉토리 와 소유권 변경

sudo svnadmin create /svn
sudo chown -R www-data:www-data /svn

svn 은 아파치를 거쳐서 작업하기 때문에 svn 프로젝트가 저장되는 공간을 www-data 계정의 소유로 바꾸는 작업입니다. 

7. 테스트 

예전에 포스트에서 만들었던 echo-server (클릭) 을 올려보기로 하지요. echo-server 가 저장된 곳이 '~/work/echo-server' 라고 가정하면 

cd ~/work
svn import ./echo-server http://localhost/svn/echo-server -m "create echo-server repository" 

그리고 브라우져에서 

http://localhost/svn/echo-server 

로 가서 제대로 올라갔는지 확인하세요. 





 
ACE 는 훌륭한 네트워크 프레임 워크 입니다. 서버를 작성할 때 신경써야 할 귀찮은 것들을 알아서 처리를 해줍니다. 그러한 ACE를 이용해서 우분투에서 간단한 에코서버(Echo Server)를 작성하는 방법을 알아보겠습니다. 

1. 설치 

그 귀찮은 ACE 받고 , 압축 풀고, 컴파일 하는 과정이 우분투에서는 간단한 명령 몇번이면 다 됩니다. 

sudo apt-get install libace-5.6.3 libace-dev libace-doc mpc-ace 

각각의 설명입니다. 
i   libace-5.6.3                    - C++ network programming framework         
i   libace-dev                      - C++ network programming framework develop
i   libace-doc                      - C++ network programming framework document
i   mpc-ace                         - makefile, project and workspace creator 

libace* 들은 ACE 관련 라이브러리와 헤더들입니다. mpc-ace 는 편하게 ACE 관련한 프로젝트 make 해주는 관리 툴입니다. (진짜 편합니다. 예전에 직접 작성해줄려고 하면 여러가지로 귀찮았습니다)


2. 환경 설정

.bashrc

export ACE_ROOT=/usr/share/ace
export LD_LIBRARY_PATH=/usr/lib:$ACE_ROOT/lib:$LD_LIBRARY_PATH 

라고 추가 설정해 주고 

source ~/.bashrc 

라고 입력해줍니다. 

3. 에코 서버 (Echo Server)

메아리 서버라고도 부르지요, 뭔가를 입력하면 똑같은 말을 돌려주는 기능을 가진 서버로 거의 모든 서버의 기본입니다. (서버 세계의 Hello World 라고나 할까요?)

이 소스는 어디선가 봐서 따온 것인데요. 누구것인지 기억이 안납니다. 혹시나 이 게시물을 보시게 되면 연락주세요 (__ 

9088 포트에 에코서버를 띄우고 클라이언트의 접속을 기다립니다. 

#include <ace/OS.h>
#include <ace/Log_Msg.h>
#include <ace/Message_Block.h>
#include <ace/INET_Addr.h>
#include <ace/Svc_Handler.h>
#include <ace/SOCK_Acceptor.h>
#include <ace/SOCK_Stream.h>
#include <ace/Synch_Traits.h>
#include <ace/Reactor.h>
#include <ace/Acceptor.h>
#include <ace/Reactor_Notification_Strategy.h>

class Stream_Handler : public ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> {
private :
	typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> super;
	
	ACE_INET_Addr remote_addr_;
	ACE_Reactor_Notification_Strategy noti_;

public :
	Stream_Handler()
		: noti_(0, this, ACE_Event_Handler::WRITE_MASK)
	{ /* empty */ }

	//override
	virtual int open(void * = 0)
	{
		ACE_TRACE("Stream_Handler::open");
		if( super::open() == -1 )
			return -1;
		noti_.reactor(this->reactor());
		this->msg_queue()->notification_strategy(¬i_);
		if( this->peer().get_remote_addr(remote_addr_) == 0 )
		{
			ACE_DEBUG((LM_INFO, "[DEBUG%T](%N:%l) ### New client accepted: %s:%u\n", 
				remote_addr_.get_host_addr(), remote_addr_.get_port_number()));
		}
		return 0;
	}

	//override
	virtual int handle_input(ACE_HANDLE handle = ACE_INVALID_HANDLE)
	{
		ACE_TRACE("Stream_Handler::override");
		char buf[1024];
		ssize_t recv_cnt;
		if( (recv_cnt = this->peer().recv(buf, 1024)) <= 0 )
			return -1;
		ACE_Message_Block *mb;
		ACE_NEW_RETURN(mb, ACE_Message_Block(buf, recv_cnt), -1);
		mb->wr_ptr(recv_cnt);
		this->putq(mb);
		return 0;
	}

	//override
	virtual int handle_output(ACE_HANDLE handle = ACE_INVALID_HANDLE)
	{
		ACE_TRACE("Stream_Handler::handle_output");
		ACE_Message_Block *mb;
		ACE_Time_Value nowait(ACE_OS::gettimeofday());
		while( this->getq(mb, &nowait) != -1 )
		{
			ssize_t send_cnt = this->peer().send(mb->rd_ptr(), mb->length());
			if( send_cnt == -1 )
				ACE_ERROR((LM_ERROR, "[ERROR%T](%N:%l) ### %p\n", 
                                "Stream_Handler::handle_output"));
			else
				mb->rd_ptr(send_cnt);
			if( mb->length() > 0 )
			{
				this->ungetq(mb);
				break;
			}
			mb->release();
		}
		if( this->msg_queue()->is_empty() )
			this->reactor()->cancel_wakeup(this, ACE_Event_Handler::WRITE_MASK);
		else
			this->reactor()->schedule_wakeup(this, ACE_Event_Handler::WRITE_MASK);
		return 0;
	}
	
	//override
	virtual int handle_close(ACE_HANDLE handle, ACE_Reactor_Mask close_mask)
	{
		ACE_TRACE("Stream_Handler::handle_close");
		ACE_DEBUG((LM_INFO, "[DEBUG%T](%N:%l) ### Connection close %s:%u\n", 
				remote_addr_.get_host_addr(), remote_addr_.get_port_number()));
		return super::handle_close(handle, close_mask);
	}
	
};


int ACE_TMAIN(int argc, ACE_TCHAR *argv[])
{
	ACE_INET_Addr listen;
	listen.set(9088);
	ACE_Acceptor<Stream_Handler, ACE_SOCK_ACCEPTOR> acceptor;
	acceptor.open(listen);
	ACE_Reactor::instance()->run_reactor_event_loop();
	ACE_RETURN(0);
}



또한 이 소스는 Reactor 를 쓰고 있는데 그것에 관한 설명은 다음으로 미루기로 하지요. 설명하자면 끝도 없을 테니까요. 위 내용을 server.cpp 로 저장합니다. 

4. mpc-ace 

mpc 는 복잡하게 작성해야만 했던 ACE 관련한 makefile 을 정말 편하게 만들어 주는 관리 툴입니다. 기본적으로 확장자가 mpc 인 파일이 필요합니다. 

server.mpc

project(server) : aceexe {
  exename = server
//  includes += directory_name other_directory
//  libpaths += /usr/X11R6/lib
 
  Header_Files {
    }
 
  Source_Files {
          server.cpp
  }
}

project(server) 를 보면 ( ) 안의 이름으로 make 파일이 만들어 집니다. 
aceexe 라고 쓰여진 것은 ace 관련된 실행파일로 만들라는 이야깁니다. 
exename = server 는 server 라는 이름의 실행파일이 만들어 집니다. 
includes 와 libpaths 는 추가되는 참조 디렉토리와 라이브러리를 더할 때 쓰입니다. 여기서는 안 쓰니 코멘트 처리합니다. 
Source_Files 에 컴파일할 대상을 적어줍니다. 현재 server.cpp 밖에 없습니다. 

mpc-ace server.mpc -type gnuace 

로 입력하면 GNUMakefile.server 라고 메이크파일이 하나 생성됩니다. 

make -f GNUMakefile.server 

로 입력하면 바로 컴파일 되고 실행파일이 생성됩니다. 





+ Recent posts