프로그래머로써 이런 문제들을 격으셨을겁니다.
1. 대량 이메일, 이미지 리사이징, 또는 큰 작업이 들어가는 시점에 유져가 기다리는 시간이 오래 걸린다.
2. API랑 연결하는 시간이 오래걸려서 유져가 기다리는 시간이 오래 걸린다.
3. 프로세싱이 오래 걸리는 시간, 즉 유져가 프로세싱이 끝날때 까지 기달리는 시간이 오래 걸리면, 문제가 있을수도 있다.
오래걸리는 요소들을 백그라운드에서 처리하게 되면, 유져가 기다리는 시간이 단축뒬수 있습니다.
간출여서 말하자면, Parallel Processing, 즉 동시에 같이 작업이 진행될수 있도록 하는 방식입니다.
방법은 여러가지가 있는데요, 가장 효과적이고 안전하게 생각하는것은 MQ 라고 생각합니다.
MQ는, 오래걸릴것 같은 작업들을 Queue에 추가하여, Queue에서 백그라운드 일꾼들에게 작업을 순서대로 진행 하게 하는 방식입니다. 시스템에 부담을 덜기위해 최대 일꾼수를 지정할수 있으며, 가능한대로 스케일링, 최적화가 가능한 시스템 입니다.
MQ란, 여러종류들이 있지만, 오늘 강좌에서는 MongoDB로 직접 만들어서 사용하는게 좋을듯 합니다. 이유는 MongoDB에서 쓰기, 읽기가 충분히 빠르기 때문에 다른 솔수션들을 추가로 배우지 않아도 돼니까요.
인기있는 MQ들:
- RabbitMQ http://www.rabbitmq.com/
- Amazon Simple Queue Service http://aws.amazon.com/ko/sqs/
필요 요소:
- CodeIgniter 2.x (CLI 지원되는 버젼)
- 리눅스 시스템
- supervisord - http://supervisord.org/
- MongoDB
- MongoDB 라이브러리 - https://github.com/alexbilbie/codeigniter-mongodb-library/tree/v2
- PHP MongoDB Extension, mongo.so
-------------------------
Part I : Models
일단 Queue model을 만듭시다. 이름은 queue_model.php 입니다.
01.
<?php
02.
03.
class
Queue_model
extends
CI_Model {
04.
05.
// 콜랙션 이름
06.
private
$queue_collection
=
'queues'
;
07.
08.
function
__construct() {
09.
parent::__construct();
10.
// 라이브러리 불러오기
11.
$this
->load->library(
'mongolib'
);
12.
}
13.
14.
/**
15.
* 인덱스 만들기, 만들려면 한번 실행을 해주세요.
16.
*/
17.
public
function
initialize_index() {
18.
$this
->mongolib->add_index(
$this
->queue_collection,
array
(
'locked'
=>
'ASC'
));
19.
}
20.
21.
/**
22.
* 작업 추가하기
23.
*/
24.
public
function
add_queue(
$data
) {
25.
$insert_data
=
array
(
26.
'data'
=>
$data
,
27.
'locked'
=> null,
28.
'locked_at'
=> null,
29.
'date'
=>
new
MongoDate()
30.
);
31.
return
$this
->mongolib->insert(
$this
->queue_collection,
$insert_data
);
32.
}
33.
34.
/**
35.
* Queue 사이즈 불러오기, 작업필요한지 확인
36.
*/
37.
public
function
count
() {
38.
return
$this
->mongolib->where(
array
(
'locked'
=> null))->
count
(
$this
->queue_collection);
39.
}
40.
41.
/**
42.
* 작업 실행, 가장 최근작업 부터 불러와서 여기서 작업을 실행 한다.
43.
*/
44.
public
function
pop_queue() {
45.
46.
// find and modify
47.
// 같은 작업이 반복되지 않을려면, 잠기지 않은 아이템들부터 불러온다.
48.
$query
=
array
(
'locked'
=> null);
49.
50.
// 찻았으면 잠근다.
51.
$update
=
array
(
'$set'
=>
array
(
'locked'
=> true,
'locked_at'
=>
new
MongoDate()));
52.
53.
// 최근 날짜부터
54.
$sort
=
array
(
'date'
=> 1);
55.
56.
// 실행
57.
$data
=
$this
->mongolib->find_and_modify(
$this
->queue_collection,
$query
, null,
$update
);
58.
59.
// 성공 했을경우, 아이템을 삭제하고 작업을 실행한다.
60.
if
(
$data
[
'ok'
]) {
61.
$queue_item
=
$data
[
'value'
];
62.
$this
->mongolib->where(
'_id'
,
$queue_item
[
'_id'
])->
delete
(
$this
->queue_collection);
63.
64.
// 작업 실행.
65.
var_dump(
$queue_item
[
'data'
]);
66.
}
67.
}
68.
}
*주의* 인덱스를 안만드시면 속도에 문제가 있습니다. 경험상 최대 100배정도 느립니다. 인덱스를 꼭 만드세요.
Part II: Controllers
다음은 백그라운드에서 돌릴 일꾼을 만드는 일 입니다. Controller에서 Model을 접근하여 일거리를 찻은 다음, 찻았으면 일을 하게 하는 방식입니다.
Controller 이름은 queue, queue.php 입니다.
01.
<?php
if
(!defined(
'BASEPATH'
))
exit
(
'No direct script access allowed'
);
02.
03.
class
queue
extends
CI_Controller {
04.
05.
protected
$phone
;
06.
07.
function
__construct() {
08.
parent::__construct();
09.
$this
->load->model(
'queue_model'
);
10.
}
11.
12.
/**
13.
* 대몬으로 실행시킬 일꾼.
14.
*/
15.
public
function
daemon() {
16.
while
(1) {
17.
$cur_count
=
$this
->queue_model->
count
();
18.
if
(
$cur_count
> 0) {
19.
// 작업이 있으면 일을 시작해라.
20.
$this
->queue_model->pop_queue();
21.
}
else
{
22.
// 없으면 잠시 기달림.
23.
sleep(rand(3, 7));
24.
}
25.
}
26.
}
27.
}
Part III: Supervisord
Supervisord를 설치 합니다.
Supervisord에서 mysite.conf 만듭니다. 일단, 일꾼은 3명으로 지정하겠습니다. (numprocs=3).
1.
[program:mysite]
2.
command = php /
var
/www/index.php queue daemon
3.
process_name=%(program_name)s_mysite%(process_num)02d
4.
numprocs=3
5.
autorestart=true
6.
autostart=true
Part IV: Finish
일 추가 할려면, queue_model 불러오셔서 $this->queue_model->add_queue($data) 하시면 됩니다.
일 시작 할려면, pop_queue 함수 안에서 불로온 $data 변수를 가지고 일을 실행하시면 됩니다. 만약 대량 이메일 경우, $data에서 불러오는 이메일 리스트를, 여기서 돌리시면 됩니다. 이미지 프로세싱도 같은 형식입니다.
numprocs=3 지정이 되어 있다면, 한꺼번에 3명에 일꾼들이 동시에 작업하게 됩니다. 동시에 작업을 하게 되면 완성된 parallel processing을 느끼실수 있을겁니다.
수고하세요. ^^