Azure Web PubSub 服务内部情况

借助 Azure Web PubSub 服务,你可以通过使用简单的 WebSocket 连接来轻松发布/订阅消息。

  • 可用任何支持 Websocket 的语言编写客户端。
  • 一个连接中同时支持文本消息和二进制消息。
  • 简单的协议允许客户端直接相互发布消息。
  • 该服务可管理你的 WebSocket 连接。

术语

  • 服务:Azure Web PubSub 服务。
  • Connection:一种连接(也称为客户端或客户端连接),它是客户端与 Web PubSub 服务之间的逻辑关系。 通过“连接”,客户端和服务可以进行一系列有状态交互。 使用不同协议的 Connections 可能表现不同,例如,某些连接仅限于网络连接的持续时间,而其他连接则可以跨越客户端和服务之间的多个连续网络连接

  • 中心:中心是客户端连接集的逻辑概念。 通常将一个中心用于一种方案,例如聊天中心或通知中心。 当客户端连接进行连接时,它会连接到某个中心,并且在其生存期内属于该中心。 客户端连接连接到中心后,中心就会存在。 不同的应用程序可以使用不同的中心名称共享一个 Azure Web PubSub 服务。 虽然中心数量没有严格的限制,但与相比,中心使用更多的服务负载。 建议使用预先确定的中心集,而不是动态生成中心。

  • 组:组是与中心的连接的子集。 可以随时向组添加客户端连接或者从组中删除客户端连接。 例如,当某个客户端加入聊天室,或某个客户端离开聊天室,此类聊天室可以看成是一个组。 一个客户端可以加入多个组,一个组可以包含多个客户端。 组类似于组“会话”,有人加入组时,就创建组会话,当组中没有人时,会话就会消失。 发送到组的消息将传递到连接至组的所有客户端。

  • 用户:与 Web PubSub 的连接可以属于一个用户。 用户可能具有多个连接,例如当单个用户跨多个设备或多个浏览器选项卡进行连接时。

  • 消息:客户端连接后,可以通过 WebSocket 连接将消息发送到上游应用程序或是从上游应用程序接收消息。 消息可以采用纯文本、二进制或 JSON 格式,最大为 1 MB。

  • 客户端事件:系统会在客户端连接生命周期内创建事件。 例如,某个简单的 WebSocket 客户端连接在尝试连接到服务时会创建 connect 事件,在成功连接到服务时会创建 connected 事件,在向服务发送消息时会创建 message 事件,在从服务断开连接时会创建 disconnected 事件。 有关客户端事件的详细信息,请参阅客户端协议部分。

  • 事件处理程序:事件处理程序包含用于处理客户端事件的逻辑。 通过门户或 Azure CLI 在服务中预先注册和配置事件处理程序。 详细信息请参阅事件处理程序部分。

  • 事件侦听器(预览版):事件侦听器只侦听客户端事件,但不能通过客户端的响应来干扰客户端的生存期。 有关详细信息,请参阅事件处理程序部分。

  • 服务器:服务器可以处理客户端事件、管理客户端连接或将消息发布到组。 事件处理程序和事件侦听器都被视为服务器端。 有关服务器的详细信息,请参阅服务器协议部分。

工作流

显示 Web PubSub 服务工作流的关系图。

如上图所示的工作流:

  1. 客户端使用 WebSocket 传输连接到服务的 /client 终结点。 服务会将 WebSocket 的每一帧转接到配置上游(服务器)。 WebSocket 连接可以连接到任何自定义子协议以供服务器处理,也可以与服务支持的子协议 json.webpubsub.azure.v1(允许客户端直接执行发布/订阅操作)进行连接。 详情请参阅客户端协议
  2. 对于不同的客户端事件,服务将使用 CloudEvents 协议调用服务器。 CloudEvents 指与协议无关的标准定义,用于说明云原生计算基金会 (CNCF) 托管的事件的结构和元数据。 CloudEvents 协议的详细实现依赖于服务器角色,如服务器协议中所述。
  3. Web PubSub 可以使用 REST API 调用服务,将消息发送到客户端或管理已连接的客户端。 详情请参阅服务器协议

客户端协议

客户端连接使用 WebSocket 协议连接到服务的 /client 终结点。 WebSocket 协议通过单个 TCP 连接提供全双工信道,并于 2011 年由 IETF 标准化为 RFC 6455。 大多数语言都提供本机支持来启动 WebSocket 连接。

我们的服务支持两种类型的客户端:

简单的 WebSocket 客户端

顾名思义,简单的 WebSocket 客户端就是一个简单的 WebSocket 连接。 此客户端还有自定义子协议。

例如,在 JS 中,可使用以下代码创建简单的 WebSocket 客户端。

// simple WebSocket client1
var client1 = new WebSocket("wss://test.webpubsub.azure.cn/client/hubs/hub1");

// simple WebSocket client2 with some custom subprotocol
var client2 = new WebSocket(
  "wss://test.webpubsub.azure.cn/client/hubs/hub1",
  "custom.subprotocol"
);

简单的 WebSocket 客户端遵循客户端<->服务器体系结构,如以下序列图所示:显示客户端连接顺序的关系图。

  1. 当客户端启动 WebSocket 握手时,服务会尝试调用 connect 事件处理程序执行 WebSocket 握手。 开发人员可使用此处理程序来处理 WebSocket 握手,确定要使用的子协议,对客户端进行身份验证,并将客户端加入组。
  2. 成功连接客户端后,服务将调用 connected 事件处理程序。 其可用作通知,并且不会阻止客户端发送消息。 开发人员可使用此处理程序执行数据存储,并且可通过向客户端发送消息来进行响应。 服务还会将 connected 事件推送到所有相关的事件侦听器(如果有)。
  3. 当客户端发送消息时,服务会向事件处理程序触发 message 事件。 此事件包含在 WebSocket 帧中发送的消息。 代码需要在此事件处理程序中调度消息。 如果事件处理程序返回不成功的响应代码,服务将断开客户端连接。 该服务还会将 message 事件推送到所有相关的事件侦听器(如果有)。 如果服务找不到任何已注册的服务器来接收消息,该服务还会断开客户端连接。
  4. 当客户端断开连接时,服务会在检测到连接中断问题时尝试在事件处理程序中触发 disconnected 事件。 服务还会将 disconnected 事件推送到所有相关的事件侦听器(如果有)。

方案

这些连接可用于典型的客户端-服务器体系结构,其中客户端向服务器发送消息,服务器使用事件处理程序处理传入的消息。 当客户在应用程序逻辑中应用现有子协议时,也可使用此类连接。

PubSub WebSocket 客户端

服务还支持名为 json.webpubsub.azure.v1 的特定子协议,该子协议允许客户端直接执行发布/订阅操作,而不是往返于上游服务器。 我们使用 PubSub WebSocket 客户端支持的 json.webpubsub.azure.v1 子协议来调用 WebSocket 连接。 有关详细信息,请参阅 GitHub 上的 Web PubSub 客户端规范

例如,在 JS 中,可使用以下代码创建 PubSub WebSocket 客户端。

// PubSub WebSocket client
var pubsub = new WebSocket(
  "wss://test.webpubsub.azure.cn/client/hubs/hub1",
  "json.webpubsub.azure.v1"
);

PubSub WebSocket 客户端可以:

  • 加入组,例如:

    {
      "type": "joinGroup",
      "group": "<group_name>"
    }
    
  • 退出组,例如:

    {
      "type": "leaveGroup",
      "group": "<group_name>"
    }
    
  • 将消息发布到组,例如:

    {
      "type": "sendToGroup",
      "group": "<group_name>",
      "data": { "hello": "world" }
    }
    
  • 将自定义事件发送到上游服务器,例如:

    {
      "type": "event",
      "event": "<event_name>",
      "data": { "hello": "world" }
    }
    

PubSub WebSocket 子协议包含 json.webpubsub.azure.v1 子协议的详细信息。

你注意到,对于简单的 WebSocket 客户端服务器必须具有角色才能从客户端接收 message 事件。 简单的 WebSocket 连接在发送消息时始终会触发 message 事件,并且始终依赖于服务器端处理消息以及执行其他操作。 借助 json.webpubsub.azure.v1 子协议,授权客户端可以加入组,并可直接发布消息到组。 该子协议还可以通过自定义消息所属的事件,将消息路由到不同的事件处理程序/事件侦听器。

方案

当客户端想要相互对话时,可以使用此类客户端。 若客户端享有相关权限,则可从 client2 向服务发送消息,服务可直接将消息传送给 client1

客户端 1:

var client1 = new WebSocket(
  "wss://xxx.webpubsub.azure.cn/client/hubs/hub1",
  "json.webpubsub.azure.v1"
);
client1.onmessage = (e) => {
  if (e.data) {
    var message = JSON.parse(e.data);
    if (message.type === "message" && message.group === "Group1") {
      // Only print messages from Group1
      console.log(message.data);
    }
  }
};

client1.onopen = (e) => {
  client1.send(
    JSON.stringify({
      type: "joinGroup",
      group: "Group1",
    })
  );
};

客户端 2:

var client2 = new WebSocket("wss://xxx.webpubsub.azure.cn/client/hubs/hub1", "json.webpubsub.azure.v1");
client2.onopen = e => {
    client2.send(JSON.stringify({
        type: "sendToGroup",
        group: "Group1",
        data: "Hello Client1"
    });
};

如上例所示,client2 通过向 client1 所在的 Group1 发布消息,来直接向 client1 发送数据。

客户端事件摘要

客户端事件分为两种类别:

  • 同步事件(阻止):同步事件会阻止客户端工作流。
    • connect:此事件仅适用于事件处理程序。 当客户端启动 WebSocket 握手时会触发事件,开发人员可以使用 connect 事件处理程序来处理 WebSocket 握手,确定要使用的子协议,对客户端进行身份验证,并将客户端加入组。
    • message:当客户端发送消息时会触发此事件。
  • 异步事件(非阻止)异步事件不会阻止客户端工作流。 而是将通知发送到服务器。 当此类事件触发失败时,服务会记录错误详细信息。
    • connected:当客户端成功连接到服务时会触发此事件。
    • disconnected:当客户端与服务断开连接时会触发此事件。

客户端消息限制

WebSocket 每一帧允许的最大消息大小为 1MB。

客户端身份验证

身份验证工作流

客户端使用签名的 JWT 令牌连接到服务。 上游还可拒绝充当传入客户端的 connect 事件处理程序的客户端。 事件处理程序通过指定客户端在 Webhook 响应中具有的 userIdrole 来对客户端进行身份验证,或者拒绝客户端并显示 401 错误。 详情请参阅事件处理程序部分。

工作流如下图所示。

显示客户端身份验证工作流的关系图。

客户端只有在获得授权时才能向其他客户端发布。 客户端的 role 确定客户端拥有的初始权限:

角色 权限
未指定 客户端可以发送事件。
webpubsub.joinLeaveGroup 客户端可以加入/退出任何组。
webpubsub.sendToGroup 客户端可以向任何组发布消息。
webpubsub.joinLeaveGroup.<group> 客户端可以加入/退出 <group> 组。
webpubsub.sendToGroup.<group> 客户端可以向 <group> 组发布消息。

服务器端也可以通过服务器协议动态授予或撤消客户端的权限,如后面部分中所示。

服务器协议

服务器协议为服务器提供处理客户端事件以及管理客户端连接和组的功能。

服务器协议通常包含三种角色:

  1. 事件处理程序
  2. “ODBC 目标编辑器”
  3. 事件侦听器

事件处理程序

事件处理程序可处理传入的客户端事件。 通过门户或 Azure CLI 在服务中注册和配置事件处理程序。 触发客户端事件时,服务可以标识是否处理该事件。 现在,我们使用 PUSH 模式调用事件处理程序。 服务器端的事件处理程序会公开一个可公开访问的终结点,供服务在事件触发时调用。 其可用作 Webhook。

Web PubSub 服务使用 CloudEvents HTTP 协议将客户端事件传送到上游 Webhook。

服务将为每个事件构建一个 HTTP POST 请求并发送到已注册的上游,并且需要 HTTP 响应。

从服务发送到服务器的数据一律采用 CloudEvents binary 格式。

显示 Web PubSub 服务事件推送模式的关系图。

上游和验证

首次使用之前,需要通过门户或 Azure CLI 在服务中注册和配置事件处理程序。 触发客户端事件时,服务可以标识是否必须处理该事件。 对于公共预览版,我们使用 PUSH 模式调用事件处理程序。 服务器端的事件处理程序会公开一个可公开访问的终结点,供服务在事件触发时调用。 其可用作 Webhook 上游

URL 可以使用 {event} 参数为 Webhook 处理程序定义 URL 模板。 当客户端请求进入时,服务会动态计算 Webhook URL 的值。 例如,当请求 /client/hubs/chat 传入时,为中心 http://host.com/api/{event} 配置事件处理程序 URL 模式 chat;当连接客户端时,则率先发送到以下 URL:http://host.com/api/connect。 此行为可能适用于 PubSub WebSocket 客户端发送自定义事件的情况,其中事件处理程序可帮助将不同的事件调度到不同的上游。 URL 域名中不允许使用 {event} 参数。

通过 Azure 门户或 CLI 设置事件处理程序上游时,服务遵循 CloudEvents 滥用保护措施来验证上游 Webhook。 WebHook-Request-Origin 请求头设置为服务域名 xxx.webpubsub.azure.cn,并且其要求含标头 WebHook-Allowed-Origin 的响应包含此域名。

执行验证时,{event} 参数将解析为 validate。 例如,当尝试将 URL 设置为 http://host.com/api/{event} 时,服务会尝试向 http://host.com/api/validate 请求执行“OPTIONS”操作,并且仅当响应有效时才可成功设置配置。

目前,我们不支持 WebHook-Request-RateWebHook-Request-Callback

服务和 Webhook 之间的身份验证/授权

  • 匿名模式
  • 通过配置的 Webhook URL 提供 code 的简单身份验证。
  • 使用 Microsoft Entra 授权。 有关详细信息,请参阅如何使用托管标识
    • 步骤 1:为 Web PubSub 服务启用标识
    • 步骤 2:从现有 Microsoft Entra 应用程序中选择代表你的 Webhook Web 应用的应用程序

“ODBC 源编辑器”

服务器本质上就是获授权用户。 借助事件处理程序角色,服务器可知晓客户端的元数据(例如 connectionIduserId),以便其:

  • 关闭客户端连接
  • 向客户端发送消息
  • 将消息发送到属于同一用户的客户端
  • 将客户端添加到组
  • 将身份验证为同一用户的客户端添加到组
  • 从组中删除客户端
  • 从组中删除身份验证为同一用户的客户端
  • 将消息发布到组

其还可以授予或撤消对 PubSub 客户端的发布/订阅权限:

  • 向某些特定组或所有组授予发布/加入权限
  • 撤销某些特定组或所有组的发布/加入权限
  • 检查客户端是否有权加入或发布到某个特定组或所有组

服务为服务器提供 REST API 以便管理连接。

显示 Web PubSub service 连接管理器工作流的关系图。

此处定义了详细的 REST API 协议。

事件侦听器

注意

事件侦听器功能目前是作为预览版提供的。

事件侦听器侦听传入的客户端事件。 每个事件侦听器包含一个用于指定它所关注的事件类型的筛选器,以及一个有关要将事件发送到何处的终结点。

目前,我们支持将事件中心用作事件侦听器终结点。

需要提前注册事件侦听器,以便在触发客户端事件时,服务可以将该事件推送到相应的事件侦听器。 请参阅此文档,了解如何使用事件中心终结点配置事件侦听器。

可以配置多个事件侦听器。 配置它们的顺序不会影响其功能。 如果一个事件与多个侦听器匹配,则会将该事件调度到所有匹配的侦听器。 请参阅下图中的示例。 例如,如果同时配置四个事件侦听器,则每个接收匹配项的侦听器将处理该事件。 与其中三个侦听器匹配的客户端事件将发送到三个侦听器,其余侦听器将被忽略。

事件侦听器数据流示例图示

可以针对同一事件结合使用事件处理程序和事件侦听器。 在这种情况下,事件处理程序和事件侦听器都会收到该事件。

Web PubSub 服务使用 Azure Web PubSub 的 CloudEvents AMQP 扩展将客户端事件传送到事件侦听器。

“摘要”

你可能已经注意到,事件处理程序角色处理从服务到服务器的通信,而管理员角色处理从服务器到服务的通信。 合并两个角色后,使用 HTTP 协议时,服务与服务器之间的数据流将如下所示。

显示 Web PubSub 服务双向工作流的关系图。

后续步骤

使用这些资源开始生成自己的应用程序: