ZLink Framework .NET — 사용 가이드¶
다른 언어로 보기 — C++ · C#/.NET · Java · Kotlin · Node/TypeScript
실시간 메시징이 중요한 서버 시스템을 여러 프로세스로 나눠 만드는 .NET
애플리케이션 프레임워크다. ASP.NET Core 위에 그대로 들어가므로 별도 런타임으로 옮겨갈
필요가 없다.
// Both sides must name the mesh identically, or they never see each other
// as peers. The routing id names this node; without it the Framework assigns
// a generated one, which a caller cannot type into a URL.
var mesh = options
.AddRouteMesh("game")
.Listen("tcp://127.0.0.1:7201")
.SetRoutingId(RoutingId.From("game-server-1"));
handler 하나를 등록하면 메시지 디코딩·routing·인코딩은 framework가 처리한다.
만드는 시스템¶
여러 서버 프로세스가 역할을 나눠 협력하고, 상태 변화를 실시간으로 client에 전달해야 하는 시스템에 맞게 설계됐다.
| 도메인 | 핵심 시나리오 |
|---|---|
| 실시간 게임 | room 생성 → player 입장 → 게임 상태 갱신 → client push |
| 고객 지원 채팅 | 대화 개설 → 상담원 배정 → 메시지 중계 → 대화 상태 push |
| 주문 워크플로 | 주문 접수 → 단계별 처리 → 상태 변경 → client 알림 |
| 배송·배차 | 배차 요청 → 수행자 배정·수락 → 상태 추적 → 실시간 push |
공통 구조는 역할별 서버 프로세스가 typed 메시지로 통신하고, client는 실시간 연결로 상태 변화를 받는 것이다.
핵심 기능¶
Channel 메시징 — 서버 간 typed 요청-응답¶
channel은 서버 사이의 통신 경로에 붙인 이름이다. 한쪽이 channel 이름으로 요청을 보내면 반대편이 처리해 응답한다. 직렬화(JSON · MessagePack · Protobuf)는 framework가 처리한다.
보내는 쪽이다.
app.MapGet(
"/players/{playerId}/profile",
async (string playerId, IZLinkRouteClient route, CancellationToken cancellationToken) =>
{
// The target is a channel name. Which node answers is decided at call time.
var profile = await route
.RequestToChannel("profile", new GetPlayerProfile(playerId))
.Async<PlayerProfile>(cancellationToken);
return Results.Ok(profile);
}
);
받는 쪽이다.
// Answers a request addressed to the "profile" channel. Any node that exposes
// this channel may receive it; the caller does not pick one.
public sealed class GetPlayerProfileHandler : IZLinkRequestHandler<GetPlayerProfile, PlayerProfile>
{
public ValueTask<PlayerProfile> HandleAsync(
GetPlayerProfile request,
IZLinkMessageContext context,
CancellationToken cancellationToken
)
{
var profile = new PlayerProfile(request.PlayerId, "rookie", Level: 1);
return ValueTask.FromResult(profile);
}
}
request-reply 외에 fanout(pub/sub)과 route mesh(주소 라우팅) 패턴도 제공한다. Channel 메시징 →
Spot — 상태 단위를 lock 없이 관리¶
Spot은 게임 room, 지원 대화, 주문 처리 단위처럼 상태 영역 하나와 그 참가자를 묶는 실행 단위다. 한 Spot 안에서 일어나는 일 — 참가자 packet, timer, 입장과 퇴장 — 은 모두 직렬로 처리된다. lock 없이 상태에 접근하며, 비동기 처리를 사용해도 같은 Spot에 두 요청이 겹치지 않는다.
// A room owns its own state and is addressed by a global SpotId. Messages sent
// to one room run one at a time, so the fields below need no synchronization.
public sealed class GameRoom(IZLinkSpotContext context) : IZLinkSpot
{
private readonly List<string> _chat = [];
private string _title = "untitled";
public IZLinkSpotContext Context { get; } = context;
public string Title => _title;
public IReadOnlyList<string> Chat => _chat;
// Runs before the room accepts any message. Rejecting here means the create
// call fails and no room exists. Omit this method to accept every request.
public ValueTask<ZLinkSpotCreateResponse> OnCreateAsync(
ZLinkMessage request,
CancellationToken cancellationToken
)
{
var room = request.Decode<OpenRoom>();
_title = room.Title;
return ValueTask.FromResult(ZLinkSpotCreateResponse.Accept());
}
public void Append(string line) => _chat.Add(line);
}
배정을 담당하는 entry spot(node마다 하나)과 상태 본체인 room spot(단위마다 하나)으로 나뉜다. 주기 작업은 timer로 등록한다. Spot →
STREAM과 Actor — client 실시간 연결¶
client의 실시간 양방향 연결을 STREAM이라 하고, 연결 하나를 대표하는 서버 쪽 객체가 Actor다. client가 접속하면 session이 Actor를 만들고, Actor는 Spot에 입장해 상태 처리에 참가한다.
// A slotted packet uses its dispatch Actor. An unslotted packet can use
// the connection only while exactly one Actor is bound.
var actor =
dispatch.Actor
?? (
Context.Actors.Bound.Count switch
{
1 => Context.Actors.Bound.Single(),
0 => throw new InvalidOperationException(
"Authenticate an Actor before sending player packets."
),
_ => throw new InvalidOperationException(
"Select an Actor handle when more than one Actor is bound."
),
}
);
await actor.RelayAsync(payload);
client 쪽 접속은 별도 산출물인 stream connector가 담당한다. STREAM → · Session과 Actor 연결 →
Location — endpoint를 코드에 적지 않는다¶
같은 역할의 서버 여러 대가 떠 있을 때 어느 쪽으로 연결할지 주소를 코드에 적지 않는다. 공용 location store가 주소를 관리하고, 각 서버는 id로 지금 있는 node를 조회한다.
// Rooms and players are addressed by id, not by host, so their current
// location is kept here. Every node reads and writes the same store under
// the same prefix.
options.AddLocationStore(
new ZLinkRedisLocationStore(redis =>
{
redis.ConnectionString = "127.0.0.1:6379";
redis.KeyPrefix = "zlink-tutorial";
})
);
ASP.NET Core가 제공하는 것¶
DI container, configuration, HTTP endpoint, logging은 ASP.NET Core의 것을 그대로 사용한다.
ZLink Framework는 그 위에 handler와 mesh를 등록하므로, 같은 프로세스 안에서 REST
endpoint와 실시간 연결이 함께 동작한다.
목차¶
| 순서 | 문서 | 내용 |
|---|---|---|
| 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 | 모니터링 | 재작성 대기 — 상태 snapshot과 진단 |
파일 번호는 언어에 상관없이 같은 장을 가리키는 식별자다. 읽는 순서는 이 표가 정한다.
01 · 11 · 13 · 16장은 설치 방법과 표면 이름이 언어마다 달라 .NET 전용으로 따로 사용한다.
다이어그램 읽는 법¶
이 가이드의 다이어그램은 같은 시각 언어를 사용한다 — 색이 곧 개념이다.
여러 장이 같은 토폴로지를 그리며, 장마다 확대하는 위치만 바뀐다.
관련 문서¶
.NET문서 진입점: ZLink Framework for .NET- 공개 계약: .NET 공개 계약
- 언어 중립 의미: 공통 스펙
- client library: HTTP client · Stream connector