콘텐츠로 이동

ZLink Framework C++ — 사용자 가이드

실시간 메시징이 중요한 서버 시스템을 여러 프로세스로 나눠 만드는 C++ 애플리케이션 프레임워크다.

#include <zlink/framework.hpp>

int main (int argc, char **argv)
{
    auto app = zlink::framework::app_t::create ();
    app.add_zlink_framework ([] (zlink::framework::zlink_framework_options_t &options) {
        options.http ()
          .listen ("http://0.0.0.0:8080")
          .map_post<open_conversation_http_handler_t> ("/conversations");
    });
    return app.run (argc, argv);
}

핸들러 클래스 하나를 등록하면 메시지 디코딩·routing·인코딩은 프레임워크가 처리한다.


만드는 시스템

여러 서버 프로세스가 역할을 나눠 협력하고, 상태 변화를 실시간으로 클라이언트에 전달해야 하는 시스템에 맞게 설계됐다.

도메인 핵심 시나리오
실시간 게임 룸 생성 → 플레이어 입장 → 게임 상태 갱신 → 클라이언트 push
고객 지원 채팅 대화 개설 → 상담원 배정 → 메시지 중계 → 대화 상태 push
주문 워크플로 주문 접수 → 단계별 처리 → 상태 변경 → 클라이언트 알림
배송·배차 배차 요청 → 수행자 배정·수락 → 상태 추적 → 실시간 push

공통 구조는 하나다 — 역할별 서버 프로세스가 typed 메시지로 통신하고, 클라이언트는 실시간 연결(stream)로 상태 변화를 받는다.

↗ 크게 보기

각 서버 프로세스는 독립 실행 파일이고 서로 TCP로 연결된다. 하나의 서버 안에 HTTP 입구, 다른 서버와의 통신 경로, 클라이언트 연결, 상태 단위 관리가 모두 동거한다. samples/TicTacToe(2개 서버)와 samples/Bingo(4개 서버)가 동작하는 완전한 예제다.


핵심 기능

채널 메시징 — 서버 간 typed 요청-응답

채널은 서버 사이의 통신 경로에 이름을 붙인 것이다. 한쪽이 채널 이름으로 요청을 보내면 반대편이 처리해 응답한다. struct를 그대로 주고받으며 직렬화(JSON / MessagePack / Protobuf)는 프레임워크가 처리한다.

// 보내는 쪽 (채널 클라이언트)
auto result = co_await _client
    .request ("support.core", open_conversation_req_t{user_id})
    .async<open_conversation_res_t> ();

// 받는 쪽 (채널 서버의 핸들러)
class open_conversation_handler_t {
  public:
    using request_type = open_conversation_req_t;
    using reply_type   = open_conversation_res_t;
    static constexpr const char *topic_name = "OpenConversation";
    open_conversation_res_t handle (const open_conversation_req_t &req) { ... }
};

request-reply 외에 fanout(pub/sub)과 route mesh(주소 라우팅) 패턴도 제공한다. Channel 메시징 →


SPOT — 상태 단위를 락 없이 관리

SPOT은 게임 룸, 지원 대화, 주문 처리 단위처럼 "하나의 상태 영역" 과 그 참여자를 묶는 실행 단위다. 한 SPOT 안에서 일어나는 모든 것 — 참여자 패킷, 타이머, 입퇴장 — 은 직렬로 처리된다. std::mutex 없이 상태에 접근할 수 있고, 코루틴으로 비동기 처리를 사용해도 같은 SPOT에 두 요청이 겹치지 않는다.

class conversation_spot_t : public zlink::framework::spot_t,
                             public conversation_t   // 대화 상태 직접 소유
{
  public:
    void configure (zlink::framework::spot_context_t &context)
    {
        context.handlers ().add_actor_packet<&conversation_spot_t::send_message> ();
    }

    send_message_res_t send_message (const user_actor_t &actor,
                                     const zlink::framework::message_context_t &,
                                     const send_message_req_t &request)
    {
        return append (actor.user_id, request.text);   // std::mutex 없이 안전
    }
};

배정·할당을 담당하는 entry spot(노드당 1개)과 상태 본체인 room spot(단위마다 1개)으로 나뉜다. 주기 작업은 timer로 등록한다. Spot →


Stream + Actor — 클라이언트 실시간 연결

클라이언트의 실시간 양방향 연결을 stream이라 하고, 연결 하나를 대표하는 서버 쪽 객체가 actor다. 클라이언트가 접속하면 session이 actor를 생성하고, actor는 SPOT에 입장해 상태 처리에 참여한다.

class support_session_t : public zlink::framework::packet_stream_session_t {
  public:
    task_t<void> on_packet (stream_t &stream,
                            const stream_dispatch_context_t &dispatch,
                            const zlink::message_t &payload) override
    {
        auto actor = co_await _actors.find (actor_id);
        co_await actor.value ().relay (payload);   // 현재 dispatch의 packet을 actor로 전달
    }
};

클라이언트 쪽 접속은 별도 산출물인 stream connector가 담당한다. 8장 → · 9장 →


HTTP Hosting — 서버 프로세스 안에 REST API 내장

별도 웹 서버 없이 같은 프로세스 안에 REST endpoint를 올린다. 경로 파라미터, middleware/handler에서 인증 로직을 구현할 수 있고 TLS를 지원하며 readiness / liveness / health check endpoint도 한 줄로 등록한다.

options.http ()
  .listen ("https://0.0.0.0:8443")
  .configure_tls ([] (auto &tls) {
      tls.certificate_file (cert_path).private_key_file (key_path);
  })
  .map_post<create_game_http_handler_t> ("/games")
  .map_get<get_room_http_handler_t> ("/rooms/{room_id}")
  .map_readiness ("/ready");

20장 →


Configuration · DI · Logging · Monitoring

운영 서버에 필요한 부속을 내장한다.

  • Configuration — CLI 인자, 환경 변수, JSON 파일을 우선순위 순서로 합성. bind<T>() 한 번으로 설정 섹션을 struct에 매핑한다.
  • DI 컨테이너 — 핸들러 생성자 매개 변수만으로 서비스가 자동 주입된다. singleton / scoped / transient 수명을 지원한다.
  • Logging — logger_t<TOwner> DI로 받아 소스 이름이 자동 태그된 로그를 남긴다.
  • Monitoring / Health — socket·discovery·spot·타이머 이벤트를 typed 구독으로 받는다. /ready, /healthz endpoint에 health check를 연결한다.

18장 → · 19장 → · 11. Monitoring 장


Registry / Discovery — 서버 주소 자동 연결

여러 Play 서버 인스턴스가 뜰 때 어느 서버로 연결할지, endpoint를 코드에 하드코딩하지 않는다. Redis 기반 공용 Location Store가 주소를 관리하고, 각 서버는 add_location_store<redis::redis_location_store_t> ()로 동적으로 찾는다.

options.add_location_store<redis::redis_location_store_t> ()
  .set_connection_string (topology.redis_endpoint)
  .set_key_prefix (topology.redis_key_prefix + "location:");

auto room_mesh = options.add_route_mesh (sample_names_t::room_spot_mesh);
room_mesh.set_routing_id (zlink::routing_id_t::from ("bingo-play-" + topology.play_node))
  .listen (topology.selected_play_spot_router_endpoint ());
room_mesh.objects ()
  .server ()
  .add_entry_spot<bingo_entry_spot_t> ()
  .add_spot_factory<bingo_room_spot_t> (sample_names_t::room_spot);

10장 →


목차

순서 문서 내용
1 개요 핵심 표면, 계층 구조, topology를 빠르게 파악하기
2 둘러보기 MORPG 하나로 기능별 동작을 직접 눌러 보며 확인하기
3 퀵스타트 설치, 두 process가 서로 호출하는 최소 project, 첫 실행 점검
4 핵심 개념 channel·Spot·Actor·session이 각각 무엇인가
5 Channel 메시징 이름으로 부르는 경로 — 등록과 호출
6 Spot 여럿이 함께 사용하는 자리를 id로 만들고 부르기
7 Actor 개체 하나를 id로 만들고 부르기
8 STREAM mesh 밖의 client가 연결 하나로 붙기
9 Session과 Actor 연결 연결 하나를 Actor 하나에 묶기
10 Location id로 지금 있는 node를 조회하기
11 모니터링 기능별 가이드 자리 표시 — 본문은 아직 없다
12 실행 모델 두 queue, 직렬화 범위, 실행권
13 Backpressure 처리보다 도착이 빠를 때와 영향을 주는 옵션
14 활성화와 수명 종류별 생성 시점, lifecycle callback, 주입 수명
15 Actor membership Spot 사이 이동, 예약과 상한
16 Timer와 worker 주기 실행, 줄 밖 실행, 실행권 반납
17 Relocation 옮겨도 남는 것, adapter, 이동 단위
18 Channel 동작 원리 패턴 차이, 대상 선택, pub/sub, 연결과 discovery
19 Handler와 메시지 처리 등록 변형, filter, codec, handler 종류
20 STREAM의 동작 원리 등록 검증, 오류 귀속, 응답 token, 실행 방식
21 Session 묶음의 동작 원리 묶는 개수, 경로 갱신, 끊김 통지, 실패
22 운영과 lifecycle 런타임 메트릭, relocate, drain, readiness 연결
23 Options 옵션 목록, 기본값과 바꾸는 시점
24 샘플 고르기 어떤 샘플을 먼저 볼지 고르고 실행하는 방법
25 Bingo 따라 읽기 인증·매칭·room·timer·multicast·cleanup을 코드 순서로
26 TicTacToe 따라 읽기 수동 연결·수동 등록, 다른 node로의 Actor join
27 SupportChat 따라 읽기 session 하나에 Actor 여럿, metadata relay, idle timer
28 DeliveryDispatch 따라 읽기 one-way send, deadline 기록과 재배정, 고객 push
29 ShoppingMall 따라 읽기 owner Instance Spot, replay·다음 단계·expected version
30 GameQuest 따라 읽기 player별 owner, best-effort push와 보정
31 ZoneWorld 따라 읽기 capacity placement, 경계 join과 relocation, fanout·관찰
32 E2E 테스트 client library로 시스템 전체를 검증하기
33 주요 타입 사용 색인 계약 인터페이스를 검증 코드로 색인
34 DI 컨테이너 ZLink host — 수명 3종, 등록, handler 자동 주입
35 Configuration ZLink host — 설정 소스, 우선순위, section 바인딩
36 HTTP Hosting ZLink host — embedded HTTP server와 route handler
37 실행과 설정 모델 ZLink host — thread 모델과 설정이 만나는 자리
38 모니터링 재작성 대기 — 상태 snapshot과 진단

파일 번호는 언어에 상관없이 같은 장을 가리키는 식별자다. 1~17장은 다섯 언어가 공유하고, 18~21장은 C++에만 있다 — DI·configuration·HTTP hosting·실행 모델은 .NET이 런타임에서 받는 것을 C++은 프레임워크가 직접 제공하기 때문이다.

이 넷은 기초에 해당하므로, 2·3장을 읽은 뒤 바로 18~21장을 먼저 보고 4장으로 돌아와도 좋다.


다이어그램 읽는 법

이 가이드의 모든 다이어그램은 같은 시각 언어를 사용한다 — 색이 곧 개념이다.

↗ 크게 보기

여러 장이 같은 TicTacToe/Bingo 토폴로지를 그리며, 장마다 확대 위치만 바뀐다.

관련 문서