[Unity DarkRift2 - clustering] 2. Darkrift2 - linux 설치

Changhoony·2022년 8월 9일
0

Unity_DarkRift2_Cluster

목록 보기
2/4

* 지난이야기

지난 이야기를 마무리하면서, consul을 구성했다. consul이 잘 작동한다면 절반의 성공을 거뒀다고 보면 된다. 이제 DarkRift2를 리눅스에서 실행하는 법과 서비스를 consul에 등록해서 DarkRift2 서버끼리 통신하는 법만 남았다.

* 리눅스용 실행 콘솔

공식 가이드를 제공하긴 하지만.. 깃허브에서 제공하는 가이드는

dotnet DarkRift.Server.Console.dll

이 부분만 중요하다. 왜냐하면 어차피 DarkRift2 소스가 무료로 모두 공개 되었기 때문에 빌드하면 되기 떄문이다.

우선 기존의 DarkRift.Consoel.exe 는 .Net FrameWork로 빌드 되었다. 그래서 리눅스에서 별 짓을 해도 잘 되지 않는다.

따라서 우리는 DarkRift.Console을 dll로 뽑아내서 리눅스에서 실행할 것이다.

1. 준비하기

sudo apt-get update && \
  sudo apt-get install -y dotnet-sdk-6.0

터미널을 열고 이렇게 .Net Core가 설치가 된다. 실행하려면 donet 명령어가 필요하다.

  • 빌드는 윈도우에서 해서 넘겨주는게 속편하다.

솔루션 뷰어에서 / DakrRift / DarkRift.Client / DarkRift.Server / DarkRift.Server.Console / 이렇게 네 개의 프로젝트를 제외하고 마우스 오른쪽 키로 Exclude 하자. 네개만 있으면 빌드가 된다.

라이더를 사용하고 있지만, 비쥬얼 스튜디오도 프로젝트를 마우스 오른쪽 키로 눌러 속성 탭으로 들어가자. 그리고 애플리케이션 탭을 눌러보자. 출력형식과 대상 프레임워크를 닷넷으로 설정해주자. 코어 버전은 여러 개해도 되고 하나만 해도 된다. 그러나 프레임워크는 닷넷 코어로 해야한다. 꼭

그리고 그냥 빌드를 새로 뽑으면 된다.
\DarkRift.Server.Console\bin\Debug\net6.0 폴더를 DarkRift.Server.Console.zip 이런식으로 압축해주고 리눅스에 던져주자.

2. Server.config / Cluster.config 설정

ㄱ. server.config

빌드를 새로 했다면, 안에 server.config 파일이 있을 것이고 cluster.config는 없을 것이다.

나중에 cluster.config 파일만 따로 하나 만들어 주자.

우선 server.config 작성 부분이다.

  1. 아래를 찾는다.
....
<server maxStrikes="3" />
....
  1. 이 부분을 다음과 같이 바꾼다.
<server maxStrikes="3" serverGroup ="groupName"/>

	<serverRegistry advertisedHost="abc.def.ghi.jkl">
		<serverRegistryConnector type="ConsulServerRegistryConnector">
			<settings consulAddress="http://abc.def.ghi.jkl:8500" serviceName ="service" />
		</serverRegistryConnector>
	</serverRegistry>
  • 이 부분을 바꾸면, DarkRift2가 실행될 때, ConsulServerRegistryConnector class를 찾는다. 그러므로 추후에 서버를 빌드 할 때, 커넥터를 같은 서버 프로젝트 안에 포함시켜야 한다.

  • groupName은 후술할 cluster.config 에 포함된 이름이어야 한다. 내가 지금 켜고자 하는 서버의 종류에 맞는 이름을 넣어둔다.

  • advertiseHost는 현재 DarkRift2 서버의 아이피를 입력한다. server.config 파일 아래를 살펴보면 내 서버 adress가 있다. 알맞게 바꿔주자.

<listener name="DefaultNetworkListener" type="BichannelListener" address="0.0.0.0" port="4296">
  1. 마지막으로 아래를 찾는다.
...
<plugins loadByDefault="true">
...
  1. 이 부분을 다음과 같이 바꾼다.
...
<plugins loadByDefault="true">
    <plugin type="HttpHealthCheck" load="true">
      <settings
        port="wxyz"
        host="abc.def.hij.klm"
        path="/health" />
      </plugin>
  </plugins>
...
  • 이렇게 함으로써, 서버 아이피 하나에 여러 포트를 구성해서 DarkRift2 서버를 실행할 수 있다. VM 여러 개를 쓸 필요가 없어진다. 단지 host는 address를 그대로 적어주지만, port는 각 서버에 맞게 다르게 할당하여야 하며, 내 서버 포트와 다르게 설정한다.

  • 예시는 다음과 같다.

    <plugins loadByDefault="true">
      <plugin type="HttpHealthCheck" load="true">
        <settings
          port="6245"
          host="localhost"
          path="/health" />
        </plugin>
    </plugins>
    
    <!--
      Defines the settings for storing server data.
    -->
    <data directory="Data/"/>
    
    <!--
      Defines the listeners that will be loaded by the server.
    -->
    <listeners>
      <listener name="LogicServerListener" type="BichannelListener" address="127.0.0.1" port="5500">
        <settings noDelay="true" />
      </listener>
    </listeners>

ㄴ. cluster.config

  • 중요한 점
    * Group 끼리의 관계 : 암나사와 수나사의 역할은 각각 누가?
    • Group 이름의 중요성 : server.config에서 설정한 이름과 같아야 한다.
      <server maxStrikes="3" serverGroup ="groupName"/>

    작성법 - 예시

<?xml version="1.0" encoding="utf-8" ?>

<cluster>
	<groups>
		<group name="group1" visibility="external">
			<connectsTo name="group2" />
		</group>

		<group name="group2" visibility="internal" />
        <group name="group3" visibility="internal" />
	</groups>
</cluster>
  • 비유하자면, external은 플러그와 같고, internal은 멀티탭과 같다. external인 경우에만 추가로 설정한다.
<connectsTo name="otherGroupName" />
  • group1인 서버와, group2 와 3인 서버를 잘 구별해서 현재 실행하는 서버에 맞게 설정하고, server.config에도 내가 적용할 서버이름과 같은지 재차 확인한다.

3. DarkRift - Consul 커넥터 작성

마지막으로 C# 코딩을 통해, 현재 다크리프트 서비스를 consul에 등록하는 절차가 남았다.

<serverRegistryConnector type="ConsulServerRegistryConnector">

이 부분에서 ConsulServerRegistryConnector 대신에 다른 타입을 써도 상관 없지만, 개발진에서 제공하는 기본 스크립트가 있기 때문에 굳이 수정할 필요는 없다.

이 스크립트에서 namespace를 지우고 , consul.dll은 NuGet에서 받아서 사용한다.

  • consul.dll을 C#으로 활용한 예시가 있다. 좀 더 공부하고 싶은 사람들은 따로 여기서 확인해보자. 예제는 다음과 같으며, DarkRift2에서 제공하는 예제와도 거의 같다.

작성 예시

  1. ConsulServerRegistryConnector를 열어보면, 다음을 찾을 수 있다.

수정 전

private readonly string healthCheckUrl = "http://localhost:10666/health";

여기서 localhost는 server.config에서 작성했던 부분과 같게 적는다.

<plugins loadByDefault="true">
    <plugin type="HttpHealthCheck" load="true">
      <settings
        port="wxyz"
        host="abc.def.hij.klm"
        path="/health" />
      </plugin>
  </plugins>

수정 후

private readonly string healthCheckUrl = "http://abc.def.hij.klm:wxyz/health";

굳이 여러 아이피를 둘 필요가 없이, port 부분만 바꿔주면 하나의 아이피에서 여러 콘솔을 실행할 수 있다.

  1. 다음으로 AgentServiceCheck 와 AgentServiceRegistration을 찾는다.
AgentServiceCheck healthCheck = new AgentServiceCheck()
{
    HTTP = healthCheckUrl,
    Interval = healthCheckPollInterval,
    DeregisterCriticalServiceAfter = healthCheckTimeout
};
AgentServiceRegistration service = new AgentServiceRegistration
{
    ID = id.ToString(),
    Name = serviceName,
    Address = host,
    Port = port,
    Tags = new string[] { "group:" + group },
    Meta = properties,
    Check = healthCheck,
};

host와 port는 그냥 두면 알아서 현재 서버의 아이피와 포트를 가져다 입력한다. 여기서 중요한 부분은 아래와 같다.

  • Name
  • Tags

먼저 Name의 경우 server.config에서 작성한 다음 부분을 살펴보자.

...
<settings consulAddress="http://abc.def.ghi.jkl:8500" serviceName ="service" />
...

serviceName 이라는 항목이 있다. 이 항목과 위 스크립트의 Name이 같게 설정되어야 한다.

그리고 Tags도 중요하다. group은 clust.config와 server.config에서 설정했던 부분이다. group으로 잡아두면 알아서 입력하지만 group 앞에 "group:" 이 반드시 포함되어야 한다.

그리고 이 설정된 service와 healthCheck는 해당 스크립트 client.Agent.ServiceRegister(service);에서 실행된다.

try
{
    await client.Agent.ServiceRegister(service);
}
catch (Exception e)
{
    Logger.Error("Failed to register server with Consul as an exception occurred.", e);
    throw e;
}

이후 , http://bind_addr:8500 에 들어가서 Service 와 Node 탭을 확인해 보면 알맞게 적용 됨을 알 수 있다.

예시 - 5개의 서비스(서버) 연결

여기까지 잘 따라왔다면, 95%정도 되었다.

ㄷ. 서버에서 메시지 주고 받기 : 공식 가이드

서버 실행 방법은 다음과 같다.

...
cd Foo/MyServerConsoleDll
dotnet DarkRift.Server.Console.dll
...
  • DarkRift.Server.Console.dll을 빌드한 위치로 이동한다.
  • 해당 폴더 내의 DarkRift.Server.Console.dll을 실행한다.
  • 만약 에러가 발생하거나 별도의 dll을 사용할 경우 Plugins 폴더를 하나 만들어서 넣는다.
  1. RemoteManager.GetAllGroups()로 모든 그룹 정보를 가져 온다.
  2. foreach문으로 RemoteManager.GetAllGroups()의 서버 정보들을 탐색한다.
foreach(var group in RemoteManager.GetAllGroups())
{
	group.ServerJoined += OnServerJoined;
    group.ServerLeft += OnServerLeft;
}
  1. group.ServerJoined += OnServerJoined; 와 group.ServerLeft += OnServerLeft; 를 작성한다.
private void OnServerJoined(object? sender, ServerJoinedEventArgs e) 
{ 
	e.RemoteServer.ServerConnected += OnServerConnected;
    e.RemoteServer.ServerDisconnected += OnServerDisconnected;
}
private void OnServerLeft(object? sender, ServerLeftEventArgs e)
{
	e.RemoteServer.ServerConnected -= OnServerConnected;
    e.RemoteServer.ServerDisconnected -= OnServerDisconnected;
}
  1. OnServerJoined와 OnServerLeft함수로 각 원격서버에 e.RemoteServer.ServerConnected += OnServerConnected 와 e.RemoteServer.ServerDisconnected += OnServerDisconnected 를 구현한다.
  2. e.RemoteServer.ServerConnected 함수에서 e.RemoteServer.MessageReceived += OnServerMessagReceived;를 구현한다.
private void OnServerConnected(object? sender, ServerConnectedEventArgs e)
{
	e.RemoteServer.MessageReceived += OnServerMessageReceived;
}

private void OnServerDisconnected(object? sender, ServerDisconnectedEventArgs e)
{
	e.RemoteServer.MessageReceived -= OnServerMessageReceived;
}

이때의 e.RemoteServer는 연결된 원격 서버로 예를들어 위의 clust.config를 중심으로 설명하자면

Group1의 e.RemoteServer 는 Group2 이고
Group2의 e.RemoteServer 는 Group1 이다.

  1. SendMessageToServer 와 ReceiveMessageFromServer 주는 메시지와 받은 메시지를 각각 따로 처리한다. 이 부분은 DarkRift2로 클라이언트와 메시지를 주고 받는 부분과 같다.

ex. Write - DarkRiftWriter

using(DarkRiftWriter writer = DarkRiftWriter.Create())
{
	...어쩌구저쩌구.. 보낼 메시지를 정리한다.
    
    using (Message message = message.Create((ushort)Tag.Tags.Foo, writer))
    	e.RemoteServer.SendMessage(msg, SendMode.Reliable);
}

ex. Read - DarkRiftReader

using(Message message = e.GetMessage())
{	   
    using (DarkRiftReader reader = message.GetReader())
    {
    	var tag = reader.ReadUInt16();
        var str = reader.ReadString();
    }
}
  • 여기서 주의할 부분은 Write 에서 Tag가 없더라도 0 의 값으로 보내야 한다. 받을 때는 , 태그가 지정되어 있지 않더라도 ushort를 먼저 받아야 한다.

여기까지가 DarkRift2 - cluster 기능 가이드다. 이제는 무료가 된 에셋인 DarkRift2를 잘 활용해보자.

profile
Unity 개발

0개의 댓글