Laravel Reverb는 Laravel 팀에서 제공하는 first-party 오픈소스 웹소켓 서버다. Laravel의 BroadCasting, Pulse, Laravel Echo 등 다양한 기능들과 통합을 지원한다. 속도를 위해 미세 조정되었고, 단일 서버에서 수천 개의 연결이 가능하다고 한다.
개요
운영중인 서비스에 중복로그인 및 실시간 알림이 필요하여 웹소켓 서버가 필요했다. 운영중인 서비스의 API 서버는 Laravel 애플리케이션이었다. Nest.js를 사용하여 구축하고 싶었지만 백엔드를 담당하고 있지 않아 기술스택의 결정권이 없어 Laravel과 연동이 쉬운 방법을 고려하게 되었다. Laravel 공식문서에서 권장하는 Pusher를 생각했지만 외부서비스인 Pusher는 유료서비스인 점과 연결 수 제한 및 모니터링 같은 기능들이 제한적이었다. 그래서 대안 중 soketi와 같은 훌륭한 오픈소스도 있었지만, 설치가 쉽고 빠른 Laravel Reverb를 선택하게 되었다.
Laravel Reverb를 선택한 이유는 편리함이다. 기본적으로 Pusher 프로토콜을 활용한다. 즉, 브로드캐스팅 기능과 연동하는데 Pusher 만큼의 편리함을 제공한다. 또한 Laravel 애플리케이션에 reverb 패키지만 설치하여 artisan 커맨드로 실행까지 가능하다. 심지어 Redis를 활용하면 scale out도 가능하다. 아마 Laravel 애플리케이션에 웹소켓 서버가 필요하다면 최고의 선택이지 않을까 싶다.
설치
설치는 Laravel 애플리케이션에서 artisan 커맨드를 이용하여 쉽게 설치할 수 있다.
php artisan install:broadcasting
설정
자격 증명을 위해 .env에 APP_ID, APP_KEY, APP_SECRET을 설정한다.
REVERB_APP_ID=my-app-id
REVERB_APP_KEY=my-app-key
REVERB_APP_SECRET=my-app-secret
reverb 애플리케이션 설정을 config/reverb.php에 정의한다.
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Reverb Server
|--------------------------------------------------------------------------
|
| This option controls the default server used by Reverb to handle
| incoming messages as well as broadcasting message to all your
| connected clients. At this time only "reverb" is supported.
|
*/
'default' => env('REVERB_SERVER', 'reverb'),
/*
|--------------------------------------------------------------------------
| Reverb Servers
|--------------------------------------------------------------------------
|
| Here you may define details for each of the supported Reverb servers.
| Each server has its own configuration options that are defined in
| the array below. You should ensure all the options are present.
|
*/
'servers' => [
'reverb' => [
'host' => env('REVERB_SERVER_HOST', '0.0.0.0'),
'port' => env('REVERB_SERVER_PORT', 8080),
'hostname' => env('REVERB_HOST'),
'options' => [
'tls' => [],
],
'max_request_size' => env('REVERB_MAX_REQUEST_SIZE', 10_000),
'scaling' => [
'enabled' => env('REVERB_SCALING_ENABLED', false),
'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'),
'server' => [
'url' => env('REDIS_URL'),
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => env('REDIS_PORT', '6379'),
'username' => env('REDIS_USERNAME'),
'password' => env('REDIS_PASSWORD'),
'database' => env('REDIS_DB', '0'),
],
],
'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 5),
'telescope_ingest_interval' => env('REVERB_TELESCOPE_INGEST_INTERVAL', 5),
],
],
/*
|--------------------------------------------------------------------------
| Reverb Applications
|--------------------------------------------------------------------------
|
| Here you may define how Reverb applications are managed. If you choose
| to use the "config" provider, you may define an array of apps which
| your server will support, including their connection credentials.
|
*/
'apps' => [
'provider' => 'config',
'apps' => [
[
'key' => env('REVERB_APP_KEY'),
'secret' => env('REVERB_APP_SECRET'),
'app_id' => env('REVERB_APP_ID'),
'options' => [
'host' => env('REVERB_HOST'),
'port' => env('REVERB_PORT', 80),
'scheme' => env('REVERB_SCHEME', 'http'),
'useTLS' => env('REVERB_SCHEME', 'http') === 'https',
],
'allowed_origins' => ['*'],
'ping_interval' => env('REVERB_APP_PING_INTERVAL', 30),
'activity_timeout' => env('REVERB_APP_ACTIVITY_TIMEOUT', 60),
'max_message_size' => env('REVERB_APP_MAX_MESSAGE_SIZE', 10_000),
],
],
],
];
config/broadcasting.php 브로드캐스트 연결을 reverb로 설정한다.
<?php
return [
'default' => env('BROADCAST_CONNECTION', 'reverb'),
'connections' => [
'reverb' => [
'driver' => 'reverb',
'key' => env('REVERB_APP_KEY'),
'secret' => env('REVERB_APP_SECRET'),
'app_id' => env('REVERB_APP_ID'),
'options' => [
'host' => env('REVERB_HOST', 'localhost'),
'port' => env('REVERB_PORT', 8080),
'scheme' => env('REVERB_SCHEME', 'http'),
'useTLS' => env('REVERB_SCHEME', 'http') === 'https',
],
'client_options' => [
// Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html
],
],
...
],
];
실행
설치와 마찬가지로 artisan 커맨드를 이용하여 실행한다.
php artisan reverb:start
클라이언트
Laravel의 공식문서는 npm에서 laravel-echo와 pusher-js를 사용한 연결을 설명한다. 확장성을 위해 패키지에 의존하지 않고, Javascript WebSocket을 이용하여 연결하였다.
[ws|wss]://{host}/app/{app-key}
연결
const url = `wss://${socketUrl]}/app/${appKey}`;
const socket = new WebSocket(url);
핸들러
모든 이벤트는 pusher:{event-name}으로 메시지가 송수신 된다.
config/reverb.php에 정의된 apps.app.ping_interval 값에 정의된 초 마다 pusher:ping 이벤트를 각 클라이언트에 보내 연결을 유지한다. 만약 WebSocket으로 연결한 경우 pusher:pong을 보내 연결이 유지될 수 있도록 하여아 한다.
socket.onerror = function (this: WebSocket, ev: Event) { };
socket.onopen = function (this: WebSocket, ev: Event) {
console.log(`[ ${this.url} ] WebSocket opened.`);
this.send(
JSON.stringify({
event: 'pusher:subscribe',
data: { channel: 'my-app' },
})
);
};
socket.onclose = function (this: WebSocket, ev: CloseEvent) {
console.log(`[ ${this.url} ] WebSocket closed.`);
};
socket.onmessage = function (this: WebSocket, ev: MessageEvent) {
const msg = JSON.parse(ev.data);
switch (msg.event) {
case "pusher:ping":
this.send(JSON.stringify({ event: "pusher:pong" }));
break;
}
};
결론
Laravel 애플리케이션과 통합하기 굉장히 좋은 패키지인 것 같다. 기본적인 서버쪽 이벤트 핸들러는 구현이 되어있어 개발하는데 크게 시간을 단축한 것 같다. 브로드캐스팅 기능 또한 reverb와 바로 연동되어 쉬웠다. 클라이언트 부분도 Pusher 프로토콜을 활용했기 때문에 Pusher 문서를 통해 쉽게 WebSocket 객체를 활용할 수 있었다. Pulse를 이용한 모니터링 대시보드도 기회가 될 때 연동해봐야겠다.
'PHP > Laravel' 카테고리의 다른 글
Laravel JWT Authentication 설치 및 설정 (0) | 2021.12.27 |
---|