125호. 확장성 있는 라라벨 코드를 작성하는 5가지 방법 free

2020-04-14

오늘은 잭 엘리스가 쓴 5 ways to write Laravel code that scales 를 간단히 정리해 전달해드리고자 합니다. 잭 엘리스는 Serverless Laravel 이라는 라라벨 Vapor 강의 동영상을 판매하고 있고 Fathom Analytics라는 서비스를 운영하고 있습니다.


1. 데이터베이스 다운타임에 대비하라


데이터베이스는 다운되었지만 큐 워커는 계속 동작하는 경우 잡이 처리되지 않을 것입니다.



Job이 제한된 횟수를 초과하는 경우 이 Job들은 failed_jobs 데이터베이스 테이블에 추가됩니다.



메뉴얼의 설명에서 볼 수 있듯이 실패한 잡은 failed_jobs 데이터베이스에 저장되는데(메뉴얼 링크), 데이터베이스가 다운된 상태이므로 실패한 잡 데이터가 모두 날아갈 수 있습니다.


재시도 횟수를 늘리고 다음과 같은 코드를 이용해서 일시적인 데이터베이스 다운타임에 대비할 수 있다고 합니다.


php artisan queue:work redis --tries=15 --delay=3

try {
// Check to see if the database is online
DB::connection()->getPdo();
} catch (\Exception $e) {
if ($this->attempts() <= 13) {
$this->release(60);
} else {
$this->release(1200);
}
}

13번 재시도까지는 1분 간격으로 재시도하고, 이후 2번은 20분 간격으로 시도하도록 하는 내용입니다. 이렇게 하면 데이터베이스 다운타임이 있어도 1시간 이내에 복구하면 잡이 사라지지 않을 수 있습니다.


2. 외부 서비스 장애에 대비하라


AWS SQS는 매우 믿을만한 서비스이지만, 이 역시도 장애가 발생할 수 있습니다. 그러면 우리가 SQS에 잡을 보내지 못하는 경우가 생길 수 있습니다.


아래와 같이 예외가 발생하면 대기했다가 다시 잡을 던지도록 해서 이러한 상황에 어느 정도 대비할 수 있습니다.


// Adding Fault tolerance
retry(20, function() use ($request) {
dispatch(new JobThatUsesTheRequest($request));
}, 200);

retry()는 라라벨의 글로벌 헬퍼로(메뉴얼 링크) 예외가 발생하면 지정한 시간, 횟수 동안 재시도 후 최종 예외를 발생시킵니다.


3. 더욱 더 빠른 세션 드라이버를 사용하라


파일 세션은 분산 처리에 적합하지 않고 데이터베이스 세션은 처음엔 괜찮지만 규모가 커질 수록 느려집니다. 레디스 같은 인메모리 저장소를 사용하는게 좋습니다.


4. 의미 없는 곳에 쿼리를 낭비하지 마라


국가 정보 같은 것은 바뀔 일이 거의 없습니다. 따라서 이렇게 자주 바뀌지 않는 데이터는 캐시를 적극적으로 사용해서 데이터베이스에 요청하는 횟수를 줄이는게 좋습니다.


$countries = Cache::remember(‘countries:all’, 86400, function() {
return Country::orderBy(‘name’, ‘asc’)->get();
});

변경이 잦은 모델도 직접 관리를 잘 해주면 충분히 캐시를 활용할 수 있다고 합니다. 잭이 운영하는 Fathom Analytics에서는 Site::loadFromCache($id), Site::updateCache($id, $site), Site::deleteFromCache($id) 같은 식으로 메서드를 만들어 사용한다고 합니다. 최신 모델을 캐시에 반영하기 위해 옵져버를 사용한다고 하네요.


5. 커맨드로 하는 일을 줄여라


요약하자면 커맨드에서 직접 처리하지 말고 큐를 활용하라는 이야기 입니다. 데이터가 적을 때는 문제가 없지만 처리할 데이터가 많아지면 시간 초과가 되는 등의 문제가 발생할 수 있습니다. 커맨드에서는 큐에 잡을 보내는 일만 하고 실제 처리를 잡에서 하면 큐를 통해 한 번에 하나씩 처리할 수 있고, 여러 워커를 동시에 사용하면 더 많은 작업을 더 빨리 처리할 수도 있습니다.


마치며


코드를 작성하다보면 스케일에 대한 고려 없이 작성하게 되는 경우가 많은 것 같아요. 의존하는 서비스가 문제가 생길 가능성에 대한 방어 코드도 잘 작성 안하게 되고요. 이번 글의 원문을 통해서 코드를 작성할 때 스케일과 외부 서비스 중단 가능성에 대해 한 번 더 생각해보게 된 계기가 된 것 같습니다.


그리고 1일 1식 라라벨에 필진으로 참여하고 계시는 이삼구 라쉐프님이 얼마전에 ‘라라벨은 큐를 적극적으로 쓰도록 설계된 것 같다'고 말씀하신 적이 있는데, 이 글의 원문을 읽고 보니 정말 그런 것 같다는 생각이 들더군요. :)


이현석

메쉬 코리아 개발자. 바쁜 팀장님 대신 알려주는 신입 PHP 개발자 안내서를 쓰고, 클린 아키텍처 인 PHP를 번역했습니다. 2020년에 출간될 Laravel Up & Running 2nd Edition을 번역하고 있습니다.