DockerCompose 启动 open-match
背景介绍
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:latestcontainer_name: open-match-backendvolumes:- ./matchmaker_config_default.yaml:/app/matchmaker_config_default.yaml- ./matchmaker_config_override.yaml:/app/matchmaker_config_override.yamlports:- "50505:50505" # gRPC- "51505:51505" # HTTPenvironment: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:- redisopen-match-frontend:image: open-match-frontend:latestcontainer_name: open-match-frontendports:- "50504:50504" # gRPC- "51504:51504" # HTTPvolumes:- ./matchmaker_config_default.yaml:/app/matchmaker_config_default.yaml- ./matchmaker_config_override.yaml:/app/matchmaker_config_override.yamlenvironment: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:- redisopen-match-query:image: open-match-query:latestcontainer_name: open-match-queryports:- "50503:50503" # gRPC- "51503:51503" # HTTPenvironment: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.yamldepends_on:- redisopen-match-synchronizer:image: open-match-synchronizer:latestcontainer_name: open-match-synchronizervolumes:- ./matchmaker_config_default.yaml:/app/matchmaker_config_default.yaml- ./matchmaker_config_override.yaml:/app/matchmaker_config_override.yamlports:- "50506:50506" # gRPC- "51506:51506" # HTTPenvironment: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:- redisopen-match-swaggerui:image: open-match-swaggerui:latestcontainer_name: open-match-swaggeruiports:- "51500:51500" # HTTP for Swagger UIvolumes:- ./matchmaker_config_default.yaml:/app/matchmaker_config_default.yaml- ./matchmaker_config_override.yaml:/app/matchmaker_config_override.yamlenvironment:API_SWAGGERUI_HOSTNAME: "open-match-swaggerui"API_SWAGGERUI_HTTP_PORT: "51500"depends_on:- open-match-backend- open-match-frontend- open-match-query- open-match-synchronizerredis:image: redis:alpinecontainer_name: open-match-redisports:- "6379:6379"volumes:- redis-data:/datavolumes:redis-data:
matchmaker_config_default.yaml 的内容和 matchmaker_config_override.yaml的内容是一致的,目的就是override可以覆盖default中的值(猜的),这部分内容从k8s的yaml文件中copy过来,内容如下
logging:level: debugformat: textrpc: 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 intervalmaxInterval: 500ms# The next retry interval is multiplied by this multipliermultiplier: 1.5# Randomize the retry intervalrandFactor: 0.5# maxElapsedTime caps the retry time (in milliseconds)maxElapsedTime: 3000msapi: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 setupshostname: open-match-redis# source value: open-match-core.redis.port = 6379port: 6379usePassword: falsepasswordPath: /redis-passwordpool:maxIdle: 200maxActive: 0idleTimeout: 0healthCheckTimeout: 300mstelemetry: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的职责范围
相关文章:

DockerCompose 启动 open-match
背景介绍 open-match是Google和unity联合开源的支持实时多人匹配的框架,已有多家游戏厂商在生产环境使用,官网 https://open-match.dev/site/ 。原本我们使用的是UOS上提供的匹配能力,但是UOS目前不支持自建的Dedicated servers 集群&#x…...

Chainlit集成Dashscope实现语音交互网页对话AI应用
前言 本篇文章讲解和实战,如何使用Chainlit集成Dashscope实现语音交互网页对话AI应用。实现方案是对接阿里云提供的语音识别SenseVoice大模型接口和语音合成CosyVoice大模型接口使用。针对SenseVoice大模型和CosyVoice大模型,阿里巴巴在github提供的有开…...

Canal 扩展篇(阿里开源用于数据同步备份,监控表和表字段(日志))
1.Canal介绍 Canal把自己伪装成从数据库,获取mysql主数据库的日志(binlog)信息,所以要想使用canal就得先开启数据库日志 https://github.com/alibaba/canal Canal 主要用途是基于 MySQL 数据库增量日志解析,提供增量…...

顺序表的定义
一.顺序表的定义 顺序表--用顺序存储的方式实现线性表 顺序存储。把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关 系由存储单元的邻接关系来体现。 二.顺序表的实现--静态分配 #include<stdio.h> #define MaxSize 10 //定义最大长度 …...

青少年编程能力等级测评CPA C++一级试卷(1)
青少年编程能力等级测评CPA C一级试卷(1) 一、单项选择题(共20题,每题3.5分,共70分) CP1_1_1.在C中,下列变量名正确的是( )。 A.$123 B&#…...

R语言中的plumber介绍
R语言中的plumber介绍 基本用法常用 API 方法1. GET 方法2. POST 方法3. 带路径参数的 GET 方法 使用 R 对数据进行操作处理 JSON 输入和输出运行 API 的其他选项其他功能 plumber 是个强大的 R 包,用于将 R 代码转换为 Web API,通过使用 plumber&#x…...

uniapp 设置 tabbar 的 midButton 按钮
效果展示: 中间的国际化没生效(忽略就行) 示例代码: 然后在 App.vue 中进行监听: <script>export default {onLaunch(e) {// #ifdef APPuni.onTabBarMidButtonTap(()>{console.log("中间按钮点击回调…...

php 生成随机数
记录:随机数抽奖 要求:每次生成3个 1 - 10 之间可重复(或不可重复)的随机数,10次为一轮,每轮要求数字5出现6次、数字4出现3次、…。 提炼需求: 1,可设置最小数、最大数、每次抽奖生成随机数的个数、是否允许重复 2,可设置每轮指定数字的出现次数 3,可设置每轮的抽奖…...
MySQL 8.4修改初始化后的默认密码
MySQL 8.4修改初始化后的默认密码 (1)初始化mysql: mysqld --initialize --console (2)之后,mysql会生成一个默认复杂的密码,如果打算修改这个密码,可以先用旧密码登录: mysql -u…...

前端开发笔记--css 黑马程序员1
文章目录 1. css 语法规范2.css的书写风格3.基础选择器选择器的分类标签选择器类选择器类选择器的特殊使用--多类名 id 选择器 字体属性常见字体字体大小字体粗细字体倾斜字体的复合简写字体属性总结 文本属性文本颜色文本对齐装饰文本文本缩进文本间距文本属性总结 css的引入方…...
ORACLE 19C创建多个不同字符集PDB
现在需要在一个测试环境创建1个为AL32UTF8的PDB,2个ZHS16GBK的PDB 这种情况下,必须先创建的CDB为AL32UTF8,下面是具体步骤: 1.AL32UTF8的pdb在建实例的时候一起创建完成 2.创建第一个ZHS16GBK的PDB cdr,通过pdbseed来克隆: SQL> create pluggable database cdr admin us…...

基于协同过滤的景区旅游可视化与景区推荐系统(自动爬虫,地点可换)
文章目录 有需要本项目的代码或文档以及全部资源,或者部署调试可以私信博主项目介绍过程展示项目移植每文一语 有需要本项目的代码或文档以及全部资源,或者部署调试可以私信博主 项目介绍 本项目是一个综合性的旅游景区数据管理与分析推荐系统,集成了用…...

ElasticSearch搜索引擎
npm run start ELK: ESLogstashKibana Kibana:端口号5601 elasticSearch是面向文档 Kibana可以将ElasticSearch的数据通过友好的页面展示出来。 Es与Kibana的版本一致 es查询的效率很高,处理大数据 ES和solr的区别: 当单纯的对已有…...
leetcode哈希表(二)-两个数组的交集
题目 . - 力扣(LeetCode) 给定两个数组 nums1 和 nums2 ,返回 它们的交集。输出结果中的每个元素一定是唯一的。我们可以不考虑输出结果的顺序 。 示例 1: 输入:nums1 [1,2,2,1], nums2 [2,2] 输出:[…...

嵌入式硬件设计中EDA布局与布线实现
大家好,今天主要给大家分享一下,如何使用立创EDA进行布局和布线,具体实现过程如下: 第一:PCB概念介绍 在介绍PCB的时候,先来说明一下,电子管的发明史。 贝尔在1876年发明了电话,爱迪生1879年发明了白炽灯、特斯拉于1888年发明了电动机,所有这些,都为电子学的诞生准…...

entity,pojo,vo,dto 详解
在Java项目中,包名通常用于组织代码,使其更加清晰和易于维护。entity、pojo、vo和dto是常见的包名,它们各自有不同的含义和用途。下面将详细解释这些包名的含义,并提供一个示例,帮助你更好地理解它们在项目中的应用。 …...
C语言常见知识点
目录 一、单位 二、变量 三、常量 四、转义字符 五、操作符 六、指针 七、数组 一、单位 在刚接触编程时,肯定会了解到计算机的存储单位,它们由小到大分别为bit(位)、Byte(字节)、KB(千字…...

产品设计——应用架构
我的理解应用架构是业务架构的落地,微服务架构下平台的应用架构设计,实质是根据业务来明确应用微服务的边界。因此业务不同,应用架构图也不同。但是基本框架应该相差不大。 其划分原则莫过于高内聚、低耦合。这个跟接口设计是一致的。我们总是…...
Linux初阶——动静态库
一、静态库(格式:libxxx.a) 1. 原理 在运行之前就已经把函数的实现代码拷进 main.c 文件里了,因此静态库的权限是没有可执行权限的,因为根本不会执行到静态库里的内容。静态库的创建原理本质上就是把所有的函数源文件…...

创建一个c#程序,实现字符串类型转整数类型
首先,创建一个c#程序 在代码编辑器中编写代码,点击Run按钮或者按下F5键来运行程序。 下面,编写将字符串类型转换为整数类型的代码。 sing System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Task…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...