背景介绍
open-match是Google和unity联合开源的支持实时多人匹配的框架,已有多家游戏厂商在生产环境使用,官网 https://open-match.dev/site/ 。原本我们使用的是UOS上提供的匹配能力,但是UOS目前不支持自建的Dedicated servers 集群,只能上传镜像,UOS会自动完成分配以及创建,灵活性就不是那么高了,按照open-match的官方教程,需要有k8s的环境。但是小公司就是不想要k8s,我就想单机部署,可以使用docker-compose。open-match本身的镜像build 提供了dockerfile文件。把项目clone到本地,然后在 tm中以此使用以下命令构建对用的image
tip:该方案只是启动了 core 部分要求的组件即
搭建步骤
Install with YAML | Open Match
后面的部分,需要自行探索了,现在也在学习中,欢迎交流讨论
先构建一个中间镜像base builder,然后一次构建对应的component即可,需要修改Dockerfile.cmd中的
FROM open-match-base-build as builder
为,目的是指定使用本地的version版本,否则会远程拉取镜像,网络问题会导致不成功,需要开梯子
FROM open-match-base-build:latest as builder
docker build -f .\Dockerfile.base-build -t open-match-base-build .
docker build --build-arg IMAGE_TITLE=synchronizer -f .\Dockerfile.cmd -t open-match-synchronizer .
接着使用docker-compose.yaml 来控制启动过程,内如如下
version: '3'
services:
open-match-backend:
image: open-match-backend:latest
container_name: open-match-backend
volumes:
- ./matchmaker_config_default.yaml:/app/matchmaker_config_default.yaml
- ./matchmaker_config_override.yaml:/app/matchmaker_config_override.yaml
ports:
- "50505:50505" # gRPC
- "51505:51505" # HTTP
environment:
API_BACKEND_HOSTNAME: "open-match-backend"
API_BACKEND_GRPC_PORT: "50505"
API_BACKEND_HTTP_PORT: "51505"
REDIS_HOSTNAME: "open-match-redis"
REDIS_PORT: "6379"
depends_on:
- redis
open-match-frontend:
image: open-match-frontend:latest
container_name: open-match-frontend
ports:
- "50504:50504" # gRPC
- "51504:51504" # HTTP
volumes:
- ./matchmaker_config_default.yaml:/app/matchmaker_config_default.yaml
- ./matchmaker_config_override.yaml:/app/matchmaker_config_override.yaml
environment:
API_FRONTEND_HOSTNAME: "open-match-frontend"
API_FRONTEND_GRPC_PORT: "50504"
API_FRONTEND_HTTP_PORT: "51504"
REDIS_HOSTNAME: "open-match-redis"
REDIS_PORT: "6379"
depends_on:
- redis
open-match-query:
image: open-match-query:latest
container_name: open-match-query
ports:
- "50503:50503" # gRPC
- "51503:51503" # HTTP
environment:
API_QUERY_HOSTNAME: "open-match-query"
API_QUERY_GRPC_PORT: "50503"
API_QUERY_HTTP_PORT: "51503"
REDIS_HOSTNAME: "open-match-redis"
REDIS_PORT: "6379"
volumes:
- ./matchmaker_config_default.yaml:/app/matchmaker_config_default.yaml
- ./matchmaker_config_override.yaml:/app/matchmaker_config_override.yaml
depends_on:
- redis
open-match-synchronizer:
image: open-match-synchronizer:latest
container_name: open-match-synchronizer
volumes:
- ./matchmaker_config_default.yaml:/app/matchmaker_config_default.yaml
- ./matchmaker_config_override.yaml:/app/matchmaker_config_override.yaml
ports:
- "50506:50506" # gRPC
- "51506:51506" # HTTP
environment:
API_SYNCHRONIZER_HOSTNAME: "open-match-synchronizer"
API_SYNCHRONIZER_GRPC_PORT: "50506"
API_SYNCHRONIZER_HTTP_PORT: "51506"
REDIS_HOSTNAME: "open-match-redis"
REDIS_PORT: "6379"
depends_on:
- redis
open-match-swaggerui:
image: open-match-swaggerui:latest
container_name: open-match-swaggerui
ports:
- "51500:51500" # HTTP for Swagger UI
volumes:
- ./matchmaker_config_default.yaml:/app/matchmaker_config_default.yaml
- ./matchmaker_config_override.yaml:/app/matchmaker_config_override.yaml
environment:
API_SWAGGERUI_HOSTNAME: "open-match-swaggerui"
API_SWAGGERUI_HTTP_PORT: "51500"
depends_on:
- open-match-backend
- open-match-frontend
- open-match-query
- open-match-synchronizer
redis:
image: redis:alpine
container_name: open-match-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
volumes:
redis-data:
matchmaker_config_default.yaml 的内容和 matchmaker_config_override.yaml的内容是一致的,目的就是override可以覆盖default中的值(猜的),这部分内容从k8s的yaml文件中copy过来,内容如下
logging:
level: debug
format: text
rpc: false
# Open Match applies the exponential backoff strategy for its retryable gRPC calls.
# The settings below are the default backoff configuration used in Open Match.
# See https://github.com/cenkalti/backoff/blob/v3/exponential.go for detailed explanations
backoff:
# The initial retry interval (in milliseconds)
initialInterval: 100ms
# maxInterval caps the maximum time elapsed for a retry interval
maxInterval: 500ms
# The next retry interval is multiplied by this multiplier
multiplier: 1.5
# Randomize the retry interval
randFactor: 0.5
# maxElapsedTime caps the retry time (in milliseconds)
maxElapsedTime: 3000ms
api:
backend:
hostname: "open-match-backend"
grpcport: "50505"
httpport: "51505"
frontend:
hostname: "open-match-frontend"
grpcport: "50504"
httpport: "51504"
query:
hostname: "open-match-query"
grpcport: "50503"
httpport: "51503"
synchronizer:
hostname: "open-match-synchronizer"
grpcport: "50506"
httpport: "51506"
swaggerui:
hostname: "open-match-swaggerui"
httpport: "51500"
# Configurations for api.test and api.scale are used for testing.
test:
hostname: "open-match-test"
grpcport: "50509"
httpport: "51509"
scale:
httpport: "51509"
redis:
# Open Match's default Redis setups
hostname: open-match-redis
# source value: open-match-core.redis.port = 6379
port: 6379
usePassword: false
passwordPath: /redis-password
pool:
maxIdle: 200
maxActive: 0
idleTimeout: 0
healthCheckTimeout: 300ms
telemetry:
reportingPeriod: "1m"
traceSamplingFraction: "0.01"
zpages:
enable: "true"
prometheus:
enable: "false"
endpoint: "/metrics"
serviceDiscovery: "true"
stackdriverMetrics:
enable: "false"
gcpProjectId: "replace_with_your_project_id"
prefix: "open_match"
然后就可以启动了
运行demo
如果要运行open-match的 demo-first-match,还要一个组件
evaluator
该组件的目的是对所有的matches进行评估,并返回得分,open-match会保留评分最高的组合,比如我们以 战力相差的相近为评估规则,假定 abc,三个玩家的战力分值分配时 10 20 25,则abc三人同时匹配在1v1的条件选,应当尽可能的选择20 25这个相近的评分,评分组件evaluator这个需要开始自行实行,通过synchronizer进行调用,open-match 提供了一个default实现 即default-evaluator,使用以下命令进行构建即可
docker build --build-arg IMAGE_TITLE=default-evaluator -f .\Dockerfile.cmd -t open-match-default-evaluator .
如果开发环境存在需要平凡需求的情况,直接ide环境启动即可,但是需要在 matchmaker_config_default.yaml中指明 evaluator的位置信息
api:
evaluator:
hostname: "open-match-default-evaluator"
httpport: "5499"
grpcport: "5498"
接着需要修改几个demo的hostname地址,一个是frontend的hostname,位置在
open-match/examples/demo/components/clients/clients.go:84
//修改前
conn, err := grpc.Dial("open-match-frontend.open-match.svc.cluster.local:50504", grpc.WithInsecure())
//修改后
conn, err := grpc.Dial("localhost:50504", grpc.WithInsecure())
另外一个是 backend的hostname
open-match/examples/demo/components/director/director.go:71
//修改前
conn, err := grpc.Dial("open-match-backend.open-match.svc.cluster.local:50505", grpc.WithInsecure())
//修改后
conn, err := grpc.Dial("localhost:50505", grpc.WithInsecure())
然后还需要部署一个组件mmf,负责判断给定的组合是否符合要求,可以直接启动 cmd/scale-mmf,或者build镜像,一样的需要修改query的hostname,位置信息如下
open-match/examples/scale/mmf/mmf.go:42
//修改前
conn, err := grpc.Dial("open-match-query.open-match.svc.cluster.local:50503", utilTesting.NewGRPCDialOptions(logger)...)
//修改后
conn, err := grpc.Dial("localhost:50503", utilTesting.NewGRPCDialOptions(logger)...)
mmf的服务地址,是由director在fetchMatches指定的,相关的代码位置在
open-match/examples/demo/components/director/director.go:82
不同版本可能会有所变化,当前提及的代码内容来源于open-match的最新master代码,2024年10月9日
req := &pb.FetchMatchesRequest{
Config: &pb.FunctionConfig{
Host: "om-function.open-match-demo.svc.cluster.local",
Port: 50502,
Type: pb.FunctionConfig_GRPC,
},
Profile: &pb.MatchProfile{
Name: "1v1",
Pools: []*pb.Pool{
{
Name: "Everyone",
},
},
},
}
所以我们需要改掉这里Host所指定的值,修改为mmf的实际地址即可,最后更新docker-compose.yaml并执行 docker-compose -f docker-compose.yaml up,在cmd/demo-first-match/main.go 文件中运行main函数即可成功运行demo程序
demo程序的执行成功后会返回一个虚拟的链接地址,该链接地址由director组件返回,可以理解为在director中返回具体的dedicated server的ip地址,demo中是随机产生的代码位置在
examples/demo/components/director/director.go:132
如果不正确可以搜索2222作为线索定位
for _, match := range matches {
ids := []string{}
for _, t := range match.Tickets {
ids = append(ids, t.Id)
}
req := &pb.AssignTicketsRequest{
Assignments: []*pb.AssignmentGroup{
{
TicketIds: ids,
Assignment: &pb.Assignment{
Connection: fmt.Sprintf("%d.%d.%d.%d:2222", rand.Intn(256), rand.Intn(256), rand.Intn(256), rand.Intn(256)),
},
},
},
}
resp, err := be.AssignTickets(ds.Ctx, req)
if err != nil {
panic(err)
}
_ = resp
}
匹配成功后返回的链接地址
open-match对mmf,evaluator,director 提供了多种游戏场景的默认实现,可以在亦有模板的基础上结合业务进行开发,具体内容可以查看examples/scale/README.md
感觉联机几千人同时在线这种方式应该够够吧,先这样,散会
补一张open-match 时序图,看懂这个业务开发层面知道各个component的职责范围