Virtual Server
1. Overview
Virtual Server is an Extension service offered by PGOS, allowing developers to expand the functionality and implement customized features through code writing and deploy third-party software to run. The essence of a Virtual Server is that developers create web servers, which are then deployed and run on the container runtime platform provided by PGOS. To orchestrate and manage the virtual servers written by developers, PGOS uses the Knative framework and ensures isolation between Virtual Servers under different TitleRegions.
Virtual Server offers the following fundamental features:
- Language transparency: Developers can use any programming language to build their virtual server.
- Backend HTTP API: Developers can call PGOS backend APIs within their code. Sample code is provided for assembling the correct requests using Golang, Node.js, and Python.
- Autoscaling: CPU and memory usage can be set for each virtual server instance, which can hold multiple instances based on actual load and needs.
- Triggers: Virtual servers can be triggered from game clients and dedicated servers and can also be bound with PGOS events for seamless integration.
- CI/CD: Developers can upload source code, allowing PGOS to build Docker images. Alternatively, developers can upload zip files containing exported Docker images or provide a public Docker repository address for PGOS to pull images and create a Virtual Server accordingly.
- Data visualization: PGOS can generate graphs depicting CPU, memory, and instance count trends, as well as overall request Round-Trip Time (RTT) and Queries Per Second (QPS) statistics. Moreover, it can display the distribution of RTT API response times in histograms.
- Log collection: PGOS can store, index, and analyze logs printed on the console shell by Virtual Servers, organized by revision. This enables efficient log management and tracking for developers. Especially, the access log is supported in Nginx default log format.
- VS Storage: Access databases and cloud disk in your Virtual Server.
- Intranet Communication: The Virtual Server can communicate with each other internally by the intranet
- Access in browser: You can visit your Virtual Server in Browser directly. Note that you can do it only through the PGOS Portal.
2. Create and Manage Virtual Server
To create a Virtual Server, a docker image needs to be prepared. This can be done in three ways:
- Export your docker image to a zip file and upload on web portal by using
docker save
command. - Upload your source code that contains
Dockerfile
and PGOS will do the CI job for you. - Submit a public docker registry path so PGOS is able to pull the image.
Then navigate to Virtual Server page, and click the "+ Add Virtual Server" button.
These fields are required:
Server Name: a name that can only contain uppercase and lowercase letters, numbers, and hyphens (-).
Resource Type & Pull Method: select how you want to build your docker image.
Source Code with Dockerfile: let PGOS build it for you, and you must upload the source code with Dockerfile in the root folder.
Docker Image from local file: upload your docker image already built, which can be exported using the docker save command.
Docker Image pull from the registry: let PGOS fetch the image from a public registry.
Specification per Instance & Max Instances:
The smallest running unit of a Virtual Server is referred to as a Pod, where each Pod can have its CPU and memory usage configured. Virtual Server supports automatic scaling, allowing you to customize the maximum number of instances running concurrently.
You can allocate the required CPU and memory size based on your program's needs, considering whether it is computation-intensive or space-intensive. This flexibility allows for optimal resource management tailored to your application's requirements.
Listen Port: since Virtual Server is essentially a Web Server, a port is needed to listen to HTTP requests. It's worth noted it is only the internal listen port inside the pod. If you access the virtual server outside such as in the browser or Http API, the port will be 443.
Persistent Volume: The disk that is mounted to the virtual server. You should create one in the
Virtual Server Storage
page.Environment Variables: Your program can access environment variables, enabling it to adapt and respond to different configurations and runtime conditions effectively.
Launch Args: The arguments for the Virtual Server launching.
Visibility: Virtual Server can be accessed from both the game client, backend http API and dedicated server. For safety issues, you can choose to block requests from the client side.
After a Virtual Server is created, you can inspect the deployment status:
Under normal circumstances, the Building Status will first attempt to build the Docker image, and the Running Status will initially display 'Deploying.' Once the image is built successfully, the Running Status transitions to 'Running.' However, if a fatal error occurs during the image startup or if it fails to launch properly, the status will be 'Exception.'
Common reasons for Virtual Server failing to start may include:
- Listening port number in program is inconsistent with the one in the configuration.
- The IP address the server listens to should be
0.0.0.0
instead of127.0.0.1
- A program crash.
- The provided image address is unreachable for retrieval, among other issues.
- Insufficient memory to launch space-intensive programs.
When a Virtual Server remains in a low-load state for an extended period, the minimum number of instances will be reduced to 1, continuing to consume CPU and memory resources. If you wish to minimize resource usage during the development phase, you can click the 'Stop' button on the page to halt Virtual Server instances and fully reclaim all resources. When you need to run the Virtual Server again, simply clicking the 'Start' button will quickly create it back.
3. Virtual Server Environment Variables
The environment variables of Virtual Server are set when creating the Virtual Server, which can be accessed during the Virtual Server runtime to access different configurations and runtime environments. We can also modify them later. The environment variables of the current PGOS Virtual Server come from two sources:
- User-defined environment variables.
- System preset environment variables.
3.1 User-defined environment variables
When creating or updating a Virtual Server, you can set environment variables, which can be accessed during the Virtual Server runtime to access different configurations and runtime environments. The setting of environment variables is as follows:
3.2 System preset environment variables
System preset environment variables are set by the system for the current Title Region, which can be accessed during the Virtual Server runtime to access different configurations and runtime environments. The preset environment variables are as follows:
Environment Variables | Description | Example |
---|---|---|
PGOS_Title_ID | The ID of the current Title | 5tcsa |
PGOS_Title_Name | The name of the current Title | PGOS Test Game |
PGOS_Title_Logo | The logo of the current Title | https://pgos-1303878176.cos.eu-frankfurt.myqcloud.com/logo/1629706971800-sh.png |
PGOS_Title_Region_ID | The ID of the current Title Region | euff_5tcsa_812179_dev |
PGOS_Title_Region_Name | The name of the current Title Region | PGOS Dev Title Region |
PGOS_Title_Region_Type | The type of the current Title Region | dev, test, release |
PGOS_Title_Region_Cloud_Provider | The cloud provider of the current Title Region | tcloud, aws |
PGOS_Title_Region_Backend_API_Domain | The domain of the backend API of the current Title Region | euff.server.pgos.intlgame.com |
PGOS_Title_Region_CLI_API_Domain | The domain of the CLI API of the current Title Region | euff.pgos.intlgame.com |
PGOS_VS_Name | The name of the current Virtual Server | PGOS-Test-Virtual-Server-001 |
PGOS_VS_Listen_Port | The listen port of the current Virtual Server | 8080 |
PGOS_VS_Storage_<Storage Name> | The configuration of the Virtual Server Storage | PGOS provides Redis and MongoDB storage, each with different configuration information, as follows. |
3.2.1 Redis Environment Variable
If we have a Redis storage named "RedisA", we can access the configuration information of this storage through the environment variable PGOS_VS_Storage_RedisA
. The configuration information of the Redis storage is as follows:
PGOS_VS_Storage_RedisA = '{"type":"Redis","version":"5.2.0","name":"RedisA","memory":1,"network":[{"port":6379,"inner_ip":"172.21.0.81"}],"password":"TVCND0FHGk4jE8x7","instanceId":"crs-9vnjdg8g","clusterType":"Standard","shardNumber":0,"masterSlaveNumber":1}',
Environment variable value is a JSON string, which contains the configuration information of the Redis storage, as follows:
{
"type": "Redis",
"version": "5.2.0",
"name": "RedisA",
"memory": 1,
"network": [
{
"port": 6379,
"inner_ip": "172.21.0.0"
}
],
"password": "xxxxx",
"instanceId": "crs-9vnjdg8g",
"clusterType": "Standard",
"shardNumber": 0,
"masterSlaveNumber": 1
}
- type: Storage type, which is Redis
- version: Redis version
- name: Redis name
- memory: Redis memory size
- network: Redis network configuration, including port and inner IP
- password: Redis password
- instanceId: Redis instance ID
- clusterType: Redis cluster type
- shardNumber: Redis shard number
- masterSlaveNumber: Redis master-slave number
3.2.2 MongoDB Environment Variable
If we have a MongoDB storage named "MongoDBA", we can access the configuration information of this storage through the environment variable PGOS_VS_Storage_MongoDBA
. The configuration information of the MongoDB storage is as follows:
PGOS_VS_Storage_MongoDBA = '{"type":"MongoDB","version":"5.0.0","cpu":2,"disk":100,"name":"MongoDBA","memory":4,"account":"mongouser","network":[{"port":27017,"role":"Primary","inner_ip":"172.21.0.42"},{"port":27017,"role":"Secondary","inner_ip":"172.21.0.116"},{"port":27017,"role":"Secondary","inner_ip":"172.21.0.145"}],"password":"2bnUyqZ3fbgUxM4T","mongosCpu":0,"instanceId":"cmgo-rgde6vl5","replicaSet":"cmgo-rgde6vl5_0","clusterType":"Standard","shardNumber":0,"mongosMemory":0,"mongosNodeNum":0,"masterSlaveNumber":3}'
Environment variable value is a JSON string, which contains the configuration information of the MongoDB storage, as follows:
{
"type": "MongoDB",
"version": "5.0.0",
"cpu": 2,
"disk": 100,
"name": "MongoDBA",
"memory": 4,
"account": "mongouser",
"network": [
{
"port": 27017,
"role": "Primary",
"inner_ip": "172.21.0.1"
},
{
"port": 27017,
"role": "Secondary",
"inner_ip": "172.21.0.2"
},
{
"port": 27017,
"role": "Secondary",
"inner_ip": "172.21.0.3"
}
],
"password": "xxxx",
"mongosCpu": 0,
"instanceId": "cmgo-rgde6vl5",
"replicaSet": "cmgo-rgde6vl5_0",
"clusterType": "Standard",
"shardNumber": 0,
"mongosMemory": 0,
"mongosNodeNum": 0,
"masterSlaveNumber": 3
}
- type: Storage type, which is MongoDB
- version: MongoDB version
- cpu: MongoDB CPU cores
- disk: MongoDB disk size
- name: MongoDB name
- memory: MongoDB memory size
- account: MongoDB account
- network: MongoDB network configuration, including port, role, and inner IP
- password: MongoDB password
- mongosCpu: MongoDB mongos CPU cores
- instanceId: MongoDB instance ID
- replicaSet: MongoDB replica set
- clusterType: MongoDB cluster type
- shardNumber: MongoDB shard number
- mongosMemory: MongoDB mongos memory size
- mongosNodeNum: MongoDB mongos node number
- masterSlaveNumber: MongoDB master-slave number
3.3 Access Environment Variables
When the Virtual Server is running, you can access the environment variables through the code. Different programming languages and frameworks have different ways to access them. Here is an example using NodeJS/Golang/Python to access the environment variables:
- Golang
- NodeJS
- Python
package main
import (
"fmt"
"os"
)
func main() {
// 1. Print all environment variables
for _, e := range os.Environ() {
fmt.Println(e)
}
// 2. Print specified environment variables
titleID := os.Getenv("PGOS_Title_ID")
titleName := os.Getenv("PGOS_Title_Name")
titleLogo := os.Getenv("PGOS_Title_Logo")
titleRegionID := os.Getenv("PGOS_Title_Region_ID")
titleRegionName := os.Getenv("PGOS_Title_Region_Name")
titleRegionType := os.Getenv("PGOS_Title_Region_Type")
titleRegionCloudProvider := os.Getenv("PGOS_Title_Region_Cloud_Provider")
titleRegionBackendAPIDomain := os.Getenv("PGOS_Title_Region_Backend_API_Domain")
titleRegionCLIAPIDomain := os.Getenv("PGOS_Title_Region_CLI_API_Domain")
vsName := os.Getenv("PGOS_VS_Name")
vsListenPort := os.Getenv("PGOS_VS_Listen_Port")
vsStorageRedis := os.Getenv("PGOS_VS_Storage_Redis")
vsStorageMongoDB := os.Getenv("PGOS_VS_Storage_MongoDB")
}
// 1. Print all environment variables
const env = process.env;
console.log('All Environment Variables are: ', env)
// 2. Print specified environment variables
const titleId = process.env.PGOS_Title_ID;
const titleName = process.env.PGOS_Title_Name;
const titleLogo = process.env.PGOS_Title_Logo;
const titleRegionId = process.env.PGOS_Title_Region_ID;
const titleRegionName = process.env.PGOS_Title_Region_Name;
const titleRegionType = process.env.PGOS_Title_Region_Type;
const titleRegionCloudProvider = process.env.PGOS_Title_Region_Cloud_Provider;
const titleRegionBackendAPIDomain = process.env.PGOS_Title_Region_Backend_API_Domain;
const titleRegionCLIAPIDomain = process.env.PGOS_Title_Region_CLI_API_Domain;
const vsName = process.env.PGOS_VS_Name;
const vsListenPort = process.env.PGOS_VS_Listen_Port;
const vsStorageRedis = process.env.PGOS_VS_Storage_Redis;
const vsStorageMongoDB = process.env.PGOS_VS_Storage_MongoDB;
import os
# 1. Print all environment variables
for key, value in os.environ.items():
print(f"{key}={value}")
# 2. Print specified environment variables
title_id = os.environ.get("PGOS_Title_ID")
title_name = os.environ.get("PGOS_Title_Name")
title_logo = os.environ.get("PGOS_Title_Logo")
title_region_id = os.environ.get("PGOS_Title_Region_ID")
title_region_name = os.environ.get("PGOS_Title_Region_Name")
title_region_type = os.environ.get("PGOS_Title_Region_Type")
title_region_cloud_provider = os.environ.get("PGOS_Title_Region_Cloud_Provider")
title_region_backend_api_domain = os.environ.get("PGOS_Title_Region_Backend_API_Domain")
title_region_cli_api_domain = os.environ.get("PGOS_Title_Region_CLI_API_Domain")
vs_name = os.environ.get("PGOS_VS_Name")
vs_listen_port = os.environ.get("PGOS_VS_Listen_Port")
vs_storage_redis = os.environ.get("PGOS_VS_Storage_Redis")
vs_storage_mongodb = os.environ.get("PGOS_VS_Storage_MongoDB")
4. Trigger Virtual Server
Virtual Server can interact with other services on PGOS, such as subscribing to Events, implementing scheduled Tasks, and integrating with the economic system's events. Developers can also access Virtual Server from the client-side or Dedicated Server (DS) using HTTP API, just like interacting with other PGOS services.
4.1 Triggered by PGOS Event
PGOS generates numerous Events, including player login/logout, room creation, room disbanding, player entry, and game session start/stop. You can write Virtual Servers to subscribe to these events and execute custom actions when the events are triggered.
For instructions on handling Events, please click here.
For structure definitions of all PGOS Events, please click here.
4.2 Triggered by Backend HTTP API
PGOS allows game backends access PGOS services by using HTTP API, which includes Virtual Server. You can fire a request to the Virtual Server directly by backend HTTP API, for example:
curl
-H "content-type: application/json" \
-H "TitleId: your_title_id" \
-H "TitleRegionId: your_titleregion_id" \
-H "SecretId: your_secret_id" \
-H "ServerTicket: your_server_ticket" \
-X POST -d "I am request body" -v http://your_titleregion_domain/virtualserver/invoke/vs-sample-python/backend_hello
NOTE: the SecretId is the first part of your Server Key, the ServerTicket is a ticket which is calculated by PGOS backend Http ticketing algorithm. You can also do it in your source code for sure, so that you can access the Virtual Server in your own program. For more instructions, please click here.
4.3 Triggered by PGOS Task
PGOS supports scheduling tasks to run at specified intervals. For information on configuring scheduled tasks to trigger Virtual Server, please click here.
4.4 Triggered by PGOS Economy Service
PGOS's economic system allows players to trigger in-game logic through a Virtual Server when using consumable items. For usage instructions, please click here.
4.5 Triggered by PGOS Matchmaking Service
PGOS's matchmaking system allows developers to change matchmaking result through a Virtual Server, which can modify team assignments, split one battle into more, or drop some tickets out of a battle. For usage instructions, please click here.
4.6 Triggered by IDIP Adapter
Bind Virtual Server in IDIP addon.
4.7 Triggered by Game Client
PGOS allows game to trigger virtual server through the following PgosSDK
APIs on the game client:
/**
* Invoke the specified virtual server.
*
* @param Req The request info of the call.
*/
void InvokeVirtualServer(
const FVirtualServerReq& Req,
TFunction<void(const FPgosResult& Ret, const FVirtualServerResp* Data)> Callback) const;
struct FVirtualServerReq
{
/** The name of the virtual server. */
FString server_name;
/** [Optional] The requested url if there is any. For example: "/battle/settlement". */
FString url;
/**
* Extra header fields required by the game.
* It is advisable to add a "Content-Type" field to avoid being rejected by the server, based on the request body format.
* for example: extra_headers["Content-Type"] = "text/plain"
* Here are the fields reserved for PGOS that cannot be used by the game:
* "SDKVersion", "ProtoVersion", "TitleId", "TitleRegionId", "SessionId", "PlayerId", "PlayerTicket",
* "RoutingKey", "UicAccountInfo", "SecretID", "Pid", "ServerTicket", "DstAddr", "DstCode", "AccTicket".
*/
TMap<FString, FString> extra_headers;
/** [Optional] The request binary data. */
TArray<uint8> body;
};
struct FVirtualServerResp
{
/** The HTTP status code of the response. */
int32 status = 200;
/** The HTTP header of the response. */
TMap<FString, FString> resp_headers;
/** The HTTP body of the response. */
FString resp_body;
};
Example Code:
#include "PgosSDKCpp.h"
void SomeUObjectClass::SomeFunction()
{
auto Extension = IPgosSDKCpp::Get().GetClientExtensionAPI();
if (Extension)
{
FVirtualServerReq Req;
Req.server_name = TEXT("sample_vs"); // Virtual Server name
Req.url = TEXT("/battle/settlement"); // Virtual Server respond to /battle/settlement
Req.extra_headers.Add("Content-Type", "text/plain"); // Based on the request body format
FString BodyString = TEXT("This is a test string from the game client.");
int32 Utf8Length = FTCHARToUTF8_Convert::ConvertedLength(*BodyString, BodyString.Len());
Req.body.SetNumUninitialized(Utf8Length);
FTCHARToUTF8_Convert::Convert((ANSICHAR*)req.body.GetData(), req.body.Num(), *BodyString, BodyString.Len());
Extension->InvokeVirtualServer(Req, [](const FPgosResult& Ret, const FVirtualServerResp* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("InvokeVirtualServer Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("InvokeVirtualServer Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
4.8 Triggered by Game Server
PGOS allows game to trigger virtual server through the following PgosSDK
APIs on the game server:
/**
* Invoke the specified virtual server.
*
* @param Req The request info of the call.
*/
void InvokeVirtualServer(
const FVirtualServerReq& Req,
TFunction<void(const FPgosResult& Ret, const FVirtualServerResp* Data)> Callback) const;
struct FVirtualServerReq
{
/** The name of the virtual server. */
FString server_name;
/** [Optional] The requested url if there is any. For example: "/battle/settlement". */
FString url;
/**
* Extra header fields required by the game.
* It is advisable to add a "Content-Type" field to avoid being rejected by the server, based on the request body format.
* for example: extra_headers["Content-Type"] = "text/plain"
* Here are the fields reserved for PGOS that cannot be used by the game:
* "SDKVersion", "ProtoVersion", "TitleId", "TitleRegionId", "SessionId", "PlayerId", "PlayerTicket",
* "RoutingKey", "UicAccountInfo", "SecretID", "Pid", "ServerTicket", "DstAddr", "DstCode", "AccTicket".
*/
TMap<FString, FString> extra_headers;
/** [Optional] The request binary data. */
TArray<uint8> body;
};
struct FVirtualServerResp
{
/** The HTTP status code of the response. */
int32 status = 200;
/** The HTTP header of the response. */
TMap<FString, FString> resp_headers;
/** The HTTP body of the response. */
FString resp_body;
};
Example Code:
#include "PgosSDKCpp.h"
void SomeUObjectClass::SomeFunction()
{
auto Extension = IPgosSDKCpp::Get().GetServerExtensionAPI();
if (Extension)
{
FVirtualServerReq Req;
Req.server_name = TEXT("sample_vs"); // Virtual Server name
Req.url = TEXT("/battle/settlement"); // Virtual Server respond to /battle/settlement
Req.extra_headers.Add("Content-Type", "text/plain"); // Based on the request body format
FString BodyString = TEXT("This is a test string from the game server.");
int32 Utf8Length = FTCHARToUTF8_Convert::ConvertedLength(*BodyString, BodyString.Len());
Req.body.SetNumUninitialized(Utf8Length);
FTCHARToUTF8_Convert::Convert((ANSICHAR*)req.body.GetData(), req.body.Num(), *BodyString, BodyString.Len());
Extension->InvokeVirtualServer(Req, [](const FPgosResult& Ret, const FVirtualServerResp* Data) {
if (Ret.err_code == (int32)Pgos::PgosErrCode::kSuccess)
{
UE_LOG(LogTemp, Log, TEXT("InvokeVirtualServer Success"));
}
else
{
UE_LOG(LogTemp, Log, TEXT("InvokeVirtualServer Failed: err_code=%d, err_msg=%s"), Ret.err_code, *Ret.msg);
}
});
}
}
5. Handle the Request in Virtual Server
5.1 URI Specification
Virtual Server is a web server essentially. So, every request will be handled based on URI. There are two types of URI: reserved URI and customized URI. The reserved URIs are predefined by the PGOS backend and used in specific scenarios. The customized URIs are defined by yourself and used to request a virtual server for the game client or backend. The following table lists all the cases:
URI | Triggers | HTTP Method | Definer | Is PGOS Reserved | Desc | Related Data Schema |
---|---|---|---|---|---|---|
/pgos_event | PGOS Event | POST | PGOS | Yes | Triggered by Event Handler | Event Data Schema |
/pgos_task | PGOS Task | POST | PGOS | Yes | Triggered by Task | Task Data Schema |
/pgos_economy | PGOS Economy Services | POST | PGOS | Yes | Triggered by Economy | Economy Data Schema |
/pgos_matchmaking | PGOS Matchmaking Services | POST | PGOS | Yes | Triggered by Matchmaking | Matchmaking Post Processing Schema |
/pgos_idip | Tencent IDIP Protocol | Defined by IDIP | PGOS | Yes | Only for Tencent Game | Defined by IDIP |
URI only for game backend | Game Backend | POST | Game Developer | No | The invoker is the game server Backend API: InvokeVirtualServer | Defined by Game Developer |
URI only for game client | Game Client | POST | Game Developer | No | The invoker is the game client Client API: InvokeVirtualServer | Defined by Game Developer |
The requester will receive an error message if the game client or backend requests the reserved URI(like /pgos_event) directly. Because the reserved URIs are only accessible for specific services and are triggered by PGOS automatically.
5.2 Request Headers and Security Handling
Several predefined and reserved request headers may be helpful for the game. You can also use any other headers as long as they are not the reserved headers that will be mentioned.
Header Key | Value Spec | Scope | Is PGOS Reserved |
---|---|---|---|
Titleid | The ID of your title | All | Yes |
Titleregionid | The ID of your title region | All | Yes |
Playerid | The Player ID of the game client | Only Game Client Request | Yes |
Sessionid | The Player Session ID of the game client | Only Game Client Request | Yes |
Invoker | event / task / economy / idip / backend / client / intranet | All (intranet only from virtual server) | Yes |
X-Forwarded-For | IPs of the request. The first IP is from the request client | Game Backend/Client Request | No (Defined in RFC 2616) |
NOTE: The header Invoker is set by the PGOS backend automatically. PGOS can guarantee it is set or overwritten with proper value even though the requester set it. This header is noteworthy for game developers and can help the virtual server to validate if the request is valid. For safety considering, you should validate this header at the beginning of every request handling. For example, you implement the URI /backend_func. It is only visible for the backend. So, any request from the client to the /backend_func will fail, because you do the validation by checking the header Invoker if it equals backend. The sample Node.js code is as below:
var invoker = ctx.request.headers['Invoker'];
if (invoker != 'backend') {
ctx.status = 401; // return http code 401 to the requester
return;
}
Here is specification of the Invoker
.
URI | Invoker Header Value | Desc |
---|---|---|
/pgos_event | event | Triggered by Event Handler |
/pgos_task | task | Triggered by Task |
/pgos_economy | economy | Triggered by Economy |
/pgos_matchmaking | matchmaking | Triggered by Matchmaking |
/pgos_idip | idip | Only for Tencent Game IDIP System |
URI only for game backend | backend | The invoker is the game server Backend HTTP API: InvokeVirtualServer |
URI only for game client | client | The invoker is the game client Client API: InvokeVirtualServer |
If you don't want to expose the server to the game client totally, a simpler way is to set the Block Game Client Request in the Configuration page of the virtual server. Please refer to the 2. Create and Manage Virtual Server
5.3 The Request and Response Protocol
For the customized URI, you decide the status code, headers and body schema of the request and response. So, you can implement your backend services with virtual server. But, for the reserved URI, you should follow the protocol specification defined by PGOS. Here are the details:
5.3.1 The Protocol of /pgos_event
/pgos_event
is used to handle the PGOS Event when you bind the Event handler with the virtual server on the portal.
Content-Type: application/json
The Request body schema:
// e.g. event_player_login_pgos
{
"trigger_id": "24ea65b9-a6fb-4de7-9622-410e33c73339",
"event_name": "event_player_login_pgos",
"event_time": "1683603150102",
"event_data": {
"custom_data": "123",
"ip_addr": "183.14.133.2",
"is_new_player": false,
"location": "-",
"platform": "WINDOWS",
"player_id": "123",
"session_id": "1655777737338073088",
"start_time": 1683603150
},
"args": {
"arg1": 1
}
}
Here is the detailed Extension Data Schema for every event.
- The Response body schema
PGOS will store the response body into the access logs for event. So, you can leave it with empty string, or fill with the any string. You can view the access log on the page Extensions/Events/Trigger Logs
of portal.
- HTTP Status Code
HTTP 200 should be returned if you handle the event successfully, and other abnormal code can be returned if any error occurs on your side. It will also be stored into the access logs.
5.3.2 The Protocol of /pgos_task
/pgos_task
is used to handle the PGOS Task when you bind the Task with the virtual server on the portal.
Content-Type: application/json
The Request body schema:
// e.g. event_player_login_pgos
{
"trigger_id": "24ea65b9-a6fb-4de7-9622-410e33c73339",
"task_name": "event_player_login_pgos",
"task_time": "1683603150102",
"args": {
"arg1": 1
}
}
Here is the detailed Extension Data Schema for task.
- The Response body schema
PGOS will store the response body in the access logs for the event. So, you can leave it with an empty string, or fill it with any string. You can view the access log on the page Extensions/Events/Trigger Logs of the portal.
- HTTP Status Code
HTTP 200 should be returned if you handle the event successfully, and other abnormal codes can be returned if any error occurs on your side. It will also be stored in the access logs.
5.3.3 The Protocol of /pgos_economy
/pgos_economy
is used to handle the PGOS Economy when you bind the virtual server for Economy service on the portal.
Content-Type: application/json
The Request body schema:
// e.g. item_consume
{
"trigger_id": "24ea65b9-a6fb-4de7-9622-410e33c73339",
"operation": "item_consume",
"trigger_time": "1683603150102",
"data": {
...
}
}
Here is the detailed Extension Data Schema for every economy handling.
- The Response body schema
PGOS will store the response body in the logs for the economy. So, you can leave it with an empty string, or fill it with any string. You can view the logs of the specific player on the page Players/Query/Economy/Logs of the portal
- HTTP Status Code
HTTP 200 should be returned if you handle the event successfully, and other abnormal codes can be returned if any error occurs on your side. It will also be stored in the access logs.
5.3.4 The Protocol of /pgos_matchmaking
/pgos_matchmaking
is used to handle the matchmaking task when you have a match config associated with a virtual server.
Content-Type: application/json
The Request body schema:
// e.g. matchmaking_post_processing
{
"trigger_id": "24ea65b9-a6fb-4de7-9622-410e33c73339",
"operation": "matchmaking_post_processing",
"trigger_time": "1683603150102",
"data": {
"battle": { ... } ,
"battle_properties": [ ... ]
}
}
Here is the detailed Extension Data Schema for every matchmaking request handling.
- The Response body schema:
// e.g. matchmaking_post_processing
{
"battles": [ ... ],
"requeued_tickets": [ ... ]
}
Here is the detailed Extension Data Schema for every matchmaking response handling.
- Precautions
When implementing a virtual server, please follow the best practices recommended in the PGOS documentation.
- HTTP Status Code
HTTP 200 should be returned if you handle the event successfully, and other abnormal codes can be returned if any error occurs on your side. It will also be stored in the access logs.
5.3.5 The Protocol of /pgos_idip
/pgos_idip
is used to handle the message of the IDIP system(a Tencent in-house system and only for Tencent games) if you configure the virtual server on the IDIP add-on.
- Content-Type: defined by the IDIP system
- The Request body format: defined by the IDIP system
- The Response body format: defined by the IDIP system
- HTTP Status Code: HTTP 200. The error code and message are in the response body.
5.4 Access Virtual Storage in Virtual Server
PGOS provides Virtual Storage for the game to store the data in the DB or Cache. Since the virtual server can be built with any language and tech stack. Once you get the instance of virtual storage, you can access the storage by using any suitable driver. Here is an example: language is Golang, virtual storage is a redis, and the driver github.com/gomodule/redigo
may be a good choice.
As for how to create a virtual storage, please visit virtual storage manual.
5.5 Access PGOS Backend with HTTP API in Virtual Server
Maybe you need to access PGOS Backend in a virtual server, such as getting the player's info, modifying player data, etc. You can also use the HTTP API in any language to access the PGOS backend in a virtual server, just like doing it in-game servers. There is no difference between the two ways. Please visit Backend HTTP API to learn how to do it.
For performance and cost consideration, the requests will be automatically routed to the intranet of PGOS if your virtual server accesses the endpoints of PGOS Backend HTTP API in the same region.
5.6 Timeout Limitation
Every request has timeout limitation. If the RTT from the virtual server is over the limitation, the PGOS backend would break the request and return an HTTP 504 error to the requester. So, you should keep the maximum timeout in the virtual server.
URI | Timeout Limitation |
---|---|
/pgos_event | 3 seconds |
/pgos_task /pgos_economy /pgos_idip /pgos_matchmaking | 10 seconds |
URI only for game client | 10 seconds |
URI only for game backend | 10 seconds |
Why we set event timeout limitation to only 3 seconds? Because events are dispatched by a queue system(kafka) underlayer to gaurantee the order of the events that triggered by backend. If every event handler takes too long, it can cause a lot of blocking , eventually the throughput of the whole system will drop dramatically. So, for performance considering, every event handler needs to be set a shorter timeout limitation.
6. Local Virtual Server
You can run your virtual server locally for debugging or testing before deploying it online. As a web server, you can access the local virtual server directly through any HTTP tools (cURL, Postman, etc.). However, in this chapter, we will explain how to enable a game client or local DS (game server) to access a local virtual server.
6.1 Run Local Virtual Server
After finishing coding your virtual server, you can run it locally via the IDE or start it directly. The following screenshot is an example of launching via VS Code.
In this example, we will configure the following information on the portal console so that the game client and DS invocation code can work properly with this virtual server, whether it is running locally or online:
- Server Name: vs-smoke
- Listen Port: 8082
6.2 Add Codes to Invoke Virtual Server
Whether the virtual server is running locally or online, you can invoke it from the game client or the game server. Click the links to learn how to invoke the virtual server from game client and from game server.
6.3 Config Local Virtual Server
Configure local virtual servers for your game client or game server to access. Find and open the configuration file: pgos_config.ini
(for game client) or pgos_server_config.ini
(for game server)
The location of these configuration files:
- If launching the client/server via the UE project, look in the directory:
{GameProjectPath}\Saved\PgosRes\
- If launching the client/server from a packaged program, look in the directory:
{PackedGamePath}\{GameName}\Binaries\pgos_res
Add the local_vs_config
configuration item in the configuration file:
[Local VS Environment]
local_vs_config = {"vs_addrs":{"vs-smoke":"127.0.0.1:8082"}}
The value of the local_vs_config
configuration item is a JSON string, and you can add any virtual server you want to access locally in vs_addrs
:
- JSON key: the server name of the virtual server
- JSON value: the address of virtual server (including port), for example: 127.0.0.1:8082
6.4 Access Local Virtual Server
After completing the above operations, run your game client or game server. When the relevant code is executed, it will trigger access to the local virtual server.
7. Access Virtual Server via Browser
After PGOS v0.27.0, you can access your virtual server in browser, which is an useful feature for the following scenarios:
- Deploying the open source software in virtual server, such as Prometheus, Grafana, etc.
- Install open source database console to manage your database or disk
- Write your own admin system and deploy it to manage the data stored in the virtual server storage(Redis/MongoDB/Disk). For example, A player management system you implement.
7.1 How to Access Virtual Server via Browser
The entry of browsing virtual server is disabled by default. You can find the entry in the Summary
page of a virtual server. You should enable it before browsing like the screenshot showed. Once enabled, you can click the Open Home Page
link to open your virtual server on the new tab of the browser.
7.2 Security of Virtual Server Browsing
PGOS has the security validation for virtual server browsing. Every time you open the virtual server in browser, PGOS would validate the authentication of Portal, only the legitimate requests will be granted access. It is only accessed by your title managers(the users have virtual server updating permission of your title region). It will fail if anyone types the portal domain of the virtual server directly in the browser to open the virtual server. So, it is safety.
7.3 Known Issue
Currently, Virtual Server doesn't support the web socket connection. So, it may doesn't work if your app deployed in VS using web socket.
8. Intranet Communication for Virtual Servers
The virtual server can communiate with other virtual servers currently. You can find the Domain for Intranet
field in the Summary
page of a virtual server, shown as below.
Note that the domain for intranet is only used for communicating internally for same title. Currently the domain suffix is virtualserver.int
, the protocol is HTTP and the port is 80. Such as http://prometheus.xxx.xxx.virtualserver.int
9. Log Collection
Currently there are two type of logs that are supported: Server log and Access log.
Server log
PGOS would collect your server log automatically as long as your server print the logs into stdout. You can query them in the
Logs
page of the virtual server.Access log
PGOS will collect the access log in Nginx default format for every virtual server once you open the option in the
Summary
page shown as below. You can query them in theLogs
page as well.
10. Monitor Virtual Server
Dashboard is provided for every Virtual Server. You can find it at the Dashboard
page of Virtual Server, shown as below.
Currently we provide the following metrics:
- CPU Usage
- Memory Usage
- Instance Count
- QPS
- Avg RTT
- RTT Distribution
- Status Codes Distribution
- Network Ingress & Egress
Some metrics also can be produced by configured URL dimension, including QPS, Avg RTT, RTT Distribution, Status Codes Distribution and Network Ingress & Egress. You can config the URL whitelist by clicking Edit Metric Configuration
link, shown as below. It's worth noted that only the URLs in whitelist will be captured.
All metrics will be outputted in the PGOS Prometheus Exporter.
11. Best Practice of Writing a Virtual Server
11.1 Suggested Steps
Since the virtual server is a web server essentially. You should choose a suitable languages and framework to implement a web server. Here are the suggested steps:
Step 1. Choose a suitable language based on your scenario
To implement a server, the choice of language is the top big thing at the beginning. You always need to consider many factors, such as:
- The requirements and scenario of the server
- The language habits of the development team
- The performance expectation
- The perfection of the language ecosystem
- The development efficiency
Here are some mainstream languages that can be used to implement a server: Golang, NodeJS, Python, etc.
Step 2. Choose a suitable web server framework
The choice of server framework is the next big thing once the language is determined. A good framework should be reliable and high-performance. Here are some mainstream frameworks for your reference:
- Golang: Fiber, Beego
- NodeJS: Koa, Express
- Python: Flask, Bottle
Step 3. Write your server
You should confirm the functionality that the virtual server needs to implement. Such as:
- If it handles the PGOS Event, Task, Economy, etc. You should handle the specific URL.
- If it handles the request from the game client, you should make the protocol for communicating between the game client and the virtual server.
- If it handles the request from the game server, you should make the protocol for communicating between the game server and the virtual server, and then write the code to handle the request from the game server. Since a server can serve the server and client at the same time, So, don't forget the Invoker validation.
- If it needs to access virtual storage, you need to create the virtual storage instance in advance, and then choose a suitable driver to write the code to access it.
Step 4. Debug or test it locally
Since the virtual server is a standalone web server, you can debug and test it locally. But, if you want to do the local debug for the code of accessing virtual storage directly, you will fail to access the virtual storage since the virtual storage is deployed in the intranet of PGOS backend. Maybe you need a debug agent to access the virtual storage locally ( only for Dev Title Region).
Step 5. Upload the image and test it
You can upload the build to the virtual server on the portal or CLI once you complete all development and debug works. Then, you may need to test it on the game client or DS directly. Please make sure all functionality works well before you release your game client.
Step 6. Utilize resource efficiently
To deliver a seamless and robust experience while maintaining cost-effectiveness, it is a key aspect to utilize resource efficiently. By conducting stress tests on your service and observing the CPU and memory usage graphs on the Dashboard, you can assess whether your service is more sensitive to computation or space requirements. This information allows for a more scientific approach to adjusting the specifications of the CPU and memory, ensuring optimal performance and resource allocation. When a single instance cannot handle user requests due to performance limitations, you can improve the performance level of stateless services proportionally by adjusting the value of Max Instances. This scaling mechanism ensures that the platform adapts to increased demands while maintaining seamless user experience and operational efficiency.
11.2 Sample Code
We wrote several samples to demonstrate how to write the virtual server with different languages and write suitable Dockerfile. You can even copy it and modify the code to implement your version:) If you want to use it to deploy your virtual server, note that you should upload it as the source type Source Code with Dockerfile. For we provided Golang, Python and NodeJS samples for you. You can find them in Virtual Server Market, please enjoy them.
11. Best Practice of Deploying Third-party Software
PGOS enhanced the virtual server to support database, disk and accessing via browser. Our goal is to make it easier to run the software directly. Currently, you can almost run most of software on the market like Docker Hub, github, etc. We are building virtual server to become a game backend eco-system. There are a few typical scenarios:
- Deploy monitor software to monitor your game backend, you may need use databases or disk that are created in
Virtrual Server Storages
- Write your own server and access the data stored in the databases or disk to implement your custom services for your players
- Write your own admin system to manage your game backend, which can be accessed via browser directly in PGOS portal
If the softwares you deployed need to communicate with each other internally. You can also use the Domain for intranet
to do it. For example, you can configure the intranet domain of MongoDB Exporter you deployed as the scrape target of the Prometheus you deployed.
We released the market for virtual server, in which there will be the mainstream software images and installation guidances. We believe it would dramatically increase your productivity. Click the Virtual Server Market to see the details.