> For the complete documentation index, see [llms.txt](https://docs.edgegap.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.edgegap.com/zh/learn/fu-wu-qi-liu-lan-qi.md).

# 服务器浏览器

快速开始使用服务器浏览器，并探索各种类型的示例场景。

服务器浏览器是一项面向 [部署](/zh/learn/bian-pai/deployments.md#match-bound) 和 [持久化](/zh/learn/bian-pai/chi-jiu-hua.md) 服务器：

* **帮助玩家搜索并加入合适的服务器** ，根据容量、延迟或游戏参数；
* **预热新服务器** 以规模化方式服务全球玩家，并避免令人沮丧的排队；
* **简化服务器运维** 包括更新、重启、持久化、网格化等。

{% hint style="success" %}
如果你想在不允许玩家选择服务器的情况下，根据严格规则进行玩家匹配，考虑使用 [匹配](/zh/learn/pi-pei.md).
{% endhint %}

## ✔️ 准备工作

**测试此服务完全免费，无需信用卡。**

免费额度允许在我们的共享测试集群上每次重启后最多运行 3 小时。

本教程假定您已经：

* [理解 Edgegap 的部署模型](https://docs.edgegap.com/zh/learn/pages/5e7e2169ca3822647d4607dfc1d3487ebcc0836c#id-1.-just-in-time-deployment-dedicated-servers),
* 已在 Edgegap 上发布您的服务器应用（[Unreal Engine](/zh/unreal-engine.md), [Unity](/zh/unity.md)),
* 已从游戏客户端成功连接到您在 Edgegap 上的服务器。

### 函数与流程

<figure><img src="https://3334189208-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2Fnwlt2Ot2ahlyLvI7kdGx%2Fimage.png?alt=media&amp;token=14b5a6c8-48c8-4f23-a50a-0783acab14e3" alt=""><figcaption><p>服务器浏览器：流程与层次结构</p></figcaption></figure>

服务器浏览器提供两个主要功能：

[#start-browsing](#start-browsing "mention") 并与客户端和服务器集成：

* 客户端通过一个方法预留席位，并接收连接详情。
* 客户端可以使用自定义过滤器（游戏内 UI）浏览合适的服务器和槽位。
* 服务器通过以下方式验证玩家连接： 联合身份[^1].
* 服务器更新席位容量和元数据，以修改可发现性或触发扩容。

[#automated-scaling](#automated-scaling "mention") （可选功能）配合扩缩策略：

* 监控可用服务器实例、槽位、容量——按区域或自定义条件。
* 通过预热或即时扩缩部署新服务器以增加容量。
* 通过面向限时活动（QA 测试、锦标赛）的隔离策略自动化运维。

{% hint style="info" %}
在游戏发布后， **你的服务器浏览器需要 7x24 运行** 以确保全球玩家都能加入服务器。
{% endhint %}

## ▶️ 开始浏览

了解服务器和玩家（客户端）生命周期，以确保高效使用服务器。

### 验证

所有请求都必须发送一个 `Authorization`  带有你的密钥的 HTTP 标头 **认证令牌：**

<pre><code>Authorization： <a data-footnote-ref href="#user-content-fn-2">xxxxxxxx-e458-4592-b607-c2c28afd8b62</a>
</code></pre>

{% hint style="warning" %}
**请妥善保管你的令牌，切勿泄露！Edgegap 工作人员绝不会向你索要令牌。**
{% endhint %}

{% hint style="info" %}
Matchmaker 令牌和 Server Browser 令牌与 Edgegap API 令牌是分开的。
{% endhint %}

服务器浏览器会自动生成两种令牌：

* **服务器令牌** - 适用于 [服务器 API](#server-lifecycle) 方法，可 [作为应用版本变量注入](/zh/learn/bian-pai/application-and-versions.md#injected-variables).
  * 授予所有 API 方法的访问权限，适合测试、运维或自定义扩缩。
* **客户端令牌** - 适用于 [监控 API 和席位预留 API](#player-lifecycle) 供游戏客户端使用。

{% hint style="success" %}
将客户端令牌存储在游戏后端的密钥库中，以便在生产环境更轻松地轮换令牌。
{% endhint %}

### 发现实例

发现是一个完整初始化的服务器通知服务器浏览器并变为可见的过程 [，可通过搜索或自动分配的预留](#allocate-capacity).

{% hint style="warning" %}
**新** [部署](/zh/learn/bian-pai/deployments.md) **必须创建一个新的实例** 在初始化时，以便跟踪新增容量。
{% endhint %}

{% hint style="info" %}
参见 [#automated-scaling](#automated-scaling "mention") 以了解扩缩策略并自动开始部署。
{% endhint %}

**所需信息** 每个服务器实例包括：

* 在初始化实例时至少定义一个槽位，
* 服务器连接详情——URL、IP、端口信息和位置。

**可选自定义元数据参数** 用于玩家筛选、排序和浏览；例如：

* 槽位信息——团队容量和特定于团队的元数据（例如团队名称），
* 名称和标签——可自定义、唯一、可读且可搜索的标签；
* 兼容性数据——服务器版本或受支持的客户端版本；
* 延迟限定符——城市和区域标识符，以及已分配的 [Ping 信标](/zh/learn/bian-pai/ping-beacons.md) 详情；
* 游戏参数——关卡/场景/地图、游戏模式、难度、所用模组；
* 以及任何其他自定义参数，帮助玩家筛选并找到合适的服务器。

{% hint style="info" %}
上面的元数据参数只是示例，你可以根据需要定义任意数量的参数。
{% endhint %}

{% hint style="success" %}
要序列化嵌套对象，请将其访问路径编码到键中，格式如 `"object.child.property"`.
{% endhint %}

服务器可以 **随时更新实例或槽位元数据** 以修改其可发现性条件。更新元数据时，所有已索引键都必须提供有效值（即使未修改）。

**服务器实例必须定期发送保活心跳** 以验证其持续可用性，并防止玩家加入已崩溃或离线的服务器。若在配置的过期时间内缺少心跳，实例及任何待处理的席位预留将被自动删除。

{% hint style="info" %}
参见 [持久化](/zh/learn/bian-pai/chi-jiu-hua.md) 用于管理持久化世界状态以及 [应用与版本](/zh/learn/bian-pai/application-and-versions.md#active-caching) 用于更快的部署。
{% endhint %}

### 分配容量

实例和槽位容量可以通过两种方式分配，可单独使用也可同时使用：

* [#auto-assigned-reservation](#auto-assigned-reservation "mention") 选择一个以特定扩缩策略启动的服务器，
* [#search-and-browse](#search-and-browse "mention") 让玩家设置过滤器并浏览合适的服务器以手动选择。

{% hint style="success" %}
我们建议从 [#auto-assigned-reservation](#auto-assigned-reservation "mention") 开始，作为更简单的选项。
{% endhint %}

#### 自动分配预留

{% hint style="info" %}
实现自动分配，以 **自动选择服务器**，基于玩家所在区域。
{% endhint %}

玩家可以创建自动分配的预留，只需提供玩家 ID 和扩缩策略名称。服务器浏览器会自动查找一个提供足够可加入容量的实例槽位并预留席位，同时立即返回实例连接详情。

如果没有适合此次预留的实例槽位，则响应：

* **状态码表示策略扩缩状态** 如果正在增加更多容量，
* 如果正在扩容， **标头 `Retry-After`  表示重试前的等待时间（秒）**.

一旦预留完成，你可以跳到 [#connect-to-server](#connect-to-server "mention").

#### 搜索与浏览

{% hint style="info" %}
为以下内容实现自定义搜索 **自定义筛选条件和/或向玩家显示服务器列表。**
{% endhint %}

玩家可以使用自定义过滤和排序列出服务器实例，并 [对结果分页](#pagination) 来找到他们想加入的服务器。每个实例的槽位也可用相同方法搜索。

实例和槽位可以使用内置参数或 [已索引元数据](#configuration):

<table><thead><tr><th width="400">属性</th><th width="140">数据类型</th><th width="105">实例</th><th width="105">槽位</th></tr></thead><tbody><tr><td><code>request_id</code></td><td><code>字符串</code></td><td>✅</td><td>❌</td></tr><tr><td><code>total_joinable_seats</code>, <code>total_available_seats</code></td><td><code>整数</code></td><td>✅</td><td>❌</td></tr><tr><td><code>name</code></td><td><code>字符串</code></td><td>❌</td><td>✅</td></tr><tr><td><code>available_seats</code>, <code>reserved_seats</code></td><td><code>整数</code></td><td>❌</td><td>✅</td></tr><tr><td><code>created_at</code>, <code>updated_at</code></td><td><code>字符串</code></td><td>✅</td><td>✅</td></tr><tr><td><code>metadata.{index}</code> （自定义）</td><td><code>字符串</code>, <code>整数</code>, <code>浮点数</code>, <code>布尔值</code></td><td>✅</td><td>✅</td></tr></tbody></table>

可用的过滤运算符取决于被过滤属性的数据类型：

<table><thead><tr><th width="125">参数</th><th width="135">运算符</th><th>示例过滤器（基于简单示例）</th></tr></thead><tbody><tr><td><code>字符串</code></td><td><p><a data-footnote-ref href="#user-content-fn-3"><code>eq</code></a>  或 <a data-footnote-ref href="#user-content-fn-4"><code>ne</code></a> 或 </p><p><a data-footnote-ref href="#user-content-fn-5"><code>lt</code></a>  或 <a data-footnote-ref href="#user-content-fn-6"><code>le</code></a> 或 </p><p><a data-footnote-ref href="#user-content-fn-7"><code>gt</code></a>  或 <a data-footnote-ref href="#user-content-fn-8"><code>ge</code></a>  或<br><code>contains</code>  或<br></p></td><td><pre><code>?$filter=metadata.custom_name contains '我的游戏'
and metadata.server_version le '1.1.0'
and metadata.server_version ge '1.0.0'
&#x26;$order=metadata.custom_name asc
</code></pre></td></tr><tr><td><code>字符串</code></td><td>字面值<br><code>位于</code>  （筛选）<br><code>rank</code>  （排序）</td><td><pre><code>?$filter=metadata.city in ('芝加哥', '多伦多')
&#x26;$order=rank(metadata.city, '芝加哥', '多伦多')
</code></pre></td></tr><tr><td><code>整数</code>, <code>浮点数</code></td><td><p><a data-footnote-ref href="#user-content-fn-3"><code>eq</code></a>  或 <a data-footnote-ref href="#user-content-fn-4"><code>ne</code></a> 或 </p><p><a data-footnote-ref href="#user-content-fn-5"><code>lt</code></a>  或 <a data-footnote-ref href="#user-content-fn-6"><code>le</code></a> 或 </p><p><a data-footnote-ref href="#user-content-fn-7"><code>gt</code></a>  或 <a data-footnote-ref href="#user-content-fn-8"><code>ge</code></a>  </p></td><td><pre><code>?$filter=metadata.xp_multiplier gt 1.0
&#x26;$order=metadata.xp_multiplier desc
</code></pre></td></tr><tr><td><code>布尔值</code></td><td><a data-footnote-ref href="#user-content-fn-3"><code>eq</code></a>  或 <a data-footnote-ref href="#user-content-fn-4"><code>ne</code></a></td><td><pre><code>?$filter=metadata.allows_new_connections eq true
</code></pre></td></tr></tbody></table>

{% hint style="success" %}
根据自定义 `metadata.city`  在使用以下方式测量后，以获得最佳延迟 [Ping 信标](/zh/learn/bian-pai/ping-beacons.md).
{% endhint %}

{% hint style="info" %}
了解基于游标的 [#pagination](#pagination "mention") 以便让用户获取更多结果。
{% endhint %}

#### 预留席位

**在加入服务器之前，玩家必须预留席位** 以确保该实例提供足够的可用容量并防止过度拥挤。预留可以包含一组玩家或单个玩家。

联合身份：玩家必须在其预留中提供唯一的第三方玩家 ID。一旦他们 [#connect-to-server](#connect-to-server "mention")，就发送相同的 ID 进行服务器端验证。

一旦预留成功，玩家应立即尝试连接。待处理的 **预留将在 30 秒后过期（可配置），除非确认** 由服务器。

**超过槽位可加入席位容量的预留将被拒绝** ([409 冲突](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/409)）。可加入席位是尚未被其他玩家预留的任何可用席位。

### 连接到服务器

席位预留一经创建， **玩家应继续连接到你的部署中的游戏服务器，并通过 netcode RPC 传递其玩家 ID**.

{% tabs %}
{% tab title="Unreal Engine" %}
为了 **从 PIE（编辑器）连接** 在开发和测试期间，按波浪号键 `~`  并输入 `open {URL}:{port}` 并等待编辑器加载地图。

{% hint style="success" %}
如果连接失败或出现黑屏，请参阅我们的 [故障排除指南](/zh/unreal-engine.md#troubleshooting-and-faq-1).
{% endhint %}
{% endtab %}

{% tab title="Unity" %}
为了 **将你的 Unity 编辑器** 或 **游戏客户端** 连接到你的云部署，请输入：

* **部署** **URL** 指向服务器的 IP，通常在 `NetworkManager`  组件中。
* **外部端口** 映射到 [服务器内部监听端口](https://docs.edgegap.com/learn/advanced-features/application-and-versions#port-mapping)，通常在 Transport 组件中。

{% hint style="success" %}
如果发生连接超时或其他问题，请参阅我们的 [故障排除指南](/zh/unity.md#troubleshooting-and-faq-4).
{% endhint %}
{% endtab %}
{% endtabs %}

要验证新的连接， **你的服务器必须发送批量预留确认** 请求，其中包含所有新玩家的 ID，并在响应中接收：

* 将已接受的玩家预留分配到其首选槽位，
* 将已过期的玩家预留分配到其首选槽位，
* 未知玩家 ID 列表。

你的 **服务器决定如何处理已过期的玩家组** 以及是否允许或踢出/封禁已过期或被拒绝的用户。每个 **实例槽位必须立即更新新的可用席位数量** 以确保未来的预留不会超过槽位容量。

{% hint style="info" %}
你的服务器有权更改任何槽位的容量、添加、删除或更新任何槽位——如果待处理预留超过可用席位，则移除此槽位的所有预留。
{% endhint %}

### 放弃服务器

当玩家离开时，你的 **服务器必须增加分配给该槽位的可用席位**.

{% hint style="success" %}
如果你的服务器允许重连期，则服务器可以在更新槽位之前等待。
{% endhint %}

了解 [持久化](/zh/learn/bian-pai/chi-jiu-hua.md#recovery-objectives) 以防止令人沮丧的持久化服务器回滚。

## 🚀 自动扩缩

服务器浏览器兼容多种不同的自动扩缩方法：

* **预热方法** - 严格使用服务器浏览器扩缩策略启动服务器，
* **即时方法** - 通过 [匹配](/zh/learn/pi-pei.md) 和 [使用服务器浏览器进行填充](#allocate-capacity),
* **自定义自动扩缩器** - 通过自定义游戏后端和 [使用服务器浏览器进行填充](#allocate-capacity).

以下指南将重点介绍 **使用扩缩策略进行预热**.

<figure><img src="https://3334189208-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2F0RCiModlkLY6BVAjcrFJ%2Fimage.png?alt=media&amp;token=7f7c5639-13ad-4780-9839-b9e4bd67fe80" alt=""><figcaption><p>服务器浏览器扩缩策略 UI</p></figcaption></figure>

{% hint style="success" %}
停止部署 [Unreal Engine](/zh/unreal-engine.md#stop-deployments), [Unity](/zh/unity.md#stop-deployments)，或 [使用 API](/zh/docs/api/zhuan-yong-fu-wu-qi.md#delete-v1-self-stop-request_id-access_point_id) 以可靠地控制服务器成本。
{% endhint %}

### 监控容量

服务器浏览器将每隔 [`monitoring_interval`](#user-content-fn-9)[^9] .

{% hint style="warning" %}
为了 **防止过度扩容**，请将监控间隔设置得略高于平均服务器启动时间。
{% endhint %}

{% hint style="info" %}
每个策略都应使用一个过滤器（使用 [过滤语法](#search-and-browse)）来监控区域容量。
{% endhint %}

你配置的 [`minimum_active_instances`](#user-content-fn-10)[^10]  数量可被视为以下任一项：

* **固定容量** 你希望始终保持运行的部署数量，
* **预热待命** 部署缓冲，用于掩盖初始化延迟。

#### 固定容量

固定容量策略不应使用与席位相关的过滤器。

这种策略配置最常用于质量保证、锦标赛、封闭 Alpha、发行商演示或其他容量有限的活动。

或者，具有 [持久化](/zh/learn/bian-pai/chi-jiu-hua.md) 的游戏通常希望维持长期运行的服务器，尤其是在向玩家提供预配时 [持久化](/zh/learn/bian-pai/chi-jiu-hua.md#community-servers).

{% hint style="info" %}
扩缩策略可帮助你自动重启并即时回收崩溃的服务器。
{% endhint %}

#### 预热待命

预热策略应使用可加入席位过滤器来监控容量使用情况。

如果满足以下条件，请启动待命空闲服务器，以抢先满足玩家需求：

* 你正在全球发布，并预计在短时间内快速涌入大量玩家，
* 或者服务器初始化需要超过 30 秒（不包括部署时间[^11]),
* 或者服务器采用网格化策略，需要更复杂的网络拓扑。

### 部署服务器

当已发现的实例数量*s* 降至策略的最低活动实例数以下时。部署将在每个监控间隔之后无限重试，直到 [`deployment_registration_period`](#user-content-fn-9)[^9]  结束。

{% hint style="warning" %}
**务必验证你的** [**服务器自动发现**](#discover-instance) **与策略过滤器的集成，否则你的策略可能会无限循环并创建大量未使用的部署！**
{% endhint %}

{% hint style="info" %}
部署可使用 [私有舰队](/zh/learn/bian-pai/si-you-jian-dui.md) （通过溢出到云）或直接使用云。
{% endhint %}

可用参数包括（[参见完整 API 规范](/zh/docs/api/zhuan-yong-fu-wu-qi.md#private-fleets)):

* [**应用和版本**](/zh/learn/bian-pai/application-and-versions.md) - 构建版本、资源和其他编排参数，
* **用户** - 一组用于偏好的单一地理坐标 [服务器部署位置](/zh/learn/bian-pai/deployments.md#regional-standby),
* [**私有主机 ID**](/zh/learn/bian-pai/si-you-jian-dui.md) - 云端留空，或指定所需区域中的主机，
* [**tags**](/zh/learn/bian-pai/deployments.md#dashboard-monitoring) - 使用策略名称进行标记，以便之后查找通过此策略启动的部署，
* [**环境变量**](/zh/learn/bian-pai/deployments.md#custom-variables) - 向服务器传递自定义参数和密钥，
* [**Webhook 回调**](/zh/learn/bian-pai/deployments.md#webhooks-and-postbacks) - 通知你的游戏后端（或匹配器）有关部署生命周期事件，
* [**需要缓存位置**](/zh/learn/bian-pai/application-and-versions.md#active-caching) - 如果你只希望在缓存位置获得更快的部署。

### 示例策略

如有需要，可测试并修改这些策略中的任何一个。大多数游戏会使用多个策略。

{% tabs %}
{% tab title="🍀 QA 实例" %}
一种简单策略，用于始终保持一台服务器部署以供测试。

<pre class="language-json" data-title=""><code class="lang-json">{
  "name": "sb-qa-pool",
<strong>  <a data-footnote-ref href="#user-content-fn-12">"filter"</a>: "metadata.policy_name eq 'sb-qa-pool'",
</strong>  "deployment_request": {
    "private_host_ids": [],
    "application": <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "version": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "users": [
      {
        "user_type": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">user_data</a>": {
<strong>          "latitude": 41.881832,
</strong><strong>          "longitude": -87.623177
</strong>        }
      }
    ],
<strong>    "tags": ["sb-qa-pool"],
</strong>    "environment_variables": [
      {
<strong>        "key": "SB_SCALING_POLICY_NAME",
</strong><strong>        "value": "sb-qa-pool",
</strong>        "is_hidden": false
      }
    ]
  },
<strong>  "minimum_active_instances": 1
</strong>}
</code></pre>

{% endtab %}

{% tab title="🌡️ 预热区域" %}
为预期需求提前启动 10 倍部署。按区域复制。

<pre class="language-json" data-title=""><code class="lang-json">{
  "name": "sb-v1.0.0-chicago",
<strong>  <a data-footnote-ref href="#user-content-fn-16">"filter"</a>: "total_joinable_seats gt 0 and metadata.policy_name eq 'sb-v1.0.0-chicago'",
</strong>  "deployment_request": {
    "private_host_ids": [],
    "application": <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "version": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "users": [
      {
        "user_type": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">user_data</a>": {
<strong>          "latitude": 41.881832,
</strong><strong>          "longitude": -87.623177
</strong>        }
      }
    ],
<strong>    "tags": ["sb-v1.0.0-chicago"],
</strong>    "environment_variables": [
      {
<strong>        "key": "SB_SCALING_POLICY_NAME",
</strong><strong>        "value": "sb-v1.0.0-chicago",
</strong>        "is_hidden": false
      }
    ]
  },
<strong>  <a data-footnote-ref href="#user-content-fn-17">"minimum_active_instances"</a>: 10
</strong>}
</code></pre>

{% endtab %}

{% tab title="🔒 MMO" %}
当可用容量低于阈值时添加部署。按区域复制。

<pre class="language-json" data-title=""><code class="lang-json">{
<strong>  "name": "sb-mmo-chicago",
</strong><strong>  <a data-footnote-ref href="#user-content-fn-18">"filter"</a>: "total_joinable_seats gt 5 and metadata.policy_name eq 'sb-mmo-chicago'",
</strong>  "deployment_request": {
<strong>    <a data-footnote-ref href="#user-content-fn-19">"private_host_ids"</a>["alpha-north-america-95fab093"],
</strong>    "application": <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "version": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "users": [
      {
        "user_type": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">user_data</a>": {
<strong>          "latitude": 41.881832,
</strong><strong>          "longitude": -87.623177
</strong>        }
      }
    ],
<strong>    "tags": ["sb-mmo-chicago"],
</strong>    "environment_variables": [
      {
<strong>        "key": "SB_SCALING_POLICY_NAME",
</strong><strong>        "value": "sb-mmo-chicago",
</strong>        "is_hidden": false
      }
    ],
<strong>    <a data-footnote-ref href="#user-content-fn-20">"webhook_on_terminated"</a>: {
</strong>      "url": "https://my-webhook.com"
    }
  },
<strong>  "minimum_active_instances": 3
</strong>}
</code></pre>

{% endtab %}

{% tab title="🔑 社区" %}
每个服务器所有者一个策略，传入自定义的所有者定义密码。

<pre class="language-json" data-title=""><code class="lang-json">{
<strong>  "name": "sb-owner-jnjnc8mid",
</strong><strong>  <a data-footnote-ref href="#user-content-fn-21">"filter"</a>: "metadata.policy_name eq 'sb-owner-jnjnc8mid'",
</strong>  "deployment_request": {
<strong>    <a data-footnote-ref href="#user-content-fn-19">"private_host_ids"</a>["alpha-north-america-95fab093"],
</strong>    "application": <a data-footnote-ref href="#user-content-fn-13">"my-game-server"</a>,
    "version": <a data-footnote-ref href="#user-content-fn-14">"2024.01.30-16.23.00-UTC"</a>,
    "users": [
      {
        "user_type": "geo_coordinates",
        "<a data-footnote-ref href="#user-content-fn-15">user_data</a>": {
<strong>          "latitude": 41.881832,
</strong><strong>          "longitude": -87.623177
</strong>        }
      }
    ],
<strong>    "tags": ["community", "sb-owner-jnjnc8mid"],
</strong>    "environment_variables": [
      {
<strong>        "key": "SB_SCALING_POLICY_NAME",
</strong><strong>        "value": "sb-owner-jnjnc8mid",
</strong>        "is_hidden": false
      },
      {
<strong>        "key": "SB_SERVER_PASSWORD",
</strong><strong>        "value": "password1234",
</strong>        "is_hidden": false
      }
    ],
<strong>    <a data-footnote-ref href="#user-content-fn-22">"webhook_on_ready"</a>: {
</strong>      "url": "https://my-webhook.com"
    },
<strong>    <a data-footnote-ref href="#user-content-fn-23">"webhook_on_error"</a>: {
</strong>      "url": "https://my-webhook.com"
    },
<strong>    <a data-footnote-ref href="#user-content-fn-20">"webhook_on_terminated"</a>: {
</strong>      "url": "https://my-webhook.com"
    }
  },
  "minimum_active_instances": 1
}
</code></pre>

{% endtab %}
{% endtabs %}

## ⚙️ 配置

服务器浏览器 API 会在服务器浏览器启动时根据你的 JSON 配置生成。你可以指定自定义的过期/注册时间窗口，以及自定义元数据：

{% tabs %}
{% tab title="🍀 简单示例" %}

<pre class="language-json" data-title="sb-simple-example-v1-1-0.json"><code class="lang-json">{
	"version": "1.1.0",
	"server_instances": {
		"expiration_period": "1m",
		"<a data-footnote-ref href="#user-content-fn-24">索引</a>": {
			"policy_name": "string",
			"name": "string"
		}
	},
	"server_instance_slots": {
		"<a data-footnote-ref href="#user-content-fn-24">索引</a>": {}
	},
	"seat_reservations": {
		"expiration_period": "30s"
	},
	"scaling_policies": {
		"monitoring_interval": "10s",
		"deployment_registration_period": "1m"
	},
  "rate_limits": {
    "per_client_ip": 5
  }
}
</code></pre>

{% endtab %}

{% tab title="🎈 社交游戏" %}

<pre class="language-json" data-title="sb-social-example-v1-1-0.json"><code class="lang-json">{
	"version": "1.1.0",
	"server_instances": {
		"expiration_period": "15s",
		"<a data-footnote-ref href="#user-content-fn-24">索引</a>": {
			"policy_name": "string",
			"name": "string",
			"third_party_id": "string",
			"level": "string",
			"mode": "string",
			"difficulty": "string",
			"seed": "string",
			"max_players": "int",
			"app_version": "string",
			"location.city": "string"
		}
	},
	"server_instance_slots": {
		"<a data-footnote-ref href="#user-content-fn-24">索引</a>": {
			"third_party_id": "string",
			"max_players": "int",
			"avg_latency": "int",
			"player_ids": "string"
		}
	},
	"seat_reservations": {
		"expiration_period": "30s"
	},
	"scaling_policies": {
		"monitoring_interval": "10s",
		"deployment_registration_period": "30s"
	},
  "rate_limits": {
    "per_client_ip": 5
  }
}
</code></pre>

{% endtab %}

{% tab title="🤝 合作游戏" %}

<pre class="language-json" data-title="sb-cooperative-example-v1-1-0.json"><code class="lang-json">{
	"version": "1.1.0",
	"server_instances": {
		"expiration_period": "15s",
		"<a data-footnote-ref href="#user-content-fn-24">索引</a>": {
			"policy_name": "string",
			"name": "string",
			"third_party_id": "string",
			"level": "string",
			"mode": "string",
			"difficulty": "string",
			"avg_rank": "int",
			"max_players": "int",
			"app_version": "string",
			"tags": "string",
			"match_id": "string",
			"location.city": "string"
		}
	},
	"server_instance_slots": {
		"<a data-footnote-ref href="#user-content-fn-24">索引</a>": {
			"third_party_id": "string",
			"max_players": "int",
			"player_ids": "string"
		}
	},
	"seat_reservations": {
		"expiration_period": "30s"
	},
	"scaling_policies": {
		"monitoring_interval": "10s",
		"deployment_registration_period": "30s"
	},
  "rate_limits": {
    "per_client_ip": 5
  }
}
</code></pre>

{% endtab %}

{% tab title="⚔️ 竞技游戏" %}

<pre class="language-json" data-title="sb-competitive-example-v1-1-0.json"><code class="lang-json">{
	"version": "1.1.0",
	"server_instances": {
		"expiration_period": "15s",
		"<a data-footnote-ref href="#user-content-fn-24">索引</a>": {
			"policy_name": "string",
			"name": "string",
			"third_party_id": "string",
			"avg_rank": "int",
			"max_players": "int",
			"is_ranked": "bool",
			"app_version": "string",
			"cpu_frequency": "int",
			"match_id": "string",
			"location.city": "string"
		}
	},
	"server_instance_slots": {
		"<a data-footnote-ref href="#user-content-fn-24">索引</a>": {
			"third_party_id": "string",
			"max_players": "int",
			"avg_rank": "int",
			"avg_latency": "int"
		}
	},
	"seat_reservations": {
		"expiration_period": "30s"
	},
	"scaling_policies": {
		"monitoring_interval": "10s",
		"deployment_registration_period": "15s"
	},
  "rate_limits": {
    "per_client_ip": 5
  }

</code></pre>

{% endtab %}
{% endtabs %}

{% hint style="info" %}
为了获得最佳性能，请省略未用于筛选或排序的索引。未索引的参数仍然可以通过 API 方法或特定于引擎的 SDK 进行设置和读取。
{% endhint %}

## ☁️ 托管集群

服务器浏览器由 Edgegap 便捷地 24/7 全天候托管和管理。

选择最适合您目标的托管选项：

* **免费集群（共享）** 用于测试所有功能并探索与您的设计的协同效应，
  * 会在 3 小时后自动关闭，需要重启才能继续测试。
* **私有集群** **（专用）** 以确保为您的生产需求提供稳定的环境，
  * 选择您的区域并为实时游戏获得 24/7 支持，以便自信发布。

#### 私有集群层

我们目前提供 [3 个私有集群等级](https://edgegap.com/resources/pricing#managed-infrastructure) 以满足每个人的需求：

<table><thead><tr><th width="160">等级</th><th align="right">爱好者等级</th><th align="right">工作室等级</th><th align="right">企业等级</th></tr></thead><tbody><tr><td>最适合用于</td><td align="right">爱好者，<br>独立开发者</td><td align="right">商业发布</td><td align="right">高流量上线</td></tr><tr><td>资源</td><td align="right">1 vCPU + 2GB 内存</td><td align="right">6 vCPU + 12GB 内存</td><td align="right">18 vCPU + 48GB 内存</td></tr><tr><td>冗余</td><td align="right">1 个虚拟节点</td><td align="right">3 个虚拟节点</td><td align="right">3 个虚拟节点</td></tr><tr><td>限流（请求/秒）</td><td align="right">200</td><td align="right">750</td><td align="right">2,000</td></tr><tr><td>价格，每小时</td><td align="right">$0.0312</td><td align="right"> $0.146</td><td align="right">$0.548</td></tr><tr><td><strong>价格，30 天</strong><br>（持续使用）</td><td align="right"><strong>$22.464</strong></td><td align="right"><strong>$105.12</strong></td><td align="right"><strong>$394.56</strong></td></tr></tbody></table>

🌟 一键将免费实例升级到私有集群，获得由 Edgegap 团队维护的高可用托管，并为正式发布的游戏提供 24/7 实时支持。

您的实例资源需求将取决于以下因素：

* **玩家数量，** 更多玩家 ⇒ 更多 API 请求和更高的 CPU 使用率，
* **每位玩家的请求数，** 重试越频繁 ⇒ CPU 资源使用率越高，
* **服务器数量，** 更多服务器 ⇒ 更高的 CPU 使用率和内存使用率，
* **客户端重试回退逻辑** - 在不使用抖动退避的情况下重试 ⇒ [惊群效应](https://en.wikipedia.org/wiki/Thundering_herd_problem),
* **平均对局时长** - 会话越短 ⇒ 生命周期事件频率越高。

{% hint style="info" %}
我们的集群使用配备 AMD/Intel CPU 的云主机，主频为 2.4 - 3.2 GHz。
{% endhint %}

## 📗 API

**考虑使用我们的 SDK 来** [**Unreal Engine**](/zh/unreal-engine/developer-tools.md) **或** [**Unity**](/zh/unity/fu-wu-qi-liu-lan-qi.md) **以预置示例开始。**

{% hint style="info" %}
Unity/Android - 考虑 [使用原始字符串插值](https://www.c-sharpcorner.com/article/convert-string-to-json-in-c-sharp/) 以防止对硬编码 JSON 的代码剥离。
{% endhint %}

{% hint style="success" %}
**Swagger Web 界面**: 部署您的托管服务会生成一个 OpenAPI 规范和一个便捷的 Web 界面，可用于测试边缘情况或验证负载结构。
{% endhint %}

{% file src="/files/5d9bdffa26f5585b2fb52b0eb1e65c80d24947c3" %}

导入 API 规范到 [Scalar API Web 客户端](https://client.scalar.com/workspace/default/request/default) 或 [Swagger Editor](https://editor.swagger.io/) 查看详细信息。

### 速率限制

为了保护你的实例免于超出突发容量并崩溃，我们限制每秒每个客户端的公网 IP 地址可发起的请求数量。

该限制由以下配置： [#configuration](#configuration "mention") 参数 `rate_limits.per_client_ip`.

{% hint style="warning" %}
如果您的游戏客户端在收到响应时不重试请求 `429 请求过多` **某些玩家可能无法加入服务器** 在短时突发和流量高峰期间。
{% endhint %}

{% hint style="success" %}
**我们建议在开发期间使用较低的速率限制测试应用行为（1 req/s）。**
{% endhint %}

#### 负载测试

在类似生产环境中进行负载测试会产生部署托管成本。请参见各层级对应的资源和价格 [我们的定价页面](https://edgegap.com/resources/pricing#matchmaker).

{% hint style="warning" %}
**使用** [**私有集群**](#private-cluster-tiers) **用于压力测试。** 免费实例严格仅限于开发测试。
{% endhint %}

在设计负载测试时， **请考虑真实的玩家行为模式**:

| 真实场景                                  | 不现实的流量模式                       |
| ------------------------------------- | ------------------------------ |
| ✅ 玩家逐步加入游戏，在数小时内逐渐提升请求/秒。             | ❌ 所有玩家协调一致，在完全相同的一秒内访问 API。    |
| ✅ 玩家在重试之间等待的时间逐步增加（例如 1s-5s-10s-10s）。 | ❌ 所有玩家在收到后立即重试 `429 请求过多`  响应。 |
| ✅ 大多数玩家会在较短时间内（10-60 秒）收到分配并停止轮询。     | ❌ 所有玩家在收到分配后仍会继续轮询一段固定时间。      |
| ✅ 大多数玩家在重新开始新会话之前会先完成当前游戏（需要一些时间）。    | ❌ 所有玩家在收到服务器分配后立即重新开始新的会话。     |
| ✅ 峰值流量每天持续约 6 小时，之后部分时区的流量会下降。        | ❌ 峰值流量全天 24 小时持续，所有玩家日夜不停地玩。   |

#### 负载下的行为

**客户端请求** - 如果任何客户端达到配置的按 IP req/s 速率限制，他们会收到 `429 请求过多`  并且在重试前应退避（等待）。

**部署请求** - 如果扩缩容策略触发的部署数量超过你组织允许的 req/s 限制，你的服务器浏览器将每个监控间隔自动重试一次，并在所有实例策略之间采用加权轮询策略。

策略权重根据每轮计划部署数量得出，在所有扩缩容策略之间平均分配可用的部署配额。

### 分页

**服务器浏览器提供游标分页，以按特定顺序逐步获取筛选结果。** 这种方法在获取更多结果时需要发送游标（起始点）和页面大小（响应项数），而不是传统的 limit-offset 分页。

{% hint style="info" %}
游标分页结合我们的元数据实例索引系统，为筛选高度动态的数据提供了最一致且最灵活的用户体验。
{% endhint %}

首要任务是让用户在第一页就找到合适的服务器。

为了获得最佳体验，我们建议显示前几页的缓存结果，并且只在用户点击搜索或手动请求刷新时才更新结果。

## 🔖 更新日志

#### 语义化版本控制

我们的开发者工具和托管服务使用官方 [语义化版本控制](https://semver.org/)，表明哪些更新是✅安全的（次要、补丁），哪些可能包含⚠️破坏性更改（主要）。

**一旦某个版本发布，它将永远不会被修改/更改**.

{% hint style="info" %}
**服务器浏览器的最新版本是 `1.1.0`** 。敬请关注 [更新和公告](/zh/docs/release-notes.md).
{% endhint %}

[^1]: 第三方玩家标识符

[^2]: 示例值

[^3]: 等于

[^4]: 不等于

[^5]: 低于

[^6]: 小于或等于

[^7]: 大于

[^8]: 大于或等于

[^9]: 参见配置

[^10]: 参见示例策略

[^11]: 使用主动缓存可轻松缩短部署时间

[^12]: * 固定容量
    * 假定实例会通过注入变量在元数据中提供策略名称

[^13]: 替换为你自己的应用名称

[^14]: 替换为你自己的应用版本

[^15]: 芝加哥坐标

[^16]: * 当发现少于 10 个可加入实例时部署
    * 假定实例会通过注入变量在元数据中提供策略名称

[^17]: 我们预计芝加哥区域至少有 10 个部署

[^18]: * 当找到少于 3 个且有 5 个或更少可加入座位的实例时部署
    * 假定实例会通过注入变量在元数据中提供策略名称

[^19]: 如果有可用容量，优先使用私有集群

[^20]: 当服务器重启时通知游戏后端

[^21]: * 不监控容量
    * 假定实例会通过注入变量在元数据中提供策略名称

[^22]: 就绪时通知游戏后端

[^23]: 当部署失败时通知游戏后端

[^24]: 索引包含用于筛选或排序的自定义元数据参数
