Server-Sent Events
Server-Sent Events
섹션 제목: “Server-Sent Events”SSE 미들웨어는 Server-Sent Events 프로토콜을 사용하여 서버에서 HTTP 클라이언트로 이벤트를 스트리밍합니다.
두 가지 메커니즘이 제공됩니다: HTTP 핸들러에서의 직접 스트리밍과 sse_relay 미들웨어를 통한 프로세스 기반 릴레이입니다.
직접 스트리밍
섹션 제목: “직접 스트리밍”res:write_event()를 사용하여 HTTP 핸들러에서 SSE 이벤트를 직접 전송합니다. 응답은 첫 호출 시 자동으로 SSE 모드로 전환되며 적절한 헤더가 설정됩니다.
local http = require("http")
local function handler() local res = http.response()
res:write_event({name = "status", data = {state = "started"}}) res:write_event({name = "progress", data = {percent = 50}}) res:write_event({name = "status", data = {state = "complete"}})end각 이벤트는 name과 data 필드가 필요합니다. data 값은 자동으로 JSON으로 인코딩됩니다.
SSE Relay
섹션 제목: “SSE Relay”SSE Relay 미들웨어는 프로세스가 백킹하는 장기 SSE 스트림을 생성합니다. WebSocket Relay와 동일한 릴레이 패턴을 따릅니다.
동작 방식
섹션 제목: “동작 방식”- HTTP 핸들러가 JSON 릴레이 설정과 함께
X-SSE-Relay헤더를 설정합니다 - 미들웨어가 응답을 가로채고 SSE 세션을 생성합니다
- 세션은 자체 PID를 가진 프로세스로 등록됩니다
- 세션 PID로 전송된 메시지는 SSE 이벤트로 클라이언트에 전달됩니다
프로세스 시맨틱
섹션 제목: “프로세스 시맨틱”SSE 스트림은 자체 PID를 가진 완전한 프로세스입니다. 프로세스 시스템과 통합됩니다:
- 주소 지정 가능 — 모든 프로세스가 스트림 PID로 메시지를 보낼 수 있음
- 모니터링 가능 — 프로세스가 종료 이벤트를 위해 SSE 스트림을 모니터링할 수 있음
- 연결 가능 — SSE 스트림은 다른 프로세스에 연결될 수 있음
- EXIT 이벤트 — 스트림이 닫힐 때 모니터가 종료 알림을 받음
-- 모든 프로세스에서 SSE 클라이언트로 이벤트 전송process.send(stream_pid, "sse.message", {event = "update", value = 42})
-- SSE 스트림 모니터링process.monitor(stream_pid)done 이벤트를 받습니다.
라우터에 post-match 미들웨어로 추가합니다:
- name: sse_router kind: http.router meta: server: gateway prefix: /sse post_middleware: - sse_relay post_options: sserelay.allowed.origins: "https://app.example.com"| 옵션 | 설명 |
|---|---|
sserelay.allowed.origins | 쉼표로 구분된 허용 origin (와일드카드 지원) |
핸들러 설정
섹션 제목: “핸들러 설정”HTTP 핸들러는 프로세스를 생성하고 릴레이를 설정합니다:
local http = require("http")local json = require("json")
local function handler() local res = http.response()
-- 핸들러 프로세스 생성 local pid = process.spawn("app.sse:handler", "app:processes")
-- 릴레이 설정 res:set_header("X-SSE-Relay", json.encode({ target_pid = tostring(pid), message_topic = "sse.message", heartbeat_interval = "30s", metadata = { user_id = http.request():query("user_id") } }))end릴레이 설정 필드
섹션 제목: “릴레이 설정 필드”| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
target_pid | string | — | 메시지를 받을 프로세스 PID (분리 모드는 생략) |
message_topic | string | sse.message | 전달되는 이벤트의 토픽 필터 |
heartbeat_interval | duration | 30s | 하트비트 주기 (예: 30s, 1m) |
idle_timeout | duration | — | 비활성 후 스트림 종료 |
hard_timeout | duration | — | 절대 시간 후 스트림 종료 |
metadata | object | — | join/leave/heartbeat 메시지에 첨부 |
관리(Managed) 모드와 분리(Detached) 모드
섹션 제목: “관리(Managed) 모드와 분리(Detached) 모드”관리 모드
섹션 제목: “관리 모드”target_pid가 설정되면 릴레이는 관리 모드로 동작합니다:
- 대상 프로세스를 모니터링
- 연결 시
sse.join, 끊김 시sse.leave를 전송 - 대상이 종료되면 스트림을 자동으로 닫음
분리 모드
섹션 제목: “분리 모드”target_pid가 생략되면 릴레이는 분리 모드로 시작합니다:
- 클라이언트에
stream_pid와message_topic이 포함된ready이벤트를 발행 - 초기에는 모니터링되는 프로세스가 없음
- 프로세스가 나중에
sse.control메시지로 연결할 수 있음
-- 분리 설정: target_pid 없음res:set_header("X-SSE-Relay", json.encode({ heartbeat_interval = "30s"}))클라이언트는 ready 이벤트를 수신합니다:
{"stream_pid": "sse@node/abc123", "message_topic": "sse.message"}메시지 토픽
섹션 제목: “메시지 토픽”릴레이는 스트림과 대상 프로세스 간 통신에 다음 토픽을 사용합니다:
| 토픽 | 방향 | 시점 | 페이로드 |
|---|---|---|---|
sse.join | stream → target | 클라이언트 연결 | client_pid, metadata |
sse.message | target → stream | 기본 이벤트 토픽 | SSE 이벤트로 전달됨 |
sse.heartbeat | stream → target | 주기적 (설정된 경우) | client_pid, uptime, message_count |
sse.leave | stream → target | 클라이언트 연결 끊김 | client_pid, metadata |
sse.control | any → stream | 제어 명령 | 릴레이 설정 필드 |
sse.close | any → stream | 강제 종료 | 선택적 사유 문자열 |
대상 프로세스에서 수신
섹션 제목: “대상 프로세스에서 수신”local json = require("json")
local function handler() local inbox = process.inbox()
while true do local msg, ok = inbox:receive() if not ok then break end
local topic = msg:topic() local data = msg:payload():data()
if topic == "sse.join" then local client_pid = data.client_pid
elseif topic == "sse.heartbeat" then -- 주기적 헬스 체크
elseif topic == "sse.leave" then cleanup(data.client_pid) end endend이벤트 전송
섹션 제목: “이벤트 전송”스트림 PID에 메시지를 보내 클라이언트에 이벤트를 전송합니다:
-- 기본 메시지 토픽으로 전송process.send(stream_pid, "sse.message", { event = "update", value = 42})
-- 스트림 강제 종료process.send(stream_pid, "sse.close", "session expired")설정된 message_topic으로 전송된 이벤트는 SSE 이벤트로 클라이언트에 전달됩니다. 토픽 이름이 SSE 이벤트 이름이 됩니다.
연결 전송
섹션 제목: “연결 전송”제어 메시지를 보내 대상 프로세스, 토픽 필터, 타임아웃을 동적으로 변경합니다:
process.send(stream_pid, "sse.control", { target_pid = tostring(new_pid), message_topic = "custom.topic", idle_timeout = "5m"})대상이 변경되면 릴레이는 이전 대상에 sse.leave를, 새 대상에 sse.join을 보냅니다. 재연결 없이 분리하려면 target_pid를 빈 문자열로 설정하세요.
- 미들웨어 — 미들웨어 설정
- WebSocket Relay — WebSocket 동등 기능
- 프로세스 — 프로세스 메시징