Memory-sensitive game services refer specifically to game servers that require large memory resources. They often need to load many resources into the memory during startup to improve the player's game interaction experience. But precisely because of this, it has brought about 1) the problem of slow startup speed of game servers and low efficiency during version updates; 2) the same memory data exists between game servers but cannot be used, and node memory resources are excessively wasted.
Game developers often use shared memory technology to solve the above problems to improve game server startup efficiency and memory resource efficiency. Usually, there is an init process that performs the function of initializing the loading of the game server and writing data to the shared memory; after that, all newly created game servers on the machine do not need to repeat the process and only need to read the same address. The corresponding memory data is enough, the startup speed is improved, and the memory resources are reused, without causing waste of resources.
This article will focus on the shared memory usage of containerized game servers and provide best practices.
The solution involves two types of processes. For example, the init process mentioned above writes memory; the gs process reads memory at startup.
As shown in the architecture diagram above, the init process is managed using DaemonSet, and each game server node deploys one init; while the gs process is managed using GameServerSet, and each node can have multiple gs. When the DaemonSet deployment is completed and the corresponding init pod is successfully executed, deploy the GameServerSet and start the game server. At this time, gs will start quickly, and gs on the same node will initially reuse the same memory space.
Use the following code to create a shared memory, and then write id data to the memory every second. The id will increase by one every second.
#include<sys/ipc.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct _msg
{
int id;
char str[64];
}MSG;
int main()
{
MSG* msg;
key_t key = ftok("./",2015);
if(key == -1)
{
perror("ftok");
exit(-1);
}
int shd = shmget(key,sizeof(MSG),IPC_CREAT | 0666);
if(shd == -1)
{
perror("shmget");
exit(-1);
}
msg = (MSG*)shmat(shd,NULL,0);
if(msg == (MSG*)-1)
{
perror("shmat");
exit(-1);
}
memset((void*)msg,0,sizeof(MSG));
for(int i = 0;i < 100000;i++)
{
msg->id = i;
printf("msg->id = %d\n",msg->id);
sleep(1);
}
system("ipcs -m");
return 0;
}
The following is the code for reading shared memory. The code obtains the shared memory data with shm_id 0 and prints the id data in a loop.
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int main() {
int shm_id;
void *shared_memory;
shm_id = 0;
printf("shm_id: %d\n", shm_id);
// if (shm_id == -1) {
// perror("shmget failed");
// exit(1);
// }
shared_memory = shmat(shm_id, NULL, 0);
if (shared_memory == (void *) -1) {
perror("shmat failed");
exit(1);
}
while(1) {
printf("Value from shared memory: %d\n", *((int *)shared_memory));
}
if (shmdt(shared_memory) == -1) {
perror("shmdt failed");
exit(1);
}
return 0;
}
The Dockerfile of gs process is as follows (init process is similar):
FROM gcc:latest
WORKDIR /usr/src/myapp
COPY . .
RUN gcc -o read read.c
USER root
RUN chmod 777 /usr/src/myapp/read
EntryPoint ["/usr/src/myapp/read"]
CMD ["sleep 300000"]
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: shm-daemonset
namespace: default
spec:
selector:
matchLabels:
name: init
template:
metadata:
labels:
name: init
spec:
hostIPC: true #When hostIPC: true is set, the Pod will use the IPC namespace of the host. Pods using the host IPC can access the shared memory segment on the host.
nodeSelector:
app: shared-mem #Select the nodes using shared memory by labels.
containers:
- name: init
image: registry.cn-hangzhou.aliyuncs.com/skkk/testc:write27_v2
volumeMounts:
- name: shm
mountPath: /dev/shm
volumes:
- name: shm
hostPath:
path: /dev/shm
type: Directory
After creating ds, you can see the created shared memory on the host
You can see in the container log that the value of the id is modified every second
Create gs to read the value of id from shared memory
apiVersion: game.kruise.io/v1alpha1
kind: GameServerSet
metadata:
name: gameserver
namespace: default
spec:
replicas: 2
updateStrategy:
rollingUpdate:
podUpdatePolicy: InPlaceIfPossible
gameServerTemplate:
spec:
hostIPC: true
nodeSelector:
app: shared-mem
containers:
- image: registry.cn-hangzhou.aliyuncs.com/skkk/testc:readtest
imagePullPolicy: Always
name: gs
volumeMounts:
- name: shm
mountPath: /dev/shm
volumes:
- hostPath:
path: /dev/shm
type: Directory
name: shm
After successful deployment, you can see in the container log that the ID value in the shared memory has been obtained.
Best Practice for Gaming Gateway of Higress × OpenKruiseGame
506 posts | 48 followers
FollowAlibaba Cloud Native Community - October 19, 2022
Alibaba Cloud Native Community - November 15, 2023
Alibaba Cloud Native Community - February 1, 2024
Alibaba Clouder - June 6, 2019
Alibaba Clouder - February 26, 2021
Alibaba Clouder - October 29, 2020
506 posts | 48 followers
FollowFollow our step-by-step best practices guides to build your own business case.
Learn MoreWhen demand is unpredictable or testing is required for new features, the ability to spin capacity up or down is made easy with Alibaba Cloud gaming solutions.
Learn MoreAlibaba Cloud Container Service for Kubernetes is a fully managed cloud container management service that supports native Kubernetes and integrates with other Alibaba Cloud products.
Learn MoreAlibaba Cloud’s world-leading database technologies solve all data problems for game companies, bringing you matured and customized architectures with high scalability, reliability, and agility.
Learn MoreMore Posts by Alibaba Cloud Native Community