Skip to content

Commit 478cffa

Browse files
committed
build(docker): 新增多服务部署配置和热更依赖同步
- Dockerfile 改进热更 DLL 复制,同步所有依赖到 /app 根目录 - 新增 docker-compose.multi.yml 支持 MongoDB + 2 Game + 2 Social 多进程联调 - 新增 scripts/multi/ 下的 smoke-cross-process.sh 和 run-bots-rpc.sh 脚本
1 parent 0d68cd7 commit 478cffa

5 files changed

Lines changed: 449 additions & 3 deletions

File tree

Dockerfile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ COPY --from=publish /app/publish .
2828
# 复制json配置文件
2929
COPY GameFrameX.Config/json ./json
3030

31-
# 复制Hotfix编译结果到发布目录
32-
RUN mkdir -p /app/hotfix
33-
COPY --from=publish /app/build/hotfix/GameFrameX.Hotfix.dll /app/hotfix/
31+
# 复制Hotfix编译结果到发布目录(包含所有依赖)
32+
RUN mkdir -p /app/hotfix
33+
COPY --from=publish /app/build/hotfix/ /app/hotfix/
34+
# 热更依赖加载逻辑默认从 /app 根目录查找引用程序集,这里同步 DLL 到根目录
35+
RUN cp -f /app/hotfix/*.dll /app/ || true
3436

3537
# 切换到root用户创建数据目录并设置权限
3638
USER root

docker-compose.multi.yml

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
name: gfx-multi
2+
3+
services:
4+
mongo:
5+
image: mongo:8.2
6+
restart: unless-stopped
7+
ports:
8+
- "47017:27017"
9+
healthcheck:
10+
test: [ "CMD", "mongosh", "--quiet", "--eval", "db.runCommand({ ping: 1 }).ok" ]
11+
interval: 5s
12+
timeout: 3s
13+
retries: 20
14+
volumes:
15+
- "./running-multi/mongo:/data/db"
16+
networks:
17+
- gameframex-multi
18+
19+
social-1:
20+
build:
21+
context: .
22+
image: gameframex/server.launcher:local
23+
depends_on:
24+
mongo:
25+
condition: service_healthy
26+
command:
27+
- "--ServerType=Social"
28+
- "--ServerId=5531"
29+
- "--ServerInstanceId=2001"
30+
- "--InnerHost=0.0.0.0"
31+
- "--InnerPort=29400"
32+
- "--OuterHost=0.0.0.0"
33+
- "--OuterPort=29400"
34+
- "--HttpPort=28081"
35+
- "--IsEnableHttp=true"
36+
- "--DataBaseUrl=mongodb://mongo:27017/?authSource=admin"
37+
- "--DataBaseName=gameframex_multi"
38+
environment:
39+
services__game__tcp__0: "tcp://game-1:29100"
40+
services__game__tcp__1: "tcp://game-2:29101"
41+
services__game__http__0: "http://game-1:28080"
42+
services__game__http__1: "http://game-2:28082"
43+
ports:
44+
- "49400:29400"
45+
- "48081:28081"
46+
volumes:
47+
- "./running-multi/social-1/logs:/app/data/logs"
48+
- "./GameFrameX.Config/json:/app/Configs:ro"
49+
networks:
50+
- gameframex-multi
51+
52+
social-2:
53+
image: gameframex/server.launcher:local
54+
depends_on:
55+
mongo:
56+
condition: service_healthy
57+
command:
58+
- "--ServerType=Social"
59+
- "--ServerId=5532"
60+
- "--ServerInstanceId=2002"
61+
- "--InnerHost=0.0.0.0"
62+
- "--InnerPort=29401"
63+
- "--OuterHost=0.0.0.0"
64+
- "--OuterPort=29401"
65+
- "--HttpPort=28083"
66+
- "--IsEnableHttp=true"
67+
- "--DataBaseUrl=mongodb://mongo:27017/?authSource=admin"
68+
- "--DataBaseName=gameframex_multi"
69+
environment:
70+
services__game__tcp__0: "tcp://game-1:29100"
71+
services__game__tcp__1: "tcp://game-2:29101"
72+
services__game__http__0: "http://game-1:28080"
73+
services__game__http__1: "http://game-2:28082"
74+
ports:
75+
- "49401:29401"
76+
- "48083:28083"
77+
volumes:
78+
- "./running-multi/social-2/logs:/app/data/logs"
79+
- "./GameFrameX.Config/json:/app/Configs:ro"
80+
networks:
81+
- gameframex-multi
82+
83+
game-1:
84+
image: gameframex/server.launcher:local
85+
depends_on:
86+
mongo:
87+
condition: service_healthy
88+
social-1:
89+
condition: service_started
90+
social-2:
91+
condition: service_started
92+
command:
93+
- "--ServerType=Game"
94+
- "--ServerId=1001"
95+
- "--ServerInstanceId=1001"
96+
- "--InnerHost=0.0.0.0"
97+
- "--InnerPort=29100"
98+
- "--OuterHost=0.0.0.0"
99+
- "--OuterPort=29100"
100+
- "--HttpPort=28080"
101+
- "--IsEnableHttp=true"
102+
- "--DataBaseUrl=mongodb://mongo:27017/?authSource=admin"
103+
- "--DataBaseName=gameframex_multi"
104+
environment:
105+
services__social__tcp__0: "tcp://social-1:29400"
106+
services__social__tcp__1: "tcp://social-2:29401"
107+
services__social__http__0: "http://social-1:28081"
108+
services__social__http__1: "http://social-2:28083"
109+
ports:
110+
- "49100:29100"
111+
- "48080:28080"
112+
volumes:
113+
- "./running-multi/game-1/logs:/app/data/logs"
114+
- "./GameFrameX.Config/json:/app/Configs:ro"
115+
networks:
116+
- gameframex-multi
117+
118+
game-2:
119+
image: gameframex/server.launcher:local
120+
depends_on:
121+
mongo:
122+
condition: service_healthy
123+
social-1:
124+
condition: service_started
125+
social-2:
126+
condition: service_started
127+
command:
128+
- "--ServerType=Game"
129+
- "--ServerId=1002"
130+
- "--ServerInstanceId=1002"
131+
- "--InnerHost=0.0.0.0"
132+
- "--InnerPort=29101"
133+
- "--OuterHost=0.0.0.0"
134+
- "--OuterPort=29101"
135+
- "--HttpPort=28082"
136+
- "--IsEnableHttp=true"
137+
- "--DataBaseUrl=mongodb://mongo:27017/?authSource=admin"
138+
- "--DataBaseName=gameframex_multi"
139+
environment:
140+
services__social__tcp__0: "tcp://social-1:29400"
141+
services__social__tcp__1: "tcp://social-2:29401"
142+
services__social__http__0: "http://social-1:28081"
143+
services__social__http__1: "http://social-2:28083"
144+
ports:
145+
- "49101:29101"
146+
- "48082:28082"
147+
volumes:
148+
- "./running-multi/game-2/logs:/app/data/logs"
149+
- "./GameFrameX.Config/json:/app/Configs:ro"
150+
networks:
151+
- gameframex-multi
152+
153+
networks:
154+
gameframex-multi:
155+
driver: bridge

scripts/acceptance.sh

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#!/usr/bin/env bash
2+
3+
set -u
4+
5+
ROOT_DIR="/Users/blank/Documents/GithubWorks/GameFrameX/Server"
6+
TEST_PROJECT="Tests/GameFrameX.Tests/GameFrameX.Tests.csproj"
7+
SOLUTION="Server.sln"
8+
REMOTE_FILTER="FullyQualifiedName~RemoteMessaging"
9+
10+
MODE="fast"
11+
if [[ "${1:-}" == "--fast" ]]; then
12+
MODE="fast"
13+
fi
14+
if [[ "${1:-}" == "--full" ]]; then
15+
MODE="full"
16+
fi
17+
18+
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
19+
cat <<'EOF'
20+
用法:
21+
scripts/acceptance.sh [--fast|--full]
22+
23+
说明:
24+
默认 等同 --fast
25+
--fast 只跑必过门禁(build/test/socket扫描/Friend语义/回滚)
26+
--full 跑完整验收(在 fast 基础上增加重试开关与故障注入演练)
27+
EOF
28+
exit 0
29+
fi
30+
31+
if [[ ! -d "$ROOT_DIR" ]]; then
32+
echo "ERROR: 根目录不存在: $ROOT_DIR"
33+
exit 1
34+
fi
35+
36+
cd "$ROOT_DIR" || exit 1
37+
38+
declare -a PASSED=()
39+
declare -a FAILED=()
40+
41+
record_pass() {
42+
PASSED+=("$1")
43+
echo "PASS: $1"
44+
}
45+
46+
record_fail() {
47+
FAILED+=("$1")
48+
echo "FAIL: $1"
49+
}
50+
51+
run_cmd() {
52+
local name="$1"
53+
local cmd="$2"
54+
echo ""
55+
echo ">>> $name"
56+
echo "$cmd"
57+
if bash -lc "$cmd"; then
58+
record_pass "$name"
59+
return 0
60+
fi
61+
record_fail "$name"
62+
return 1
63+
}
64+
65+
run_check_no_match() {
66+
local name="$1"
67+
local cmd="$2"
68+
echo ""
69+
echo ">>> $name"
70+
echo "$cmd"
71+
local output
72+
output="$(bash -lc "$cmd")"
73+
local code=$?
74+
if [[ $code -eq 1 && -z "$output" ]]; then
75+
record_pass "$name"
76+
return 0
77+
fi
78+
if [[ $code -eq 0 && -n "$output" ]]; then
79+
echo "$output"
80+
record_fail "$name (发现命中)"
81+
return 1
82+
fi
83+
if [[ $code -eq 0 && -z "$output" ]]; then
84+
record_pass "$name"
85+
return 0
86+
fi
87+
echo "$output"
88+
record_fail "$name (命令执行失败)"
89+
return 1
90+
}
91+
92+
run_check_contains() {
93+
local name="$1"
94+
local cmd="$2"
95+
local expected1="$3"
96+
local expected2="$4"
97+
echo ""
98+
echo ">>> $name"
99+
echo "$cmd"
100+
local output
101+
output="$(bash -lc "$cmd")"
102+
local code=$?
103+
echo "$output"
104+
if [[ $code -ne 0 ]]; then
105+
record_fail "$name (命令执行失败)"
106+
return 1
107+
fi
108+
if [[ "$output" == *"$expected1"* && "$output" == *"$expected2"* ]]; then
109+
record_pass "$name"
110+
return 0
111+
fi
112+
record_fail "$name (未命中预期语义)"
113+
return 1
114+
}
115+
116+
echo "========================================"
117+
echo "GameFrameX 验收脚本"
118+
echo "模式: $MODE"
119+
echo "根目录: $ROOT_DIR"
120+
echo "========================================"
121+
122+
run_cmd "Restore" "dotnet restore $SOLUTION"
123+
run_cmd "Build" "dotnet build $SOLUTION"
124+
run_cmd "Test(All)" "dotnet test $SOLUTION"
125+
126+
run_check_no_match \
127+
"Logic 禁止直连 Socket 检查" \
128+
"rg -n 'new\\s+TcpClient\\(|NetworkStream|Socket\\(' GameFrameX.Hotfix/Logic"
129+
130+
run_check_contains \
131+
"Friend 重试语义检查" \
132+
"rg -n 'AllowRetry|CallWithResultAsync' GameFrameX.Hotfix/Logic/Player/Friend/FriendComponentAgent.cs" \
133+
"AllowRetry = true" \
134+
"AllowRetry = false"
135+
136+
run_cmd \
137+
"回滚开关验证 EnableUnifiedClient=false" \
138+
"RemoteMessaging__EnableUnifiedClient=false dotnet test $TEST_PROJECT --filter \"$REMOTE_FILTER\""
139+
140+
if [[ "$MODE" == "full" ]]; then
141+
run_cmd \
142+
"重试开关验证 EnableRetry=false" \
143+
"RemoteMessaging__EnableRetry=false dotnet test $TEST_PROJECT --filter \"$REMOTE_FILTER\""
144+
145+
run_cmd \
146+
"故障注入 Timeout 演练" \
147+
"RemoteMessaging__EnableFaultInjection=true RemoteMessaging__FaultInjection__Type=Timeout dotnet test $TEST_PROJECT --filter \"$REMOTE_FILTER\""
148+
149+
run_cmd \
150+
"故障注入 ConnectionDrop 演练" \
151+
"RemoteMessaging__EnableFaultInjection=true RemoteMessaging__FaultInjection__Type=ConnectionDrop dotnet test $TEST_PROJECT --filter \"$REMOTE_FILTER\""
152+
153+
run_cmd \
154+
"故障注入 SlowResponse 演练" \
155+
"RemoteMessaging__EnableFaultInjection=true RemoteMessaging__FaultInjection__Type=SlowResponse RemoteMessaging__FaultInjection__DelayMs=2000 dotnet test $TEST_PROJECT --filter \"$REMOTE_FILTER\""
156+
fi
157+
158+
echo ""
159+
echo "========================================"
160+
echo "验收汇总"
161+
echo "通过: ${#PASSED[@]}"
162+
echo "失败: ${#FAILED[@]}"
163+
echo "----------------------------------------"
164+
for item in "${PASSED[@]-}"; do
165+
echo "PASS - $item"
166+
done
167+
if [[ ${#FAILED[@]} -gt 0 ]]; then
168+
for item in "${FAILED[@]-}"; do
169+
echo "FAIL - $item"
170+
done
171+
fi
172+
echo "========================================"
173+
174+
if [[ ${#FAILED[@]} -gt 0 ]]; then
175+
exit 1
176+
fi
177+
178+
exit 0

scripts/multi/run-bots-rpc.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
PROJECT_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
6+
cd "$PROJECT_ROOT"
7+
8+
BOT_COUNT="${BOT_COUNT:-100}"
9+
TCP_HOST="${TCP_HOST:-127.0.0.1}"
10+
TCP_PORT="${TCP_PORT:-49100}"
11+
LOGIN_URL="${LOGIN_URL:-http://127.0.0.1:48080/game/api/}"
12+
DISCONNECT_LOOP="${DISCONNECT_LOOP:-true}"
13+
DISCONNECT_AFTER_LOGIN_SECONDS="${DISCONNECT_AFTER_LOGIN_SECONDS:-15}"
14+
RUN_SECONDS="${RUN_SECONDS:-180}"
15+
CONNECT_STAGGER_MS="${CONNECT_STAGGER_MS:-10}"
16+
17+
echo "启动机器人压测:"
18+
echo " BOT_COUNT=$BOT_COUNT"
19+
echo " TCP=$TCP_HOST:$TCP_PORT"
20+
echo " LOGIN_URL=$LOGIN_URL"
21+
echo " DISCONNECT_LOOP=$DISCONNECT_LOOP"
22+
echo " DISCONNECT_AFTER_LOGIN_SECONDS=$DISCONNECT_AFTER_LOGIN_SECONDS"
23+
echo " RUN_SECONDS=$RUN_SECONDS"
24+
echo " CONNECT_STAGGER_MS=$CONNECT_STAGGER_MS"
25+
26+
dotnet run --framework net10.0 --project GameFrameX.Client/GameFrameX.Client.csproj -- \
27+
--bot-count="$BOT_COUNT" \
28+
--tcp-host="$TCP_HOST" \
29+
--tcp-port="$TCP_PORT" \
30+
--login-url="$LOGIN_URL" \
31+
--disconnect-loop="$DISCONNECT_LOOP" \
32+
--disconnect-after-login-seconds="$DISCONNECT_AFTER_LOGIN_SECONDS" \
33+
--run-seconds="$RUN_SECONDS" \
34+
--connect-stagger-ms="$CONNECT_STAGGER_MS"

0 commit comments

Comments
 (0)