> 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).

# 服务器浏览器

设置 Edgegap 的服务器浏览器，让玩家能够在最近区域发现并加入持久化游戏服务器，或从游戏内列表 UI 中进行选择。

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

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

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

{% hint style="success" %}
想要在不允许选择服务器的情况下，依据严格规则匹配玩家？请考虑 [匹配系统](/zh/learn/pi-pei-xi-tong.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" %}
游戏发布后， **你的服务器浏览器需要 7×24 小时运行** 以确保全球各地的玩家都能加入服务器。
{% endhint %}

## ▶️ 开始浏览

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

**快速开始——将我们的 SDK 添加到你的游戏中**:

* 虚幻引擎 [开发者工具](/zh/unreal-engine/developer-tools.md#integration-kit):
  * [阅读文档](https://egik.betide.studio/) 由 Betide Studios 制作，
  * [从 Fab Marketplace 安装](https://www.fab.com/listings/ff17ad88-12a1-49cf-9a41-31695ed11e16) （个人用途免费使用）。
* Unity [开发者工具](/zh/unity/developer-tools.md#software-development-kit):
  * [使用 Unity Package Manager 免费安装包](https://github.com/edgegap/edgegap-unity-sdk),
  * [探索我们的入门指南和完整示例](/zh/unity/matchmaking.md).

### 身份验证

所有请求都必须发送一个 `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 方法的访问权限，便于测试、DevOps 或自定义扩缩容。
* **客户端令牌** — 适用于 [监控 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>名称</code></td><td><code>字符串</code></td><td>❌</td><td>✅</td></tr><tr><td><code>可用席位</code>, <code>已预留席位</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>包含</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>in</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-xi-tong.md) 和 [由 Server Browser 补足](#allocate-capacity),
* **自定义自动扩缩器** — 通过自定义游戏后端启动，并 [由 Server Browser 补足](#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) — 云端可留空，或指定所需区域中的主机，
* [**标签**](/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 %}

## ⚙️ 配置

Server Browser API 会在 Server Browser 启动时使用您的 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 %}

## ☁️ 托管集群

Server Browser 由 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">200</td><td align="right">750</td><td align="right">2,000</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">$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，时钟速度最高可达 3.7 GHz。
{% endhint %}

## 📗 API

**不妨参考我们的 SDK，** [**Unreal Engine**](/zh/unreal-engine/developer-tools.md) **或** [**Unity**](/zh/unity/server-browser.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 请求/秒）测试应用行为。**
{% 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 每秒请求速率限制，他们将收到 `429 请求过多`  ，并且在重试前应先退避（等待）。

**部署请求** - 如果缩放策略触发的部署数量超过您组织允许的 req/s 限制，您的 server browser 将在每个监控间隔自动重试，并在所有实例策略之间使用加权轮询策略。

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

### 分页

**Server Browser 提供游标分页，以按特定顺序逐步获取筛选结果。** 这种方式需要在获取更多结果时发送游标（起始点）和页大小（响应项数量），不同于传统的 limit-offset 分页。

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

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

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

## 🔖 更新日志

请查看 [发行说明](/zh/docs/release-notes.md) 以获取此服务的最新更新日志。

#### 语义化版本控制

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

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

{% hint style="warning" %}
**要升级服务版本 - 停止、编辑、重启。** 快速重启不会应用版本更改。
{% 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]: 索引包含您用于筛选或排序的自定义元数据参数
