2013년 12월 16일 월요일

[JMeter] JMeter로 웹소켓 테스트 하기



검색결과 국내에 JMeter-웹소켓 테스트 정리한 자료가 없어서

정리해 봅니다.


이 포스팅에서는

가장 보편적으로 쓰이는 로그인으로 예를들어 테스트를 해보겠습니다.


1. JMeter는 다음 페이지에서 다운 받을 수 있습니다.
http://jmeter.apache.org/download_jmeter.cgi


2. JMeter Websocket 
Kawasima 씨가 만드신 JMeter 플러그인을 다운 받습니다.
https://github.com/kawasima/jmeter-websocket

또는 소스코드 빌드가 귀찮으신 분은 Jar파일을 직접 다운 받으시면 됩니다.
-maven 3.0 으로 빌드되었습니다.
https://dl.dropboxusercontent.com/u/89045969/jmeter/ApacheJmeter_websocket-0.1.0-SNAPSHOT.jar
-With Dependencies
https://dl.dropboxusercontent.com/u/89045969/jmeter/ApacheJmeter_websocket-dist-0.1.0-SNAPSHOT.jar


3. 다운받은 jar파일을 \apache-jmeter\lib\ext 에 
집어넣은 후, JMeter를 실행.


4. Thread Group 을 생성합니다.















생성한 Thread Group을 선택하면
Number of Threads (몇개의 Thread를 만들 것인지.)
Ramp-Up Period (Thread 당 생성 시간)
Loop Count (하나의 Thread가 해당작업을 몇 번 수행 할 것인지.)
항목이 있습니다.

예를들어,
Number of Threads를 100
Ramp-Up Period를 100
Loop Count를 100
으로 설정해서 돌리면 Thread가 초당 하나씩 100개 까지 생성되면서
각각 작업을 100번씩 수행하겠죠? 그럼 10000번 수행하게 됩니다.

그럼 Ramp-Up Period를 0으로 설정하면?
100개의 Thread가 0초 즉 동시에 생성되어 작업을 수행합니다.
이것도 10000번 수행하게 됩니다.
즉 Ramp-Up Period를 작게 설정하면 Throughput이 더 나올 거고
테스트에 걸리는 시간이 더 단축될겁니다. 서버에는 부하가 더 가해지겠죠.


5. 웹 소켓 샘플러를 생성합니다.



6. CSV Data Set Config 생성
먼저 설명을 좀 드리면 제 웹소켓 서비스 제공 서버는 Node.js 입니다.
받은 요청을 '/' 로 스플릿 하여 어떤 서버측 함수를 호출 했는지 구별하고,
서버측 내부 함수(reqLogin)에 파라미터로 '/' 이후 부분을 넘겨주는 식입니다.


다양한 값을 테스트해보기 위해 데이터 셋을 만듭니다.
물론 Send message 부분에 직접 값을 박아서 해도 됩니다.
${ID} 와 ${PW} 부분에 실제 데이터를 집어넣으시면 되는거죠.














(잘 안보이시는분, Send message 에
reqLogin/{"ID":"${ID}","PW":"${PW}"} 라고 적혀있습니다.)


자 이제 CSV Data Set Config를 생성합니다.




생성한 CSV Data Set Config 를 눌러서
Filename 부분에는 csv 파일경로(txt파일도 가능합니다.)
Variable Names(comma-delimited) 에는 ID,PW 라고 적습니다.

csv 파일은 이런식으로 구성 하면 됩니다.



테스트를 실행하면 id1,pw1 로 로그인을 시도하기 시작하는데요

일단 잠깐..


5. View Results Tree를 생성합니다.

테스트 후 결과를 보기위한 리스너를 달아줍시다.




View Results Tree를 생성해 요청 및 반환값을 확인합니다.

단, 여기에는 중요한 이슈가 있습니다.
많은 Thread Group을 돌릴 경우에, View Results Tree 때문에
느려지는 현상이 있습니다. 심하면 에러율이 100%까지 올라가며 JMeter가 멈춥니다.
이걸 몰라서 고생을 좀 했네요.


7. Summary Report 생성
Thread Group들의 테스트 결과를 보기위해 요약 리포트를 생성합니다.
쉽게 에러율과 Throughput 등을 확인 할 수 있습니다.




8. 시작, 비우고 시작
재시작 하실땐 비우고 하셔야 테스트가 제대로 됩니다.
검정색이 시작, 붉은색이 비우는겁니다.



9. 테스트 결과















Number of Threads를 100

Ramp-Up Period를 1
Loop Count를 100

으로 테스트 한 결과입니다.

(Constant Throughput Timer를 단 후 20만 정도의 적당한 값을 설정하면
더 높은 Through put 이 나오기도 합니다.)

2013년 12월 15일 일요일

[Node.js] Mysql - ReleaseConnection 에러

Hello SyntaxHighlighter

Cannot call method 'ReleaseConnection' of null

참조 페이지
https://github.com/dougwilson/node-mysql/commit/ef13ca08dfc8025a93c607656119b7abb3b3b2aa
// Release Connection시 에러가 날 때가 있는데요.
// 해결 방법입니다.


//  lib/PoolConnection.js 수정하기

PoolConnection.prototype.release = function () {
  if (!this._pool || this._pool._closed) {
      return;
    }
  return this._pool.releaseConnection(this);
};


//  test/integration/pool/test-destroy-connection.js 수정하기

var common     = require('../../common');
var assert     = require('assert');
var Connection = require(common.lib + '/Connection');
var pool       = common.createPool();

pool.getConnection(function(err, connection) {
  if (err) throw err;
  assert.strictEqual(connection, pool._allConnections[0]);
  connection.destroy();

  assert.ok(pool._allConnections.length == 0);
  assert.ok(!connection._pool);

  assert.doesNotThrow(function () { connection.release(); });
  
  pool.end();
});






2013년 12월 7일 토요일

[MySQL] Query with `Join` and `If`


다음에 보면 더 간단하게 짤 수 있지 않을까라는 생각이 들어 메모합니다.


테이블을 잠깐 설명하면


어떤 ID가 계획(부모)을 만들고 나면
다른 ID는 부모 계획을 가리키는 동일한 계획(자식)을 만들 수 있습니다.
자식계획을 만들었다고 해서 반드시 계획에 참여 하는 것은 아닙니다.
(부모 계획에 불참 가능)


(부모 자식이라는건 개념 이해를 돕기 위한것입니다.)


 User 테이블, Plan 테이블이 있고,

User 테이블에는
 `ID`와 `NM`(이름) 컬럼이 있습니다.

Plan 테이블에는 
 Primary키인 `index` 컬럼
 Unique키인 (`ID`,`code`) 컬럼
 index 컬럼을 참조하는 `plan` 컬럼
 참여를 뜻하는 일반 컬럼 `go` 가 있습니다. 


예를들어
index가 11이고 code가 1111인 부모(plan=1) 계획에 kim은 참여(go=1) 합니다.
index가 12이고 code가 1111인 자식(plan=11) 계획에 ston는 참여(go=1) 합니다.
index가 13이고 code가 1111인 자식(plan=11) 계획에 john는 불참(go=0) 합니다. 
ㅡㅡㅡㅡㅡㅡㅡㅡ
index가 20이고 code가 1111인 부모(plan=1) 계획에 poul은 참여(go=1) 합니다.
index가 21이고 code가 1111인 자식(plan=20) 계획에 nice는 참여(go=1) 합니다.


위 예제에서 index 11번이 삭제될 경우
index 12,13번도 같이 삭제시키고 Index 20 ,21 번은 삭제 안되게 하려면?
외래키 plan에 대해 Cascade로 설정하면 되겠네요.


진짜 문제는 다음과 같습니다.

클라이언트에서
kim과 1111을 가지고 쿼리했을 때
ston과 1111을 가지고 쿼리했을 때
john과 1111을 가지고 쿼리했을 때 

동일하게 이 계획에 참여하는 kim, ston의 이름을
출력해야 합니다.
(코드만 같고 다른 계획에 속한 poul,nice의 이름이나
계획은 있지만 불참하는 john의 이름은 출력하면 안됨)


다시말해
poul과 1111을 가지고 쿼리했을 때
nice와 1111을 가지고 쿼리했을 때

동일하게 poul과 nice의 이름이 출력 되어야 겠죠.






쿼리를 다음처럼 구성해보았고 결과는 잘 나오는데요
이게 과연 성능상 좋은 쿼리인지는 잘 모르겠네요.

SELECT NM
FROM Plan join `User` on `Plan`.ID=`User`.ID
WHERE ((plan=if(
(SELECT plan FROM Plan WHERE ID=? and code=?)=1,
(SELECT `index` FROM Plan WHERE ID=? and code=? ),
(SELECT `plan` FROM Plan WHERE ID=? and code=?)))
or (`index`=if(
(SELECT `plan` FROM Plan WHERE ID=? and code=?)=1,
(SELECT `index` FROM Plan WHERE ID=? and code=? ),
(SELECT `plan` FROM Plan WHERE ID=? and code=?))))
and go=1

짤땐 힘들었는데 짠다음 줄바꿈을 하고 보니 간단하네요.

쿼리를 설명하면


FROM 절 :
이름을 얻기위해 Plan과 User 테이블을 조인하는거구요


Where 절 :
1. 부모인 경우
ID와 code 로 쿼리했을 때 `plan`값이 1이겠죠.(부모) 그래서
부모의 `index`값으로 `plan`을 쿼리해 자식 계획들을 찾습니다.
근데 이러면 여기에 자식들만 포함되고 자기 자신은 포함 되지 않았죠?
그래서 or 이후 에서
plan이 1인경우 자기자신을 포함하는겁니다.

2.자식인 경우
ID와 code 로 쿼리했을 때 `plan`값이 1이 아니겠죠(자식)
그럼 자기자신을 포함한 자식들을 찾습니다.
`plan`값을 통해서요.
근데 부모가 포함되지 않았죠?
그래서 or 이후에서 1이 아닌 경우 자신의 `plan` 값으로
자신의 부모 `index` 를 찾습니다.



2013년 11월 21일 목요일

[Ucrew] Ativ Tab 3 사용 후기



저는 정말 다양한 곳에서 이 기기를 사용하고 있는데요.

추천해드리고 싶어 포스팅 합니다.



학교

예전에는 항상 모든 PPT를 다 출력해서 필기했었는데

저희 과는 모든 과목이 PPT나 PDF를 이용하기 때문에

때문에 종이 낭비가 굉장히 심했었어요.

(A4 한통 산거 한 학기에 다쓰고도 모자람...)










하지만 이젠

이렇게 강의중에 Ativ Tab 3를 켜고

PPT를 띄워서 그대로 필기를 할 수 있습니다.





















 S 노트 앱을 켜면 이렇게 필기도 되고 음성 메모라고 해서

교수님이 말씀하고 계신것을 녹음도 할 수 있어요 !

사실 이 기능은 있는지 잘 몰랐었는데

앞으로 유용하게 사용할 수 있을 것 같습니다.













동아리

저는 오케스트라 동아리에서 활동중인데요.

악보를 안가져 왔거나

새로운 곡을 한번 연주해보고 싶을때.

항상 악보를 뽑을 수는 없잖아요?










요렇게 악보를 바로 띄우면 됩니다!

얼마나 간편한지.

옛날 같았으면 이 추운날 다시 도서관 가서

악보를 프린트해와야 합니다.











전공

그리고 저는 소프트웨어 학과다 보니

코딩해야 할 일이 많습니다.









네 여긴 지하철인데요,

이렇게 가지고 다니면서

코딩도 할 수 있어

시간이 절약됩니다!

(Visual Studio 2012입니다.)










그래봤자 탭인데 뭐가 잘 되겠어? 라고 생각하시는 분들

보십시오.























자그마치 버츄얼머신이 돌아갑니다 !

왼쪽은 Putty로 오른쪽 리눅스에 터미널 접속을 하려고 하는 것이구요

오른쪽은 리눅스(Redhat)를 VirtualBox에서 띄운 화면입니다.



코딩도 되고 VM도 돌아가고 오피스도 되고 필기에 음성녹음...

성능이 좀 떨어지긴 하지만

여태 이거 없이 어떻게 살았나 싶을 정도로

이번학기에

저에게 있어서 정말 없어서는 안될 소중한 기기라고 생각합니다.

특히, 가벼우니까요.


강추.


2013년 11월 18일 월요일

[Node.js] MySQL 커넥션이 빠르게 끊어질 때

Hello SyntaxHighlighter

Mysql Pool

이번에 사용 하는 클라우드 서버는
Mysql 설정 파일을 바꿔도 윗단에서 커넥션을 빠르게 끊어버리는 바람에 문제가 되었습니다.

맨 처음에는 setInterval와 end 함수의 콜백을 이용해 특정 시간이 될 때마다
커넥션을 모두 끊고 다시 연결을 만들도록 했었는데요.
그래도 어느 순간이 되면 커넥션 에러가 발생 했습니다.

다음 시도는 Mysql-simple-pool모듈을 사용 하는 것이었습니다.
저번에 Mysql-simple-pool에 관해 포스팅을 한 적이 있었죠.
그런데 사용시 전에는 없었던 문제가 생겼습니다. 그것은 쿼리를 처음 할 땐 에러가 난다는 것이었죠.
쿼리를 몇 번 하고 나서야 에러가 안나고 잘 처리 되었습니다.

여러가지 해결 방법을 찾아보다가 Mysql모듈도 Pool을 지원한다는 사실을 알게 되었습니다.


var mysql = require('mysql');

var mysqlConfig = {
  host:       "아이피",
  port:       "포트",
  user:       "아이디",
  password:   "암호",
  database:   "데이터베이스" 
  };

var pool= mysql.createPool(mysqlConfig);

    pool.getConnection(function(err,connection){
        connection.query("쿼리",function(err,rows){
        //rows를 처리할 내용
        //release를 해주어 커넥션이 pool로 되돌아 갈 수 있도록 해줍니다.
        connection.release();
        //이제 이 커넥션은 pool로 돌아가 다른 주체가 사용 할 수 있도록 준비합니다.
        });
    });

//추가사항 : 이렇게 해줘도 끊기는 현상이 발생 할 때가 있었습니다.
//이건 최후의 방법인데 특정 시간마다 연결했다 끊는겁니다.
//저는 결국 이 방법으로 해결했습니다.

function keepAlive(){
   pool.getConnection(function(err, connection){
     if(err) { return; }
     connection.ping();
     connection.release();
   });

    //redis client를 사용중이라면, 아마 Redis연결도 빠르게 끊길겁니다.
    //client.ping();  // 라고 해주면 Redis연결도 유지합니다.
 }
 setInterval(keepAlive, 60*1000);







2013년 11월 14일 목요일

[Node.js] Android-client not handshaken client should reconnect

Hello SyntaxHighlighter

Socket.io



이 문제가 또 저를 붙잡았습니다.

예전에 자바스크립트로 코딩할 때, 알 수 없는 이유로 클라이언트 측에서

엄청나게 많은 reconnect를 요구하는 바람에 노드 서버가 아무것도 못하고

뻗어버리는 현상이 있었는데요

그때는 자바스크립트 측에 강제로 횟수 제한을 두어서 계속 연결 시도를 못하게 했었습니다.

이번에는 클라이언트가 자바인 경우인데요.

많은 Reconnect를 요구하는 것이 아니라 제대로 연결이 안됩니다.

게다가 될때도 있고 안될때도 있어서 매우 혼란스러운 상황이었습니다.

일단 로그는 다음과 같습니다.

서버측 : client not handshaken client should reconnect
클라이언트측 : IOCallback() 의 onError 작동

해결은 다음과 같이 했습니다.
// 사용 : https://github.com/Gottox/socket.io-java-client

static public int socketCnt=0; // 사용될 전역변수입니다.
static Thread tRecv;

public static void initialize(){
    tRecv = new Thread() {
    public void run() {
    try {
     socket = new SocketIO("http://***:***/");
     IOCallback IOC=new IOCallback() {
      @Override
      ...

      @Override
       public void onError(SocketIOException socketIOException) {
         System.out.println("an Error occured");
                   //그러니까 요지는, 에러가 나면 다시 연결을 시도 한다는 겁니다.
                   //횟수 제한을 두도록 했습니다.
         try {
  if(socketCnt<10){
  socket = new SocketIO("http://***:***/");
  socket.connect(this);
  socketCnt++;
  }
 } catch (MalformedURLException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }
 socketIOException.printStackTrace();
 }
     @Override
     ...
     socket.connect(IOC);
 } catch (Exception e1) {
 // TODO Auto-generated catch block
 e1.printStackTrace();
 }
     }
            
   };
   tRecv.start();
 try {
 tRecv.join();
 } catch (InterruptedException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
}
일단은 해결 된 듯 보이지만 시간이 지나봐야 알 것 같네요ㅋㅋ

2013년 11월 12일 화요일

[CentOS] Node Forever


서버가 죽어도 프로그램을 재시작하는 Forever를 설치하고 실행해 봅시다.

npm install forever -g 로 설치합니다.

forever start /경로/실행시킬 서버 자바스크립트파일
forever stop /경로/실행시킬 서버 자바스크립트파일

forever list 를 치면 [0]등 번호가 나옵니다.

그 번호를 이용해서

forever stop 0 은 멈춤

forever logs 0 은 로그를 볼 수 있습니다.

2013년 11월 2일 토요일

[CentOS] Security (보안 설정)

CentOS 6.x 기준

SSH 로그인
vi /etc/ssh/sshd_config

#루트권한 로그인을 막으려면 파일을 열어 다음을 추가한다.
PermitRootLogin no

#패스워드 없는 계정으로 로그인이 불가능 하도록 한다.
PermitEmptyPaswords no

TCP Wrapper
vi /etc/hosts.allow
#Wrapper를 이용하여 방화벽 역할을 하게 한다.
#ftp 데몬을 들어오는것 중 211.189 대역 허용
in.ftpd:211.189.0.0/255.255.0.0

vi /etc/hosts.deny
#인가 되지 않은 것은 모두 막는다.
ALL:ALL

sshguard 설치
rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
wget http://sourceforge.net/projects/sshguard/files/sshguard/sshguard-1.5/sshguard-1.5.tar.bz2/download
bunzip2 sshguard-1.5.tar.bz2
tar -xvf sshguard-1.5.tar
cd sshguard-1.5
sudo yum install gcc make
sudo ./configure --with-firewall=iptables
sudo make && sudo make install

chkrootkit 설치
rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
yum install chkrootkit
#실행시킨다
chkrootkit 



보안은 양날의 칼입니다. 보안을 강화하면 불편해지고 느려지죠

편리하고 빠르면서도 강한 보안을 갖게 할 수는 없을까요?...

다음은 방화벽에 대해 포스팅 해 보도록 하겠습니다.

2013년 11월 1일 금요일

[Node.js] Redis 의 List 이용하기

Hello SyntaxHighlighter

Redis 의 List (lpush, llen, lrange, lrem)

var redis = require('redis');
/*
* (왼쪽에)
* 값을 넣는것은 lpush
* 길이 측정은 llen
* 범위 출력은 lrange
* 특정 값 삭제는 lrem
*
* (오른쪽)은 안해봤는데 아마 앞에 접두어로 r이 붙이면 될 것 같습니다.
*
* 그리고 모든 함수는 콜백을 지원합니다.
* 예 ) client.lrange('list',0,-1,function(err,obj){ ... });
*/
var client=redis.createClient(포트,호스트,null);

client.lpush('list','a');    //키 list에 a가 들어갑니다.
client.lpush('list','b');    //키 list에 b가 들어갑니다.
client.lpush('list','c');    //키 list에 c가 들어갑니다.
client.lpush('list','d');    //키 list에 d가 들어갑니다.

client.lrem('list',1,b);     //키 list에 b가 있다면 하나만 지웁니다.
client.lrange('list',0,-1);  //키 list를 모두 출력합니다. a, c, d 가 나옵니다.
client.lrange('list',1,2);   //키 list의 index기준 1~2를 출력합니다. c, d 가 나옵니다.
client.llen('list');         //키 list의 길이를 출력합니다. 3 이 나옵니다.



Node.js 에서 Redis의 리스트를 사용하는 예제를 찾아봤는데 없더군요.. 그래서 정리해서 올려봅니다.

2013년 10월 27일 일요일

[Nginx] PHP-FPM 실행을 했는데 500 internal error 가 나온다


삼바서버 및 PHP 등 을 통해 수십시간 삽질을 한 후...

이제 에러나면 로그 보는게 제일 빠르다는 걸 알게 되었습니다.

Nginx를 실행하고 PHP-FPM Reverse Proxy 연결하신 경우라면

PHP-FPM 로그가 아니라 Nginx 로그를 보시면 됩니다.

cat /var/log/nginx/error.log

Nginx 로그입니다.

뭘까요


Stack trace:
#0 /usr/share/nginx/html/sl/php/Predis/Connection/StreamConnection.php(94): Predis\Connection\AbstractConnection->onConnectionError('Connection refu...', 111)
#1 /usr/share/nginx/html/sl/php/Predis/Connection/StreamConnection.php(70): Predis\Connection\StreamConnection->tcpStreamInitializer(Object(Predis\Connection\ConnectionParameters))
#2 /usr/share/nginx/html/sl/php/Predis/Connection/AbstractConnection.php(95): Predis\Connection\StreamConnection->createResource()

간단하죠? Predis 에서 Redis를 이용하려 했는데 안된겁니다.

왜냐고요?

꺼져있으니까 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

/usr/local/bin/redis-server&

실행하니 잘되네요.

ㅠㅠ 내 시간들..

[CentOS] 삼바(SAMBA) 서비스가 실행이 안된다.

보안설정 50개 정도를 하고나서 갑자기 삼바가 실행이 안되었습니다.

온갖 삽질 다해보고 보안설정 이것 저것 풀어도 보고.. 삭제하고 설치하기만 몇번 했는지.

/usr/local/samba/bin/testparm /etc/samba/smb.conf 를 해봐도 이상 없고

다 지운 후 직접 컴파일도 해봤습니다.
(교훈 : 왠만하면 직접 설치는 하지말자...
지우기도 까다롭고 이 파일 저 파일 종속성도 높다)

yum -y install python-devel
mkdir /usr/local/samba
wget http://us1.samba.org/samba/ftp/samba-latest.tar.gz
tar xvfz samba-latest.tar.gz

#버젼으로 이동
cd samba-4.1.0/
./configure –prefix=/usr/local/samba –with-automount –with-pam_smbpass –with-syslog –with-utmp
make;make install
chkconfig --add smbd
cd /usr/local/samba/sbin/
cp * /sbin/

vi /usr/local/samba/etc/smb.conf
열어서 /usr/local/samba/lib 적어줌

링크도 만들어줌
# ln -s /usr/local/samba/lib/libtalloc.so.1 libtalloc.so.1
# ln -s /usr/local/samba/lib/libtdb.so.1 libtdb.so.1
# ln -s /usr/local/samba/lib/libwbclient.so.0 libwbclient.so.0


테스트 팜 실행해서 오류없나 살펴봄
/usr/local/samba/bin/testparm 

cd ..
cd bin
./smbpasswd -a root
service smbd start

그래도 안됨.

그러고보니 var/log/samba 에 로그가 쌓이죠.

cat log.smbd

로그내용
[2013/10/27 02:01:47.638162,  0] auth/auth_util.c:707(get_guest_info3)
  SamInfo3_for_guest: Unable to locate guest account [nobody]!
[2013/10/27 02:01:47.638307,  0] smbd/server.c:1205(main)
  ERROR: failed to setup guest info.

에헤헤헤헤헤헤헤헤헤헤헤헤

보안 설정 한다고 노바디 계정을 지워서 그랬어 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

vi /etc/passwd

ㅋㅋㅋㅋㅋ

2013년 10월 26일 토요일

[JAVA] Socket.io blocking(동기) Callback 방법

Hello SyntaxHighlighter

Socket.io

자바에서 Socket.io를 쓰기로 했고 저는 내부 통신 전부를 추상화된 모듈 형식으로 만들어야 했습니다.

자바는 기본적으로 Blocking으로 실행되지만, 서버인 Node.js는 Non-Blocking 입니다.

문제는 서버측에서 Emit해 주는 것을 자바에서도 비동기로 받는다는 것이었습니다.

자바스크립트에서는 비동기로 하고 싶으면 Callback을 이용해 쉽게 할 수 있는데

자바에서는 on이라는 함수가 비동기적으로 이벤트가 일어날 때 마다 받게 되어있더군요.

그래서 콜백을 직접 구현하기로 했습니다.

동작방식은 다음과 같습니다.
1.전역변수를 Null로 잡고, 자바측에서 Emit한 후 이 전역변수가 Null이 아닐때 까지 While문으로 체크합니다.
2.서버측에서 값이 전달되어 전역변수값이 바뀌면, 자바측에서 값을 받아오고 전역변수 값을 다시 Null로 만듭니다.
실제 서버에서 전달된 값이 Null일때만 조심하면 됩니다.
// 사용 : https://github.com/Gottox/socket.io-java-client
// Class명 : MyModule

static Object Emit_value=null; // 사용될 전역변수입니다.

//////////////////////////////////////////////////////////////////////////
...
public void on(String event, IOAcknowledge ack, Object... args) { 
// 이렇게 Thread 내부 on에서 event에 대한 arg를 받으면
// 그 값을 바꿉니다. 주의 할 점은 Emit_value 자체가 null 일 때 입니다.

                   Emit_value=null;
                   if(args[0]!=null){
                    Emit_value=args[0];
                   }
                   else Emit_value=-1;
               }
...
///////////////////////////////////////////////////////////////////////////

private synchronized Object callback(){
  while(Emit_value==null){try {
   Thread.sleep(20);
// 이 부분을 수정하여 timeout 이라는 변수를 하나 선언 한 후
// 다음과 같이 해주면 과도한 Block을 막을 수 있습니다. 
//Thread.sleep(timeout);
//timeout*=2; 
//if(timeout>5000) return -1;
  } catch (InterruptedException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }}
   Object temp_value=Emit_value;
   Emit_value=null;
   return temp_value;
 }

private static String md5(String input){ //MD5 암호화
     String result = input;
     if(input != null) {
         MessageDigest md;
   try {
    md = MessageDigest.getInstance("MD5");
         md.update(input.getBytes());
         BigInteger hash = new BigInteger(1, md.digest());
         result = hash.toString(16);
         while(result.length() < 32) {
             result = "0"+result;
         }
   } catch (NoSuchAlgorithmException e) {
    e.printStackTrace();
   }
     }
     return result;
 }

public int reqLogin(String ID, String PW){
  try {
   socket.emit("c/reqLogin",new JSONObject().put("PW",PW).put("ID", ID));
   return (int) callback(); // Blocking 합니다.
  } catch (JSONException e) {
   e.printStackTrace();
  }
  return 0;
 }

//사용법 : MyModule 객체를 생성한 후, 사용하면 됩니다.
MyModule.reqLogin("bykim", md5("user_password"))


Callback 함수 While문 내부의 sleep(0)은 중요합니다.

저게 없으면 CPU 사용률이 100%가 되어 서버측에서 메시지는 보냈는데

정작 자바에서는 아무것도 못받고 소켓이 끊길 때 까지 기다리게 되는 수가 있습니다.

While문 돌 때 마다 System.out.println(); 를 찍어주면 느려지기(?) 때문에 제대로 돌아가지만
그것보단 Sleep() 이 낫겠죠?

참고 : Thread의 Context Switch에 걸리는 시간은 최소 10msec정도라고 하며
60msec정도가 한계인 irix,solaris 도 있습니다.
그리고 Sleep(0)의 경우, 다른 쓰레드가 준비 되었다고 해서 자신의 CPU의 선점을 넘겨주지 않으며,
우선순위가 서로 같아야 CPU가 넘어갑니다.

2013년 10월 11일 금요일

[Java] Ant Build Failed


ANT 빌드 실패시


Socket.io 을 자바에서 사용할 수 있는 https://github.com/Gottox/socket.io-java-client

을 사용할 일이 있어서 Ant로 빌드하려 했는데 다음과 같은 에러가 떴습니다.




보면 현재 자바 홈 디렉토리가 C:\Program Files\Java\jre7 으로 되어있다고 나오는데요,

여기에는 필요한 tools.jar파일이 없기 때문에 빌드가 되지 않았습니다.

tools.jar파일이 저는 jdk1.7.0_11 폴더의 bin 에 있었기 때문에

환경변수의 PATH에 추가했는데도 이상하게 안되더군요.

그래서 C:\Windows\System32\apache-ant-1.9.2\bin\ant.bat을

직접 수정하기로 했습니다.

윈도우 cmd쉘은 정말 쉽게 잘 만들어진 쉘 중 하나죠.. ant.bat 보면서

느꼈습니다. 역시..짱짱맨

ant.bat 을 열면 글자들이 주루룩 나오는데요

@echo off 는 명령어 출력을 끄는거고

REM은 주석을 말합니다.



주석이 끝나는 지점에

set PATH=C:\Program Files\Java\jdk1.7.0_11\bin

를 추가해주니 잘 되는군요.

물론 이 PATH는 ant.bat 에만 적용되는 겁니다.





Build Successful !!



참고 :

Jar 외부 파일 임포트하는 방법


프로젝트 오른쪽 -> Properties

Java Build Path -> Libraries -> Add External JARs...

해당 Jar 파일 열기


-추가후에 ClassNotFound 에러가 뜰 때


라이브러리 오른쪽 -> Import

File System -> Browse 에서 JAR 파일 경로 및 Jar파일 선택



2013년 10월 3일 목요일

[UCrew] 발대식과 Ativ Tab 3 사용기


최근에 Microsoft UCrew 에 지원했는데 운 좋게 합격해서 발대식에 다녀왔습니다.

UCrew Kit을 받기위해 줄을 선 UCrew 멤버들









































조금 기다린 끝에

UCrew Kit 을 받을 수 있었습니다~!

UCrew Kit : 가방, 목걸이 명함, 명함, 블루투스 마우스, ATIV Tab 3, 케이블, 야구점퍼


























추첨을 통해 ATIV Book 9 lite  / ATIV Tab 3 중에 무엇을 받을지 결정 되었는데요.

저는 노트북이 이미 있었기 때문에 ATIV Tab 3를 받게 되어 너무 좋았습니다.


왼쪽부터 ATIV Tab 3 와 블루투스 키보드



























빛나는 ATIV Tab 3 그리고 블루투스 키보드 ~

Tab 3를 블루투스 키보드에 끼웠다 뺐다 하면서 사용 가능합니다.

다만 단점이 있다면 360도 회전이 안된다는것과 자석 받침대로 가능한 각도가

사진처럼 단 하나라는 것입니다.

다른 악세서리가 빨리 나오길 바랍니다ㅠㅠ



ATIV Tab 3의 가장 큰 장점은 OS로 윈도우 8이 설치되어 있다는 건데요

탭에 Visual Studio를 깔아서 생활 코딩을 합시다!

























보시다시피 기존의 윈도우에서 돌아가는 모든 프로그램이 다 돌아갑니다. 그리고

마이크로소프트 계정이 연동되어 보시다시피 기존의 제가 사용하던 씽크패드 노트북의

Node.js 바탕화면과 탭의 바탕화면이 별도 설정 없이 동기화 되어 있습니다.





ATIV Tab 3는 윈 8입니다. 두려울게 없죠!

아무생각없이 산 64G 짜리 SDXC 카드도

바로 바로 인식되고 사용가능합니다.

SDXC 64G UHS-I (exFAT , Class10) SanDisk 











































제가 솔직하게 평가해 본 ATIV Tab 3

단점?
1. 아톰....조금 느리다.(코딩하고 빌드할 때랑 프로그램 로딩할 때 )
2. 딸려있는 블루투스 키보드가 각도 조절이 하나밖에 안되고 360도 회전이 안된다.

장점?
1. 윈8이 설치되어있다. ( 오피스, 코딩, 서버 등 모든 것을 할 수 있다. )
2. 매우 가볍다. (500g대)
3. 배터리가 오래간다.
4. 터치가 가능하다.



2013년 10월 2일 수요일

Power Mockup 라이센스 취득!


최근에 Powermockup에 대해 포스팅을 했었는데요

Wulfsoft 에 감동적인(?) 메일을 보냈더니

답장이 왔습니다.

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

Hello,

Thank you for the blog post. Here's your free single-user license code in return:

...중략


If you notice any problems with PowerMockup, please let me know.


Regards

Andreas Wulf

-- 
PowerMockup
http://www.powermockup.com/

Provided by Wulfsoft
Hamburger Str. 2 - 48155 Münster - Germany
VAT ID: DE225372217

Follow us on Twitter: http://twitter.com/wulfsoft

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ

모든 기능을 사용할 수 있는 라이센스를 주셨네요 !

그래서 직접 한번 사용해 보았는데요.



이 프로그램의 장점은 기존의 라이브러리를 가져와 조금만 수정해서

나만의 Library를 만들 수 있다는 점입니다.



이렇게 원하는 폼을 Drag and Drop을 한 다음,





바로 Add to Stencil Library 버튼을 통해 라이브러리에 추가를 할 수 있습니다.


NewLogin이란 이름으로 추가를 해보았는데요.

검색도 가능하니까

추후 라이브러리를 엄청나게 많이 추가한 경우를 고려해서

알아보기 쉬운 이름으로 짓는게 좋겠죠.




이렇게 목업이 금방 완성되고

만들었던 라이브러리는 다른 PPT 파일에서도 곧바로 재사용이 가능합니다.



그럼 다음에 뵙도록 하겠습니다.



2013년 9월 29일 일요일

Power Mockup!




개발하기전에 보통 목업을 제작하는 경우가 많은데요.

목업을 종이에 직접 그리기도 하고

프로그램을 쓰기도 합니다.

저도 몇몇 목업 프로그램을 써봤는데요,

그런 프로그램들은 쓰기가 어려운(노가다성이 좀 있는) 단점이 있었습니다.

그러던 중 알게 된 좋은 프로그램! Power Mockup!


(약간 '객체지향'스럽다고 해야하나..)

한국사람이라면 누구나 PPT를 써봤을 겁니다.

PowerMockup은 PPT를 기반으로 하는 하나의 라이브러리로,

PPT를 써본 사람이라면 누구나 쉽게 쓸 수 있어요.



에 가면 무료버젼을 다운 받을 수 있습니다.

(그런데 무료버젼은 제한이 많이 있네요 ㅠ)


학생이면 40%할인을 해준다고 하니 잘 알아보시면 좋을 것 같습니다.

그럼 이만!




2013년 9월 15일 일요일

[AWS] 아마존 웹 서비스 요금(Free-tier)

-현재 가격에 관한것들(Free tier 기준)-

Free tier에서 750 시간 을 제공합니다.
즉 t1.micro 한개를 한달 내내 켜놓아도 요금이 부과되지 않아요.

인스턴스 시간은 인스턴스가 “실행” 상태일 때 계산됩니다.
즉 "Stop" 나 "Terminate" 일 때는 청구되지 않습니다.
두개의 차이? 말 그대로 Stop은 멈추는거고 Terminate는 인스턴스를 완전히 삭제하는 겁니다.

서버 한개당 하나의 Elastic IP를 할당 하는 것은 무료입니다.

서버 한개당 Elastic IP를 여러개 할당 하는 것은 유료입니다.

Elastic IP를 할당받고, 인스턴스에 할당 하지 않으면 유료입니다.
(첫 한시간동안은 무료, 이후 시간당 0.005달러)


계정을 클릭하고 Account Activity(계정 활동)을 누르면
지금까지의 사용 요금을 확인 할 수 있습니다.
바로 바로 갱신되는것은 아니고 시간이 지나야 갱신되는 듯 합니다.

"이 기간에는 청구할 내역이 없습니다." 가 아니고






이게 보이면 다음달에 여기에 적힌 금액 이상 요금이 청구된다는 뜻 입니다.

저도 ....테스트하다가 요금이...나와버렸네요.



★Free tier에서 예상치 못한 요금이 부과되는 경우를 생각해 보면...

Azure 와 달리 요금이 초과될 경우 제한 할 수 있는 정책이 아직 없습니다.
따라서 많은 I/O 또는 요청수로 예상치 못한 요금이 부과될 수 있습니다.

인스턴스 하나는 한달 내내 켜놓아도 750시간이 넘지 않아 문제가 되지 않지만,
인스턴스 여러개를 한달 내내 켜놓으면 750시간을 훌쩍 넘으므로 주의해야 합니다.

Free tier에서 인스턴스 하나일 경우는 계속 실행해 놓아도 상관없지만
여러개의 인스턴스일 경우 인스턴스를 "Stop" 혹은 "Terminate" 해야 합니다.

현재 (2013년 9월) 기준으로 Free tier 
무료 OS는 Linux/Unix/RHEL/Microsoft Windows Server 입니다. 
이외의 OS 즉 SuSe Linux Enterprise Server등은 유료입니다.

Elastic IP를 인스턴스에 할당한 상태에서, 
테스트를 끝내고 해당 인스턴스를 Terminate 시켜도 Elastic IP는 할당이 해제된 채로 
계속 남아 있습니다. 이를 반드시 Release 해주어야 요금이 부과되지 않습니다.



[AWS] 아마존 웹 서비스 시작하기


주의 : 그림없고 말로만 설명합니다.

-가입-
가입 할 때
폰번호 정확히 입력하세요.
국가코드 선택 후 010부터 다 적어도 전화 잘 옵니다.

-EC2 Instance 만들기-
계정을 눌러 AWS Management Console 메뉴 들어간 후
오른쪽 상단 Help 바로 옆을 클릭해서 지역을 정합니다.

됴코는 요새 느린 문제가 좀 있다고 알려져서 저는 싱가폴로 했습니다.

EC2 아이콘을 클릭하고
Launch Instance 라는 파란버튼 있는데 클릭하면 EC2 인스턴스를 만들기 시작합니다.

클래식 위자드 선택하고 진행하면 되는데
Continue 하다가 Key , Value 라고 나오는데 그냥 아무거나 입력하면 됩니다.
할거 없으신 분은 각각 Name , Webserver 를 입력하세요

Create & Download your Key Pair 를 클릭해서
다운 받은 pem 파일을 잘 보관하도록 합니다.

Create a new Security Group 에서는 방화벽 마냥 각종 포트를 열어주면 됩니다.
기본은 화이트 리스트방식입니다. 즉 등록한 포트만 열리고 나머지는 다 막힙니다.
22, 80, 3306, 6379, 9000 등 본인이 자주 쓰는 포트를 여세요.
핑 때릴려면 ICMP도 여시구요.

pem파일을 puttygen으로 가져온다음
암호화할 String을 입력합니다(이건 나중에 로그인 할 때 암호로 사용됩니다.)
save private key 클릭 하면 ppk파일로 저장됩니다.

PuTTY - Connection - SSH - Auth 에서 Browase 눌러서 ppk 파일 가져온 후
왼쪽 Session에서 해당 IP치고 Open 눌러서 접속합니다.

root로 로그인하고 아까 암호화 스트링을 입력하면 로그인 되는데
전 우분투 12. 버젼이므로 아이디 ubuntu로 로그인 하라고 하네요.
다시 로그인 하면 됩니다.

Elastic IP는 공인 아이피를 고정시키는 것인데,
할당 받으셨다면 반드시 Associate Address 를 눌러 인스턴스와 연결해 주세요.


다음 포스팅은 AWS Free tier의 가격에 관한 것입니다.





2013년 9월 9일 월요일

[UCrew] 나에게도 태블릿이 있다면...



오늘은 기술적인 내용의 포스팅이 아닌

특별히 UCrew 를 지원하기 위한 포스팅을 하려고 합니다.



크고 무겁고, 가지고 다니기 부담스러운 노트북과

작지만 화면이 너무 작은,

원래 용도는 사실 전화기였다는 스마트폰.





단언컨대, 
태블릿은 가장 완벽한 
스마트기기입니다.






MISSION 1 - 태블릿이 있다면???



1. 공부


저는 소프트웨어를 전공하는 학생으로 현재 프로그램을 개발하고 있는데요

이 서비스는 태블릿을 타겟으로 하는데, 거기엔 특별한 이유가 있습니다.

태블릿은 휴대성이 좋기 때문에 교수님께서 강의 하실 때

태블릿에서 이 프로그램을 실행한 후 들고 움직이면서

필기와 강의를 동시에 할 수 있게 됩니다.

개발중인 프로그램 소개 동영상부터 보시고 가실게요.








학교 교수님의 피드백도 있어요~








저는 이렇게 태블릿을 타겟으로 하는 서비스를 개발중이기 때문에

태블릿이 필요했으며, 태블릿을 타겟으로 하는 프로그램을 만든다는 것 자체가

저에게 있어서는 가장 큰 전공 공부가 되지 않을까 생각합니다.

이번에 태블릿을 쓸 기회가 저에게도 생긴다면

참 좋을 것 같습니다.



2.공모전


지난주에 이 서비스를 통해 공모전에서 대상을 수상하여 코엑스에서

교육부 장관상을 받게 되었는데요









태블릿이 있었다면 하는 아쉬움이..












이때 태블릿이 있었다면 얼마나 좋았을까 생각해 봤습니다.

이곳에 각 정부 부처의 높은 분들과 많은 기업인들이 오셨는데

발표를 할 때 이 아이디어의 타겟인 태블릿을 직접 들고 발표를 했다면

더욱 호응도 좋고 많은 분들에게 어필 할 수도 있고

서비스에 대한 이해도도 높아지지 않았을까 하는 아쉬움이 있습니다.

다른 공모전들도 신청한 것들이 있는데요,

꼭 태블릿을 받아서 시연 해 보고 싶습니다.




3. 수업


과 특성상 거의 모든 과목이 PPT로 진행됩니다.

그래서 저는 마이크로소프트사의 오피스 2013 정품을 구매하였지만

막상 노트북을 학교에 가지고 다니면서 보기엔 너무 부담스러워서

결국 A4로 프린팅하는데요.









파일 집 하나로는 부족하여 두개 샀습니다.











보시다시피


한 학기동안 뽑아야 하는 A4용지는 가히 상상을 초월합니다.

따라서 태블릿이 있다면 종이를 아껴 자연을 지킬 수도 있고

귀찮게 프린트를 하지 않아도 됩니다.




MISSION 2 - 태블릿을 받아 할 수 있는 활동???




아까 공부 부분에서 말씀드렸는데, 정확하게는 제가 듣고있는 

모든 과목이 PPT로 진행됩니다.

따라서 제가 태블릿으로 필기를 하며 수업을 참여한다면 많이 알려질 것이라고 생각합니다.

저도 다른 친구가 갖고 있는 스마트 기기에 굉장히 많은 관심을 갖고 있기 때문이죠.

또한 제가 만들고 있는 서비스를 학과 친구들과 교수님 그리고

공모전에서도 많은 기업분들, 각 부처 관계자분들께

태블릿을 가지고 시연한다면

입소문을 타게되고 그만큼 많은 파급력 즉 

굉장히 큰 간접 광고가 될 것이라고 생각합니다.








2013년 9월 1일 일요일

[CentOS 6.x] 각종 서버 설치하기

**기본

yum -y update
yum -y groupinstall "Development Tools"

**NginX


#wget http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
#rpm -ivh nginx-release-centos-6-0.el6.ngx.noarch.rpm
#yum install nginx
#service nginx start

Nginx는 /etc/nginx/nginx.conf 요 파일이 /etc/nginx/conf.d/default.conf 파일을 인클루드 하고 있습니다.
두 파일은 혹시나 모르니 cp 명령어로 백업 해놓으시고

worker_processes 부분에는 하드웨어 코어 개수만큼 숫자를 적어 줍니다.

#vi /etc/nginx/nginx.conf

user              nginx;
worker_processes  4;
error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  30;
    server {
        listen       80;
        server_name  _;
        location / {
            root   /usr/share/nginx/html;
            index  index.php index.html index.htm;
        }
        error_page  404              /404.html;
        location = /404.html {
            root   /usr/share/nginx/html;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
        location ~* \.php$ {
            root           /usr/share/nginx/html;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            fastcgi_param  PATH_INFO $fastcgi_script_name;
            include        fastcgi_params;
        }
    }
    include /etc/nginx/conf.d/*.conf;
}



#vi /etc/nginx/conf.d/default.conf
server {
    listen       80;
    server_name  _;
    location / {
        root   /usr/share/nginx/html;
        index  index.php index.html index.htm;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
    location ~ \.php$ {
        root           /usr/share/nginx/html;
        try_files $uri =404;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

service nginx restart 

**Apache 

전 리버스 프록시를 이용해 PHP 요청은 아파치가 처리하도록 했기 때문에 설치했는데
Nginx만 쓰면 굳이 설치할 필요는 없습니다.

#yum -y install httpd

구동하는 포트넘버가 80으로 되어있는데 Nginx랑 겹치니까 다른걸로 바꿔주면 됩니다.

service httpd restart


**Mysql

#yum -y install mysql mysql-server
#mysqladmin -u root password 비밀번호

패스워드 설정해줍니다.

참고로 자료 백업 및 복구할 때(전체)
백업 : mysqldump -u root -p --all-databases > 파일명.sql
복구 : mysql -u root -p < 파일명.sql

특정 테이블만 하는거면 저기 -p 뒤에 테이블 명 쓰면 됩니다.

그리고 처음에 원격접속 허용하려면 service mysqld start 로 시작한 후

 mysql -u root -p 로 로그인하고,

use mysql; 한 다음 host 컬럼을 %로 바꾸면 원격으로도 접속이 가능합니다.

UPDATE `mysql`.`user`
SET `Host`='%'
WHERE `Host`='localhost' AND `User`='root';

service mysqld restart


**REDIS

#cd /usr/local/src
#wget http://redis.googlecode.com/files/redis-2.6.14.tar.gz
#tar xvfz redis-2.6.14.tar.gz
#cd redis-2.6.14
#make -j4 && make install  -j4

redis-server&


**Node

#yum -y update
#yum -y groupinstall "Development Tools"

#wget http://nodejs.org/dist/v0.10.17/node-v0.10.17.tar.gz
#tar zxf node-v0.10.17.tar.gz
#cd node-v0.10.17
#./configure
#make
#make install

모듈들 설치는

#npm -g install 설치할모듈

실행은

#node 실행할파일

**PHP

#yum -y install
php php-mysql php-gd php-imap php-ldap php-odbc php-pear php-xml php-xmlrpc php-fpm

php.ini 파일 수정해서
short_open_tag = On 해줍니다.

우린 PHP-FPM 사용할거니까

service php-fpm restart

*Predis (PHP 와 Redis 연결)

#pear channel-discover pear.nrk.io
#pear install nrk/Predis



**Samba

이건 다 아시겠지만 윈도우처럼 파일 공유하는 서비스입니다.

yum install samba
smbpasswd -a 사용자명      // 패스워드설정
service smb start

아무리 권한을 추가해도 권한 문제가 생긴다면

vi /etc/selinux/config
Set
//SELINUX=enforcing 이걸
SELINUX=disabled //요렇게 고치면 됩니다.

vi /etc/samba/smb.conf 
를 열어서 여러가지 수정을 해줍니다.

server string = Server name
hosts allow = lo eth0 11.22.33.   // 11.22.33 대역 접속 허용 설정
[share]
    comment = hi
    path = /usr/share/nginx
    writable = yes
    valid user = by

이것은 by라는 사용자에게 /usr/share/nginx 디렉토리를 공유할 수 있도록 한 것입니다.

권한 문제가 있다면 user의 디렉토리와 /etc/passwd 의 디렉토리를 같게 해주고

chmod 755 등으로 해당 디렉토리에 권한을 줘봅니다.

/sbin/service smb restart 


***6시간짜리 팁

이렇게 하고 Nginx와 PHP-FPM 이 연동이 안됬습니다.

php -v , php-fpm -v 로 버젼을 확인해보니 5.3

혹시나해서 5.5로 업데이트 해보았습니다.

#rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
#rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm

#yum --enablerepo=remi,remi-test install httpd mysql mysql-server php php-common

#yum --enablerepo=remi,remi-test install php-mysql php-pgsql php-pecl-mongo php-sqlite php-pecl-memcache php-pecl-memcached php-gd php-mbstring php-mcrypt php-xml php-pecl-apc php-cli php-pear php-pdo

그래도 안되서 권한부터 시작해서 온갖 걸 다 해봤습니다.

/var/log/nginx/error.log 파일을 열어보면 에러로그들이 있죠

계속해서 살펴보던 중 시스템 시각과 현재 시각이 맞지 않다는 것을 알게 되었고

시간이나 맞춰야지 하고

rdate -s time.bora.net

...

갑자기 잘됨

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ???????????????????



[CentOS 6.x] 설치


CentOS 기본


지금 부터 다음 몇 회 까지는 기본적인 것을 다루려고 합니다.

CentOS 설치부터 시작해 보겠습니다. (가상머신 위에 띄우는거 아니고 로컬에 설치하는 겁니다.)

일단 저는 제대로 된 이미지 파일 찾기에서 부터 힘들었습니다.

어렵게 구해서 설치하면 설치가 안되고...(너무 오래전이라 지금은 어디서 받았는지 기억이 안나네요)

어쨌든 제대로 된 이미지 파일을 구하셨다면, iso2usb 같은 usb구워주는 프로그램을 다운받아서

USB를 부팅 USB로 만듭니다.

설치시 중요한건 부트로더 설치 위치입니다.

USB에 부트로더를 설치하면 나중에 USB를 제거했을 때 부팅이 되지 않습니다.

(물론 원인을 알고나면 쉽게 재설치 할 수 있지만 그동안 멘탈이 손상될 수 있습니다.)

저장장치 선택 에서 다 오른쪽으로 밀어버리고 기존의 하드디스크만 선택해서 파티션을 잡습니다.

Network 설정[고정아이피]

vi /etc/sysconfig/network-scripts/ifcfg-eth0

DEVICE=eth0              
BOOTPROTO=static          
HWADDR=맥주소인데 알아서 들어가 있음
IPADDR=원하는 아이피 주소
NETMASK=255.255.255.0
NETWORK=기본게이트웨이 보통 끝에 1
BROADCAST=끝에 255
ONBOOT=yes                    // 시스템 부팅시 해당 이더넷 활성화

service network restart 으로 재시작하면 끝

방화벽, 아이피설정 같은것은 setup 명령어를 통해 간편하게 할 수 있습니다.

다음은 CentOS 위에 각종 서버를 설치 해 보겠습니다.



2013년 8월 9일 금요일

[PHP] Session 파일로 관리하지 마세요.




Windows7+Apache에서 CentOS+Nginx로 갈아탔는데도 불구하고

PHP를 사용할 때 갑자기 매우 느려지는 현상이 있었습니다.

PHP-FPM 도 사용해보고 , Nginx가 Dynamic한 파일 처리에 적합하지 않다고 하여

Reverse Proxy로 Apahce를 연결해 보았지만 여전히 느린 현상이 없어지지 않았습니다.

그래서 지인과 이야기를 하던 도중 중대한 사실을 하나 알게되었습니다.

세션을 파일로 관리했던 것!

그러고보니 아무리 찾아봐도 느릴 이유가 없는데..

시스템 IO는 아직도 대부분의 시스템에서 가장 중요한 작업이고, 

강력한 Priority를 갖기 때문일 것이라 생각 했습니다.

그래서 그때부터 Redis와 PHP를 연결한 후 Redis-server 에서 세션을 다루어 보았습니다.

PHP에서 Redis를 사용하기 위해서는 , PREDIS 를 먼저 설치 해야합니다.

pear 가 어디에 있는지 find 명령어로 먼저 찾아 내고,

/pear의 위치/pear channel-discover pear.nrk.io 이후

/pear의 위치/pear install nrk/Predis 로 설치하면 끝.

저는 이 파일을 아파치에 옮겨 주었구요

php.ini 를 find해서 

extension=redis.so 

하면 php에서 redis를 사용이 가능합니다.

사용법은 
require('./redis-session.php');
RedisSession::start();

이고, 다음부터는 일반 session 사용하듯 사용하시면 됩니다.
redis-session.php 파일은 아래와 같습니다.



redis-session.php


if(!class_exists('\Predis\Client')){
  chdir(dirname(__FILE__)); 
  require_once('./Predis/Autoloader.php');
/*
./Predis/Autoloader.php 이 부분은 
redis-session.php 위치로 부터의 상대 경로입니다.
*/
  Predis\Autoloader::register();
}

function json_decode_array($d){
  return json_decode($d, true);
}

function redis_session_id_mutator($id){
  return $id;
}

class RedisSession{
  private $serializer;
  private $unserializer;
  private $unpackItems;
  private $id_mutator;

  static function start($redis_conf = array(), $unpackItems = array()){
    if(!defined('REDIS_SESSION_PREFIX'))
      define('REDIS_SESSION_PREFIX', 'session:php:');
    if(!defined('REDIS_SESSION_SERIALIZER'))
      define('REDIS_SESSION_SERIALIZER', 'json_encode');
    if(!defined('REDIS_SESSION_UNSERIALIZER'))
      define('REDIS_SESSION_UNSERIALIZER', 'json_decode_array');
    if(!defined('REDIS_SESSION_ID_MUTATOR'))
      define('REDIS_SESSION_ID_MUTATOR', 'redis_session_id_mutator');
    $obj = new self($redis_conf, $unpackItems);
    session_set_save_handler(
      array($obj, "open"),
      array($obj, "close"),
      array($obj, "read"),
      array($obj, "write"),
      array($obj, "destroy"),
      array($obj, "gc"));
    session_start(); 
    return $obj;
  }

  function __construct($redis_conf, $unpackItems){
    $this->serializer = function_exists(REDIS_SESSION_SERIALIZER) ? REDIS_SESSION_SERIALIZER : 'json_encode';
    $this->unserializer = function_exists(REDIS_SESSION_UNSERIALIZER) ? REDIS_SESSION_UNSERIALIZER : 'json_decode_array';
    $this->id_mutator = function_exists(REDIS_SESSION_ID_MUTATOR) ? REDIS_SESSION_ID_MUTATOR : 'redis_session_id_mutator';
    $this->unpackItems = $unpackItems;

    $this->redis = new \Predis\Client($redis_conf);
  }

  function serializer(){
    return call_user_func_array($this->serializer, func_get_args());
  }

  function unserializer(){
    return call_user_func_array($this->unserializer, func_get_args());
  }

  function id_mutator(){
    return call_user_func_array($this->id_mutator, func_get_args());
  }

  function read($id) {
    $d = $this->unserializer($this->redis->get(REDIS_SESSION_PREFIX . $this->id_mutator($id)));
    // Revive $_SESSION from our array
    $_SESSION = $d;
  }


  function write($id, $data) {
    
    $data = $_SESSION;
    $ttl = ini_get("session.gc_maxlifetime");
    $unpackItems = $this->unpackItems;
    $serializer = $this->serializer;
    $id_mutator = $this->id_mutator;

    $this->redis->pipeline(function ($r) use (&$id, &$data, &$ttl, &$unpackItems, &$serializer, &$id_mutator) {
      $r->setex(REDIS_SESSION_PREFIX . $id_mutator($id), $ttl, $serializer($data));

    });
  }


  function destroy($id) {
    $this->redis->del(REDIS_SESSION_PREFIX . $this->id_mutator($id));

  }

  function open($path, $name) {}
    function close() {}
    function gc($age) {}
}

register_shutdown_function('session_write_close');

?>


빨라지는 마법을 경험했습니다.

그래도... 앞으로는 PHP사용을 자제해야 겠습니다.


2013년 7월 12일 금요일

[Nginx] apache를 nginx로 window를 CentOS 로 !




최근에 Window 7, Apache ,Node , Mysql , Redis 로 구성된 서버에

Stress Test를 하게되었습니다.

첫 페이지에서 PHP로 MySQL 로 로그인 하고,

여러 과정을 지나 Node에서 서비스를 해주는 것이었습니다.

사람들 60여명 정도에게 테스트를 했는데

정말 생각지도 못하게 로그인 페이지 부터 막혔습니다.

Win7 + Apache가 그 정도일 줄이야...


그날 바로 서버를 완전히 백업하고

Centos 를 설치한 후

Apahce를 Nginx 로 바꾸고 다른 것들도 올려 보았습니다.

그 사이에 있었던 (지금 생각해보면 어처구니 없는, 하나당 몇시간동안 고생했던)

잊지 말아야 할 것들을 적어 봅니다.

####################################################
# USB로 Centos 설치할 때는 부트로더가 USB에 설치되지 않도록 주의한다.
(이 상황에서 재부팅시 부트로더 뻥난것처럼 검은화면에 커서만 깜빡거립니다.)
이 경우, 재설치 하지말고 설치한 USB로 다시 부트로더를 잡아주면 됩니다.

# PHP Session을 이용하려면 최신버젼을 설치해야 한다.

# Nginx 에서 해야할 설정들
PHP , 특히 Websocket을 이용하기 위한 설정들

# 방화벽 설정을 잘 해준다. 열어야 할 포트를 열어준다.

# Mysql 백업했던거 복원할 때 < ,  > 절대 헷갈리지 말것.

# 현재 디렉토리부터 하위디렉토리 까지 문자열 /SL/ 을 /sl/로 바꾸어야 할 때
# 단, 신중하게 사용하여야 합니다. 반드시 바꿀 폴더에 들어가서 작업 하세요.
# 잘못사용했다가 서버가 날아갔습니다.
find ./ -type f -exec sed -i 's#/SL/#/sl/#' {} \;


#삼바 폴더 권한 설정을 해도 권한 문제로 안될 때
vi /etc/selinux/config
Set
//SELINUX=enforcing
SELINUX=disabled

#whereis , ps -e | grep "찾을 프로세스" 로 잘 설치되었고 실행되는지 확인한다.

# 더 있었던것 같은데 기억이 안나는 관계로...
####################################################



CentOS 와 Nginx 로 바꾼 다음 , 신세계가 펼쳐졌습니다. 정말 확실히 느껴지는 속도차이!


여담으로..CentOS에 대해서

"누가 센트 오에스라고 읽냐 센토스라고 읽어야지" 라는 분도 계시고

Cent + OS(Operating System) 이기 때문에

센트 오에스 라고 읽어야 된다고 하는 분도 계십니다.

뭐가 맞는 건지는 잘 모르겠지만 전 그냥 센트오에스 라고 읽으렵니다 ㅋㅋ


2013년 7월 4일 목요일

[Node.js] 객체 정렬, 방 찾기, Usage

Hello SyntaxHighlighter

Tools.


var Tools={

//소켓 전체를 받아서 방을 돌려준다.
  getRoomBySocket:function(obj){
   var sockId=io.sockets.manager.roomClients[obj.id];
   var r= null;
   for(var k in sockId){
    if(!sockId.hasOwnProperty(k))
     continue
    r=k;
   }
   if(r!=null && typeof r !="undefined") r=r.split("/")[1];
   else r=null;
   console.log("[ function ] getRoomBySocket : ",r);
   return r;
  },

//객체를 소팅한다.
  sorting: function(f, r, p){
   var key = function (x) {return p ? p(x[f]) : x[f]};
      return function (a,b) {
          var A = key(a), B = key(b);
          return ((A < B) ? -1 : (A > B) ? +1 : 0) * [-1,1][+!!r];
      }
  },

//CPU , MEMORY 정보를 알려준다.
updateServerState:function(callback){
   var freemem=os.freemem();
   var totalmem=os.totalmem();
   var memUse=Math.round((totalmem-freemem)/totalmem*100);
   var r=null;
   os_util.cpuUsage(function(v){
    cpuUse=Math.round(v*100);
       r={cpu : cpuUse , mem : memUse};
       callback(r);
   });
  }
};


/*
 *사용법 
 * 소켓 가지고 그 소켓이 join된 방을 찾고 싶을 때. */ 
 Tools.getRoomBySocket(socket);  

/*
* 객체 소팅 */
  var book=[ { "price" : "500",
               "name"  : "andromeda" },
             { "price" : "1500",
               "name"  : "thebook" }];
// '가격' 높은거에서 낮은거로 소팅
book.sort(Tools.sorting('price', true, parseInt)); 
// '책 제목'의 a-z까지 순서로 소팅
book.sort(Tools.sorting('name', false, function(a){
return a.toUpperCase()
})); 

/*
* Server의 CPU, MEM 사용량 조사를 동기적으로 받아서 처리하고 싶을 때.
*/
 Tools.updateServerState(function(r){
 console.log("cpu : ",r.cpu," mem : ",r.mem);
 })

다음에 Node.js 프로젝트를 할때 유용하게 쓰일 것 같아서 포스팅합니다. 마음껏 쓰셔도 좋습니다. 혹시 코드에 문제가 있으면 저한테도 알려주세요 ^^

[Node.js] Redis 의 데이터 관리(쿼리,삽입,삭제)

Hello SyntaxHighlighter

Redis 의 hset , hget , hdel

var redis = require('redis');
/*
*자동으로 JSON String화 되어 들어가고 나오게 할 방법이 있습니다.
*npm install redis-jsonify 로 다운받으시면 됩니다.
*
*사용법
*var jsonify=require('redis-jsonify');
*var client=jsonify(redis.createClient(포트,호스트,null));
*
*/
var client=redis.createClient(포트,호스트,null);
var tempobj={test: "hi", test2:"ho"}
client.hset('test','a',JSON.stringify(tempobj) ); 
client.hget('test','a',function(err,obj){
 obj=JSON.parse(obj);
 console.log(obj.test);
});
client.hdel("test","a"); 
//test 키 전체가 삭제되는 것이 아니라 test 의 a와 내용만 삭제합니다.

github.com/mranney/node_redis 이곳에서 함수를 찾아보곤 했었는데 이곳에 없는 것들이 많더라구요. hget, hdel이 있다는 사실은 나중에 실험을 통해 알게 되었습니다.

2013년 6월 25일 화요일

[Node.js] Redis 의 hmset

Hello SyntaxHighlighter

Redis 의 hmset 사용하기

var redis = require('redis');
var client=redis.createClient(포트,호스트,null);
/*
*    MySQL을 사용하다가 Redis를 사용해보니 신세계입니다.
*/
var room={
 key1:'value1', 
 key2:'value2'
 };
//JSON 형태로 집어넣음
client.hmset('test', room);

// 'test' 키의 값중 key2는 그대로 두고 key1만 value5로 바꾸는 방법
room={
 key1:'value5'
 };
client.hmset('test', room);
client.hgetall("test",function(err,obj){
        console.log(obj.key1);
        console.log(obj.key2);
});
//value5 와 value2 가 출력됩니다.



자세히 사용방법이 적혀있는 곳이 별로 없었습니다.

그래서 처음엔 room의 value몇개를 바꾸기 위해

get으로 몽땅 값들을 가져와서 몇개만 바꾸고 다시 몽땅 set으로 집어넣었는데

아무리 생각해도 비 효율적이라 요리조리 해봤는데 저렇게 하니까 되더라구요.

그래서 지금은 value 몇개를 바꾸기 위해 전체를 가져올 필요없이

바꿀 것만 hmset으로 집어넣고 있습니다.

메모리 기반 비관계형 데이터베이스... 정말 빠릅니다 속도 차이가 확실히 느껴져요.

2013년 6월 18일 화요일

[Node.js] DB Pool ( mysql-simple-pool )

Node.js 서버가 오래 켜놓으면 꺼질 때



처음에는 노드 서버가 켜질 때 Mysql DB서버에 Connection을 맺고

계속 쿼리를 날렸었는데 , 오랫동안 서버가 켜져 있다 보니까

Connection이 끊어지면서 서버가 다운되는 현상이 있었습니다.

그래서 사전에 일정량의 Connection 객체를 만들어 모아두고,

사용이 끝난 Connection 객체를 나중에 다시 재사용 가능하도록 하는 Connection Pool을 이용해

속도도 향상시키고 서버가 다운되지도 않도록 해보았습니다.

아 , 물론 사용전에 npm install mysql-simple-pool 로 모듈을 다운 받아야 합니다.

Hello SyntaxHighlighter

Node로 mysql-simple-pool 사용하기

var Pool = require('mysql-simple-pool');
var pool= new Pool( 200,{  //최대 200 개의 연결.
 host:       "호스트주소",
 port:       "MySQL 포트. 기본 3306",
 user:       "MySQL 계정",
 password:   "MySQL 패스워드",
 database:   "접근 할 데이터베이스" 
});


//Query 날리기. write라는 테이블중 id가 data.id 인 것의 
//x,y 값을 data.x , data.y 로 바꿈.

pool.query('UPDATE `write` set x=? , y=? WHERE id=?',
[data.x, data.y , data.id], function (err,rows){
if(err!=null) console.log(err); //err있으면 출력.
});



최대 200개의 연결을 한다고 Pool을 생성 하더라도 , 실제 커넥션은 3~6개만 생기는데,

커넥션을 계속 생성 하는 것이 아니라 적은 개수를 라운드 로빈 스타일로 돌려서

처리 하는 것 같습니다.


사실 쿼리를 자주 날려야 하기 때문에, 지금은 누군가 쿼리를 한번 하면,

노드 서버에서 객체의 멤버로 저장 하고있다가

다음 사람이 쿼리를 원하면 DB에 쿼리하지 않고 변수를 바로 돌려주도록 하였습니다.

-> 지금은 또 수정되어서, Mysql에서 쿼리한 결과를 In-Memory DB 인 Redis에 넣어놓고,

또 요청할 경우 Mysql 이 아닌 Redis에서 가져다 씁니다.



언젠가 Mysql 과 Redis의 장점만이 합쳐진 DB가 나오길 바랍니다..