GameFlexMatch开发者对接指南(C#版本)
术语:
- 租户,表示使用华为云服务的企业用户
- 用户,表示租户应用的使用者
- GameFlexMatch服务,表示华为云提供的应用托管服务平台
- 租户管理面,表示租户与GameFlexMatch服务交互的应用,比如游戏大厅
- 租户托管应用,表示租户托管在GameFlexMatch服务平台上的应用,比如游戏应用
接口对接
接口对接分为两部分,一部分是租户管理面与GameFlexMatch服务的交互(通过RESTful API交互),一部分是与租户托管应用与GameFlexMatch服务的交互(通过集成SDK交互)整体流程如下:

接口认证
在调用所有的接口前,需要进行认证校验
- 构造登录请求,其中密码为RSA加密后的密文,加密的公钥需与fleetmanager部署的后端私钥保持一致,加密脚本可参考
/tools/cipher
POST URL: /v1/user/login
Request Body:
{
"username": "admin",
"password": "cipher-password"
}
Response Body:
{
"username": "admin",
"id": "63da950a-b3ea-11ed-bb27-fa163**********",
"Auth-Token": "eyJhbGciO*************eXrA",
"Activation": 1,
"UserType": 9,
"total_res_count": 1
}
- 将
Response中的Auth-Token字段以及字段值加入到请求体的header里,即可正常访问GameFlexMatch接口业务
租户管理面与GameFlexMatch服务的交互接口说明:
详细接口信息看API接口文档,下面对接口做一些说明:
-
CreateFleet 创建
fleet,这里可以通过"process_configuration"指定开机之后的启动路径和参数(这些参数是这个fleet每个虚拟机都可以拿到的参数) -
ShowFleet
fleet创建之后状态是Activating,需要通过ShowFleet查询改Fleet的状态,只有状态变成"active"之后才表示这个Fleet可用,才可以进行Server Session的创建 -
UpdateFleetInstanceCapacity
Fleet创建之后默认会启动一个虚拟机来启动进程,需要等Fleet的状态为active,通过这个接口可以修改指定Fleet的虚拟机最大值、最少值和期望值 -
UpdateFleet 该接口主要是修改
Fleet的属性,比如打开弹性伸缩,修改实例标签,会话保护策略与保护时长等 -
CreateScalingPolicy
Fleet创建之后默认不启动弹性伸缩,需要等Fleet的状态为active,调用updateFleet开启弹性伸缩,然后通过CreateScalingPolicy可以配置自己的弹性阈值 -
CreateServerSession 可以通过
CreateServerSession,给Fleet创建一个server session,如果该Fleet有可用的进程,该进程会接收到这个接口的参数 如果指定Fleet当前没有可用的虚拟机可用于分配,该接口会返回失败,如果开启了弹性策略的话,业务可以等待一段时间后再重试。 -
ShowServerSession 可以通过
ShowServerSession,获取指定ServerSession的连接信息,也可以为Server Session创建client session来获取连接信息,GameFlexMatch平台通过client session对连接做了细致管理,推荐使用 如果ServerSession还没active的话,访问地址会被隐藏 -
CreateClientSession 可以通过
CreateSession,给指定的Server Session创建一个client session,根据这个client session,不同的用户可以连接入服务器上,比如游戏里面,10个用户一局游戏,游戏局就是server session,而client session就表示了不同的用户,这些用户使用相同的服务器连接地址连接入托管服务器;创建client session之后,会有60s的有效期,超过这个时间之后client session会变为不可用 -
DeleteFleet 结束后,该接口是提供给用户做最后清理的,该接口可以清理所有指定
fleet的所有资源
租户托管应用与GameFlexMatch服务的交互API
- 托管应用的回调API(GameFlexMatch服务会调用的接口)列表如下,三个接口托管服务中需要根据自己的业务逻辑进行实现
| API Name | API Description |
|---|---|
| OnHealthCheck | 返回托管服务的健康状态,只有健康状态是true的应用,才会被分配到server session |
| OnStartServerSession | 接收到server session的创建信息,业务需要在这里执行server session的创建逻辑 |
| OnProcessTermiante | 接收到服务关闭的信息,业务需要在这里正确关闭进程,回收资源 |
- 托管应用的主调API(托管应用调用的GameFlexMatch接口)列表如下,
| API Name | API Description |
|---|---|
| ProcessReady | 注册托管应用进程信息,通知GameFlexMatch服务进程已经启动完成 |
| ActivateServerSession | 激活server session,表示相应的server session已经创建完毕,可以用于后续流程 |
| AcceptClientSession | client session已经连接成功 |
| RemoveClientSession | 终止client session |
| DescribeClientSessions | 获取指定server session的所有client session信息 |
| UpdateClientSessionCreationPolicy | 更新指定server session的client session创建策略,主要是是否允许新的client session接入 |
| TermianteGameServerSession | server session结束,终止响应的server session |
| PrcocessEnding | 进程回收工作完成,可以正常关闭进程 |
托管应用集成教程(c#)
-
grpc生成c# sdk代码 当前提供了c#与go语言的demo(详见:
demo/go与demo/csharp) -
在托管服务启动并确认自己可以提供服务,托管服务进程需要调用ProcessReady API 去通知GameFlexMatch平台自己已经启动完成,可以开始被分配server session。GameFlexMatch平台接受到通知后,会设置进程的状态为Activating,此时进程还不可用与分配server session。
public static AuxProxyResponse ProcessReady(string[] logPath, int clientPort, int grpcPort) { logger.Println($"Getting process ready, LogPath: {logPath}, ClientPort: {clientPort}, GrpcPort: {grpcPort}"); var req = new ProcessReadyRequest{ ClientPort = clientPort, GrpcPort = grpcPort, // pid是当前进程的id Pid = Process.GetCurrentProcess().Id, }; req.LogPathsToUpload.Add(logPath); //repeated类型解析pb后,是只读类型,需要Add加入 return GrpcClient.ScaseClient.ProcessReady(req, meta); } -
GameFlexMatch平台接收到ProcessReady通知后,会调用进程的onHealthCheck确认进程已进入ready状态,然后设置进程的状态为Active
// 对象需要继承ProcessGrpcSdkService.ProcessGrpcSdkServiceBase public class ServerSdk : ProcessGrpcSdkService.ProcessGrpcSdkServiceBase public override Task<HealthCheckResponse> OnHealthCheck(HealthCheckRequest request, ServerCallContext context) { logger.Println($"OnHealthCheck, HealthStatus: {ScaseManager.HealthStatus}"); return Task.FromResult(new HealthCheckResponse{ HealthStatus = ScaseManager.HealthStatus }); } -
租户管理面可以通过调用CreateServerSession API去创建server session,改server session会绑定到指定的fleet的某个托管应用进程上。GameFlexMatch平台接收到ServerSession创建请求后,会异步调用onStartServerSession API去通知托管应用进程,同时设置该server session为"Activating"状态
public override Task<ProcessResponse> OnStartServerSession(StartServerSessionRequest request, ServerCallContext context) { logger.Println($"OnStartServerSession, request: {request}"); ScaseManager.SetServerSession(request.ServerSession); return Task.FromResult(new ProcessResponse()); } -
托管应用进程收到onStartServerSession后,需要处理自己的业务,在所有的都处理完成后,需要调用ActivateServerSession API 去通知GameFlexMatch平台当前server session已经激活,GameFlexMatch会设置改server session状态为active
public static AuxProxyResponse ActivateServerSession(string serverSessionId, int maxClients)
{
logger.Println($"Activating game server session, ServerSessionId: {serverSessionId}, MaxClients: {maxClients}");
var req = new ActivateServerSessionRequest{
ServerSessionId = serverSessionId,
MaxClients = maxClients,
};
return GrpcClient.ScaseClient.ActivateServerSession(req, meta);
}
- 租户管理面通过CreateClientSession接口给用户获取连接信息,用户接入使用client session接入后,托管盈余公进程会调用AcceptClientSession去通知GameFlexMatch平台当前client session已经接入,GameFlexMatch平台会设置改client session状态为active;如果client session创建60秒后都没有接入的话,状态会转变为timeout,该client session不可再用
public static AuxProxyResponse AcceptClientSession(string ClientSessionId)
{
logger.Println($"Accepting Client session, ClientSessionId: {ClientSessionId}");
var req = new AcceptClientSessionRequest{
ServerSessionId = serverSession.ServerSessionId,
ClientSessionId = ClientSessionId,
};
return GrpcClient.ScaseClient.AcceptClientSession(req, meta);
}
- 在用户断开连接后,托管应用进程需要调用RemoveClientSession API 去移除该用户,GameFlexMatch平台会将相应的client session设置为"complete"状态,并回收该配额
public static AuxProxyResponse RemoveClientSession(string ClientSessionId)
{
logger.Println($"Removing Client session, ClientSessionId: {ClientSessionId}");
var req = new RemoveClientSessionRequest{
ServerSessionId = serverSession.ServerSessionId,
ClientSessionId = ClientSessionId,
};
return GrpcClient.ScaseClient.RemoveClientSession(req, meta);
}
- 在一个server session结束之后,托管应用进程需要调用TermianteServerSession API 去通知GameFlexMatch平台将该server session状态设置为terminated
public static AuxProxyResponse TerminateServerSession()
{
logger.Println($"Terminating game server session, ServerSessionId: {serverSession.ServerSessionId}");
var req = new TerminateServerSessionRequest{
ServerSessionId = serverSession.ServerSessionId
};
return GrpcClient.ScaseClient.TerminateServerSession(req, meta);
}
- GameFlexMatch平台如果要关闭托管应用进程(比如租户删除fleet),会调用onProcessTermainte来通知托管应用进程进行资源回收并关闭进程(该过程并不会改变会话状态)
public override Task<ProcessResponse> OnProcessTerminate(ProcessTerminateRequest request, ServerCallContext context)
{
logger.Println($"OnProcessTerminate, request: {request}");
// 设置进程终止时间
ScaseManager.SetTerminationTime(request.TerminationTime);
// 终止游戏服务器会话
ScaseManager.TerminateServerSession();
// 进程退出
ScaseManager.ProcessEnding();
return Task.FromResult(new ProcessResponse());
}
- 托管应用进程关闭之前需要调用ProcessEnding来通知GameFlexMatch平台将自身的process对象状态设置为Termianted
public static AuxProxyResponse ProcessEnding()
{
logger.Println($"Process ending, pid: {pid}");
var req = new ProcessEndingRequest();
return GrpcClient.ScaseClient.ProcessEnding(req, meta);
}
- 托管应用进程可以根据业务需要调用DescribeClientSessions来获取指定server session的全部client session信息
public static DescribeClientSessionsResponse DescribeClientSessions(
string ServerSessionId, string ClientId, string ClientSessionId, string ClientSessionStatusFilter, string nextToken, int limit)
{
logger.Println($"Describing Client session, ServerSessionId: {ServerSessionId}, \
ClientId: {ClientId}, ClientSessionId: {ClientSessionId}, \
ClientSessionStatusFilter: {ClientSessionStatusFilter}, \
NextToken: {nextToken}, Limit: {limit}");
var req = new DescribeClientSessionsRequest{
ServerSessionId = ServerSessionId,
ClientId = ClientId,
ClientSessionId = ClientSessionId,
ClientSessionStatusFilter = ClientSessionStatusFilter,
NextToken = nextToken,
Limit = limit,
};
return GrpcClient.ScaseClient.DescribeClientSessions(req, meta);
}
- 托管应用进程可以根据业务需要来调用UpdateClientSessionCreationPolicy接口来更新client session的创建策略(是否允许新用户接入到当前server session)
public static AuxProxyResponse UpdateClientSessionCreationPolicy(string newPolicy)
{
logger.Println($"Updating Client session creation policy, newPolicy: {newPolicy}");
var req = new UpdateClientSessionCreationPolicyRequest{
ServerSessionId = serverSession.ServerSessionId,
NewClientSessionCreationPolicy = newPolicy,
};
return GrpcClient.ScaseClient.UpdateClientSessionCreationPolicy(req, meta);
}
- 启动grpc服务
public class Program
{
public static int ClientPort = PortServer.GenerateRandomPort(2000, 6000);
public static int GrpcPort = PortServer.GenerateRandomPort(6001, 10000);
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>{
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(options =>
{
// gRPC Port (Setup a HTTP/2 endpoint without TLS.)
options.ListenAnyIP(GrpcPort, o => o.Protocols =
HttpProtocols.Http2);
// HTTP Port
options.ListenAnyIP(ClientPort);
});
webBuilder.UseStartup<Startup>();
});
}
}
- 连接GameFlexMatch平台的grpc 服务
public class GrpcClient
{
// 60002端口为GameFlexMatch grpc服务的启动端口
private static string agentAdress = "127.0.0.1:60002";
public static ProcessGrpcSdkService.ProcessGrpcSdkServiceClient ProcessServerClient
{
get
{
Channel channel = new Channel(agentAdress, ChannelCredentials.Insecure);
return new ProcessGrpcSdkService.ProcessGrpcSdkServiceClient(channel);
}
}
public static ScaseGrpcSdkService.ScaseGrpcSdkServiceClient ScaseClient
{
get
{
Channel channel = new Channel(agentAdress, ChannelCredentials.Insecure);
return new ScaseGrpcSdkService.ScaseGrpcSdkServiceClient(channel);
}
}
}