Closed alkee-allm closed 4 years ago
각 서버의 내부 ip 는 최초 입력된 ServerManagement backend url 을 통해 요청받은 backend 에서 ip 를 확인할 수 있겠으나, 외부 ip 가 필요한 경우 이를 알아내는 별도의 방법이 필요하다.
특히나 game server 는 matching 결과에 따라 해당 서버로 유저를 접속시켜야 하기때문에 유저가 직접 접속할 수 있는 endpoint 가 필요하기 떄문.
container(docker)로 실행이 되는 경우, 결국 service TCP port 를 스스로의 힘으로 알 수 없는 이유와 마찬가지로, 외부 환경(ip-port 등)은 host 로부터 환경변수 등으로 전달받아 이를 사용하는 방식으로 진행하는 것이 가장 단순할 것.
외부로부터 받은 ip(frontend address) 정보를 사용하기 위해 #28 문제가 우선 수정되어야 할 것.
backend - frontend 가 하나의 서비스 위에서 동작하기 때문에 routing 시에 서로다른 주소로만 연결될 수 있도록 장치(filter)가 필요하다.
grpc 는 별도의 routing filter 를 갖지 않기때문에 제공하는 Interceptor를 이용해야할 듯.
Interceptor 의 virtual 함수 중 UnaryServerHandler
가 서버 method 가 호출되는 경우 발생.
내부 network 에서의 호출인지 어떻게 판단하지?
context.Host
및 context.Peer
가 string 으로만 제공되기때문에, dns 사용여부 ipv4, ipv6 등의 차이 등등, 이를 이용해 내부 연결인지 확인하기가 매우 어렵다. header 에 server backend 전용 token 을 삽입하고 이를 검증하는 방식이 합리적일 것.
이렇게 하려면, backend 함수 호출시 개별적으로 호출하던 방식을 통합해 header 를 사용하도록 refactoring 되어야 한다.
public override async Task<Null> Broadacast(BroadacastRequest request, ServerCallContext context)
{
using var channel = Grpc.Net.Client.GrpcChannel.ForAddress(config.ServerManagementBackendAddress);
var client = new K2B.ServerManagement.ServerManagementClient(channel);
await client.BroadcastAsync(new K2B.PushRequest
...
각 service 별로 generated 된 stub 를 가지고 있고, 이들 각각 가지고 있는 method 들을 실행하는 구조이기 때문에 단순히 묶기(generalize)가 어렵다.
channel 과 stub 가 thread-safe 하다니 이들만이라도 재사용하도록 해볼까 생각했으나, PushBackend
와 같이 고정되지 않은 channel 들을 사용하는 경우가 많아 이역시 간단하지 않아보인다.
재사용이 가능하다 하더라도, channel 에 header 를 붙이기 위해서 channel credential 을 확정(insecure or ssl)해야 해 어려움이 있고, 그렇지 않으면 매 client call 마다 meta 를 설정해야할텐데, 이 역시 문제가 있다.
일단 header 를 확인하는 방법은, interceptor 를 grpc 에 추가하고
services.AddGrpc(options =>
{
options.Interceptors.Add<Filter.BackendValidator>();
});
UnaryServerHandler 에서 header(MetaData.Entry) 를 검사
public override Task<TResponse> UnaryServerHandler<TRequest, TResponse>(TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
{ // Server 의 handler 가 호출될 때
if (IsBackendMethod(context.Method) == false) return base.UnaryServerHandler(request, context, continuation); // not interested
if (config.BackendGroupId == context.RequestHeaders.FirstOrDefault((me) => me.Key == "BackendGroupId")?.Value)
return base.UnaryServerHandler(request, context, continuation);
throw new ApplicationException("invalid backend request");
}
지저분하지만, Unity 작업(#24)에서와 마찬가지로 stub method 를 호출할때마다 header 를 parameter 로 전달. 추후에 #30 과 함께 정리가 될 수 있을 것으로 기대.
최초에 생각했던 방식으로는, 서버가 시작할 때 정보(configuration)는 아무것도 가지고있지 않고 시작하고 ServerManagementServer(이하 관리서버) 에서 모든 정보(configuration)를 구성해 전달하면 이를 서버가 사용하는 방식.
관리서버가 정책에 따라 직접 Server 를 띄우는(container 생성 및 run) 상황이 아니라면, 상당히 비효율적일 것이라 생각됨.
따라서.. 관리서버는 띄워지는 서버가 요청하는 역할에 따라 서버들에 정보를 제공하고 서버들을 모니터링 하는 수준으로 한정하는 것이 좋겠다. 즉, 실제 서비스를 띄우는 시점에 띄울 서버에 직접적인 설정으로 서버 역할을 지정.
다만, 서버의 scale-out / in 시에 서버마다 해야하는 설정은 최소화(자동화 가능한 수준)하면 될 것. 심지어 K2 에서는 게임서버의 scale-in/out(container 의 생성 및 삭제 관리)이 빈번할 것으로 예상되는데 이는 이 프로젝트의 영역이 아니다. 따라서 이 project(out game server)의 scale out 이 빈번하지 않을 것이라 예상되어 오래 고민하고 복잡한 구조를 가져갈 필요성이 매우 낮다.
서버통계를 제공하는 방법이나, 게임서버의 컨트롤(서버 스위칭 및 분산 연결)등은 이후 별도의 issue 에서 다루도록 할 것.
https://github.com/alkee-allm/k2proto/blob/8185bce462fdbce34dbe4d6540fb3ce2dbfd700e/K2svc/Backend/ServerManagementService.cs#L27-L37
현재 임시코드 부분을 실제 동작하도록 서버-관리 기능을 추가한다