php에서 gcm 푸시 빠르게 보내기 (feat. async / generator)

29
빠르게 푸시 보내기 Feat. Async / Generator 전창완

Upload: changwan-jun

Post on 08-Jan-2017

464 views

Category:

Software


3 download

TRANSCRIPT

Page 1: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

빠르게 푸시 보내기Feat. Async / Generator

전창완

Page 2: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

전창완

- Oponiti & Allbus 서버 개발자

- Wandu Framework

발표자 소개

Page 3: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

1. 기존의 방식

2. 어떻게 개선할까? - Async

3. 어떻게 개선할까? - Generator

4. 결론

목차

Page 4: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

GCM 이란?

기존의 방식

1. GCM(Google Cloud Messaging)이란, 구글 서버를 이용해서 안드로이드 기기에 푸시를 보내는 것.

Page 5: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

기존에 알아야 할 사실

기존의 방식

1. GCM은 HTTP Request를 통해 메세지를 보낼 수 있음.

2. GCM은 한번에 1000개씩 보낼 수 있음.

3. 저희 서버에 등록된 GCM Key는 약 70만개.

4. 모든 사람이 공정하게 푸시를 받아야 함. (매번 푸시 발송 순서가 달라야함.)

Page 6: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

기존의 방식기존의 동작 방식

사용자를�1000명�단위로�그룹�지정

그룹을�랜덤으로�Shuffle

1000개�단위로푸시�전송

로그�쌓기

끝날때까지 반복

Page 7: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

기존의 방식기존의 동작 방식

사용자를�1000명�단위로�그룹�지정

그룹을�랜덤으로�Shuffle

1000개�단위로푸시�전송

로그�쌓기

끝날때까지 반복

여기까지 보통 80분 정도 소요..

Page 8: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

기존의 방식기존의 동작 방식

사용자를�1000명�단위로�그룹�지정

그룹을�랜덤으로�Shuffle

1000개�단위로푸시�전송

로그�쌓기

끝날때까지 반복

Page 9: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

기존의 방식기존의 동작 방식

사용자를�1000명�단위로�그룹�지정

그룹을�랜덤으로�Shuffle

1000개�단위로푸시�전송

로그�쌓기

끝날때까지 반복

여기를 개선해보자!!

Page 10: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

Async, 간단히 이야기 하면..(다들 아시잖아요..)

Page 11: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

누구나 다 아는 그 그림, Sync vs Async

어떻게 개선할까? - Async

Sync Async

Page 12: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

방금 그 그림을 반복해서 한다면..

어떻게 개선할까? - Async

Sync Async

*순서는 절대 보장되지 않아요!

Page 13: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

HTTP RequestAsync로 사용해보자!

Page 14: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

PHP에서 Async의 지원

어떻게 개선할까? - Async

1. PHP의 모든 로직은 전부 Sync가 기반.

2. PHP에서 Async를 사용하기 위해서는 Pthread 모듈이나 Ev, Uv 등을 사용해야 함.

3. 하지만, 내장 Curl의 경우 Multi Curl을 지원해서 비동기로 처리가 가능.

4. 즉, Curl에 한해서는 기본 PHP로 Async스럽게 사용할 수 있음.

5. 이 Multi Curl을 Async(그리고 Promise)를 통해서 구현해놓은 라이브러리가 Guzzle Http.

Page 15: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

어떻게 개선할까? - AsyncHTTP Request할때, 다들 많이 사용할 그 녀석..

Page 16: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

어떻게 개선할까? - AsyncRequest 생성 매서드 하나 만들고..

public function createRequest($tokens, $message, $url = '') { return new Request( 'POST', new Uri('https://android.googleapis.com/gcm/send'), '1.1', [ 'Authorization' => "key=xxxxxxxxxxxxxxx", 'Content-Type' => 'application/json', ], new StringStream(json_encode([ 'registration_ids' => $tokens, 'data' => [ 'alert' => $message, 'url' => $url ], ])) );}

Request, Uri, StringStream은 PSR-7 객체입니다.

Page 17: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

어떻게 개선할까? - Async나머진 그냥 예시 참고해서 만들면 됨.

http://docs.guzzlephp.org/en/latest/quickstart.html

$requests = [];foreach ($this->getChunkedTokens() as $tokens) { $message = $this->getMessate(); $url = $this->getUrl(); $requests[] = $this->createRequest($tokens, $message, $url); } $pool = new Pool($this->client, $requests, [ 'concurrency' => 20, // 메모리에 맞춰서 알아서.. 'fulfilled' => function ($response, $index) { $this->output->writeln(date('[Y-m-d H:i:s] ') ."success in {$index} !!"); }, 'rejected' => function ($reason, $index) { $this->output->writeln(date('[Y-m-d H:i:s] ') ."fail in {$index} .."); },]);$pool->promise()->wait();

Page 18: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

테스트 환경에서 70만건 발송(단위 = 초)

어떻게 개선할까? - Async

Page 19: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

여기까지 했을 때의 문제.

어떻게 개선할까? - Async

1. Iterator로 요청하기 때문에 GCM 키 70만건을 한번에 메모리에 올려야 함.

2. 구글 문서에 따르면 GCM Key는 최대 4Kb 까지 가능함.

3. 그냥 2.8Gb 정도의 메모리를 소요함.

4. 굳이 이 만큼의 메모리를 소비해야 하는가?

Page 20: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

Generator를 사용하자!(한번쯤 들어보셨잖아요..)

Page 21: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)
Page 22: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

어떻게 개선할까? - Generator기존의 소스

$requests = [];foreach ($this->getChunkedTokens() as $tokens) { $message = $this->getMessage(); $url = $this->getUrl(); $requests[] = $this->createRequest($tokens, $message, $url); } $pool = new Pool($this->client, $requests, [ 'concurrency' => 30, // 메모리에 맞춰서 알아서.. 'fulfilled' => function ($response, $index) { $this->output->writeln(date('[Y-m-d H:i:s] ') ."success in {$index} !!"); }, 'rejected' => function ($reason, $index) { $this->output->writeln(date('[Y-m-d H:i:s] ') ."fail in {$index} .."); },]);$pool->promise()->wait();

Page 23: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

어떻게 개선할까? - GeneratorGenerator 다듬어진 소스

$requests = function () { foreach ($this->getChunkedTokens() as $tokens) { $message = $this->getMessage(); $url = $this->getUrl(); yield $this->createRequest($tokens, $message, $url); }}; $pool = new Pool($this->client, $requests(), [ 'concurrency' => 30, // 메모리에 맞춰서 알아서.. 'fulfilled' => function ($response, $index) { $this->output->writeln(date('[Y-m-d H:i:s] ') ."success in {$index} !!"); }, 'rejected' => function ($reason, $index) { $this->output->writeln(date('[Y-m-d H:i:s] ') ."fail in {$index} .."); },]);$pool->promise()->wait();

Page 24: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

테스트 환경에서 70만건 발송(단위 = MB)

어떻게 개선할까? - Generator

Page 25: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

실제 서비스에서는어떻게 개선되었는가?

Page 26: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

실서버 걸리는 시간(단위 = 분)

결론

Page 27: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

실서버 걸리는 시간(단위 = 분)

결론

메모리는 보통 50MB 이하..

Page 28: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

결론

1. Async는 속도를 향상 시킬 수 있음. 따라서, Async로 할 수 있는 작업은 Async로 바꾸자.

2. Generator는 메모리를 효율적으로 사용할 수 있음. 따라서, Generator로 할 수 있다면 Generator를 사용해보자.

3. 그렇지만 이게 정답은 아님.

정리

Page 29: PHP에서 GCM 푸시 빠르게 보내기 (feat. Async / Generator)

Q & A ?