콘텐츠로 이동

.NET Quickstart — 설치부터 첫 요청까지

이 장을 읽고 나면

패키지를 설치하고, 두 process가 서로 호출하는 최소 project를 실행할 수 있다.

저장소의 framework/languages/dotnet/quickstart/ 프로젝트다. 아래 코드 블록은 사이트를 빌드할 때 그 파일에서 읽는다. location store 없이 process 둘이 서로의 endpoint를 직접 지정해 request/reply 한 번을 주고받는다.

0. 예제 저장소 clone

이 장의 project는 zlink-dotnet-examples 저장소의 quickstart/이며, 기능 가이드가 읽는 program인 tutorial/과 samples/도 같은 저장소에 있다.

git clone https://github.com/zlink-systems/zlink-dotnet-examples.git
cd zlink-dotnet-examples/quickstart

main은 최신 릴리스에 그 뒤의 수정을 더한 것이고, 패키지 버전은 그 릴리스에 맞춰져 있다. 이전 릴리스는 tag vA.B.C로 받는다(git checkout vA.B.C). 이슈와 PR은 zlink-systems/zlink로 보낸다.

1. 설치

  • .NET SDK 8.0 이상 (net8.0)
  • nuget.org 접근

NuGet에서 받는다. 서버 하나를 만들 때 필요한 최소 조합은 다음과 같다.

# 계약과 runtime. core 메시징 엔진(Zlink)을 전이 의존으로 포함한다
dotnet add package Zlink.Framework
# DI·hosted service 등록(AddZLinkFramework)
dotnet add package Zlink.Framework.AspNetCore

Zlink(binding)는 적지 않는다. Zlink.Framework가 의존 버전을 선언한다.

Directory.Packages.props
<Project>

  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>

  <ItemGroup>
    <!-- 계약과 runtime. Zlink(binding)는 여기서 고정하지 않고
         Zlink.Framework의 전이 의존에 맡긴다. -->
    <PackageVersion Include="Zlink.Framework" Version="0.30.0" />
    <!-- DI·hosted service 등록(AddZLinkFramework). -->
    <PackageVersion Include="Zlink.Framework.AspNetCore" Version="0.30.0" />
  </ItemGroup>

</Project>
nuget.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
</configuration>

필요할 때 추가하는 package는 다음과 같다.

package 언제 추가하나
Zlink.Framework.Locations.Redis Redis location store로 자동 연결을 사용할 때(Location)
Zlink.Framework.Codecs.Protobuf · .MessagePack 기본 JSON codec 대신 사용할 때(Handler와 메시지 처리)
Zlink.Stream.Connector 외부 client(게임 client·모바일)를 만들 때(STREAM)
Zlink.HttpClient 서버에서 HTTP를 호출할 때(HTTP Client 가이드)

라이선스는 계층마다 다르다 — core·binding은 MPL-2.0, framework는 FSL-1.1-ALv2, Zlink.HttpClient는 Apache-2.0이다. 서비스를 만들어 파는 데 드는 비용은 없다.

2. 공유 계약

Shared/Contracts.cs
namespace QuickStart.Shared;

// Request/reply contract shared by the server and client processes.
public sealed record Hello(string Name);

public sealed record Greeting(string Text);

3. 처리하는 쪽

IZLinkRequestHandler<,>를 구현한 handler는 Channel(...).Server().AddRequestHandler<...>()에 직접 등록한다. 어느 channel에 노출할지가 이 등록으로 정해진다.

Server/Program.cs
using QuickStart.Shared;
using Zlink.Framework.AspNetCore;
using Zlink.Framework.Contracts.Handlers;

var builder = WebApplication.CreateBuilder(args);

// This process does not expose HTTP; the port only needs to differ from the
// client process's port when both run on the same host.
builder.WebHost.UseUrls("http://127.0.0.1:5081");

builder.Services.AddZLinkFramework(options =>
{
    // Names the mesh and opens this process's endpoint for peers to connect to.
    var mesh = options.AddRouteMesh("services").Listen("tcp://127.0.0.1:7101");
    // This process handles the "greeting" channel. An IZLinkRequestHandler<,>
    // class registers on the channel builder; AddHandlersFromAssemblyOf wires
    // only attribute-declared handlers.
    mesh.Channel("greeting").Server().AddRequestHandler<HelloHandler, Hello, Greeting>();
});

var app = builder.Build();
await app.RunAsync();

// Handles one request on the "greeting" channel.
public sealed class HelloHandler : IZLinkRequestHandler<Hello, Greeting>
{
    public ValueTask<Greeting> HandleAsync(
        Hello request,
        IZLinkMessageContext context,
        CancellationToken cancellationToken
    ) => ValueTask.FromResult(new Greeting($"hello, {request.Name}"));
}
Server/Server.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <IsPackable>false</IsPackable>
    <RootNamespace>QuickStart.Server</RootNamespace>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Zlink.Framework" />
    <PackageReference Include="Zlink.Framework.AspNetCore" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="../Shared/Shared.csproj" />
  </ItemGroup>

</Project>

4. 호출하는 쪽

두 process를 같은 host에서 실행하므로 HTTP 포트를 각각 지정한다. 지정하지 않으면 Kestrel 기본 포트가 충돌한다.

Client/Program.cs
using QuickStart.Shared;
using Zlink.Framework.AspNetCore;
using Zlink.Framework.Contracts.Channels;

var builder = WebApplication.CreateBuilder(args);

// http://127.0.0.1:5080/hello/{name} is this process's own HTTP surface.
builder.WebHost.UseUrls("http://127.0.0.1:5080");

builder.Services.AddZLinkFramework(options =>
{
    // This process also needs its own endpoint.
    var mesh = options.AddRouteMesh("services").Listen("tcp://127.0.0.1:7102");
    // This side only calls; it does not handle "greeting".
    mesh.Channel("greeting").Client();
    // Manual connection — the server's endpoint is given directly.
    mesh.PeerConnections.Connect("tcp://127.0.0.1:7101");
});

var app = builder.Build();

app.MapGet(
    "/hello/{name}",
    async (string name, IZLinkRouteClient route, CancellationToken cancellationToken) =>
    {
        // The target is a single ChannelName; which node handles it is not specified.
        var reply = await route
            .RequestToChannel("greeting", new Hello(name))
            .Async<Greeting>(cancellationToken);

        return Results.Ok(reply.Text);
    }
);

await app.RunAsync();

5. 실행

cd zlink-dotnet-examples/quickstart
dotnet build

# 터미널 두 개. server를 먼저 실행한다.
dotnet run --project Server/Server.csproj
dotnet run --project Client/Client.csproj

curl http://127.0.0.1:5080/hello/world

응답은 "hello, world", 상태 코드 200이다.

6. 첫 실행이 안 될 때 확인할 항목

증상 확인할 항목
package를 찾지 못한다 §1의 package 이름을 그대로 적었는지 확인한다. binding 버전은 따로 고정하지 않는다
startup이 실패한다 두 process의 mesh 이름이 같은지, listen endpoint가 다른 process와 겹치지 않는지 확인한다
호출이 대상 없음으로 끝난다 받는 쪽이 그 channel 이름을 server 역할로 등록했는지, 두 process가 peer로 연결됐는지 확인한다
응답이 오지 않는다 보낸 쪽이 request를 썼는지 확인한다. send는 응답을 받지 않는다

7. 옮겨 갈 것

파일 내용
Directory.Packages.props PackageVersion 두 항목. binding은 전이 의존으로 둔다
Shared/Contracts.cs record 기반 메시지 정의
Server/Program.cs AddRouteMesh → Listen → Channel(...).Server().AddRequestHandler<...>()
Client/Program.cs Channel(...).Client(), PeerConnections.Connect(...), RequestToChannel(...).Async<T>()

8. 다음으로 읽을 것

이 두 process는 endpoint를 서로 직접 적어 연결한다. 서버를 늘리거나 다른 주소로 다시 시작해도 호출 코드를 그대로 두려면 자동 연결이 필요하고, 그것은 Location이 다룬다.