Azure Web PubSub 的 WebSocket 客户端协议

客户端通过标准 WebSocket 协议连接到 Azure Web PubSub。

服务终结点

Web PubSub 服务提供两种类型的终结点供客户端与之连接:

  • /client/hubs/{hub}
  • /client/?hub={hub}

{hub} 是必需参数,充当各种应用程序的隔离项。 可以在路径或查询中设置它。

授权

客户端使用 JSON Web 令牌 (JWT) 连接到服务。 可以将令牌以 /client/?hub={hub}&access_token={token} 形式置于查询字符串中,或以 Authorization: Bearer {token} 形式置于 Authorization 标头中。

下面是常规授权工作流:

  1. 客户端与应用程序服务器协商。 应用程序服务器包含的授权中间件可处理客户端请求,并对 JWT 进行签名,以使客户端连接到服务。
  2. 应用程序服务器将 JWT 和服务 URL 返回到客户端。
  3. 客户端尝试使用从应用程序服务器返回的 URL 和 JWT 令牌连接到 Web PubSub 服务。

支持的声明

还可以通过在 JWT 令牌中指定特殊声明,在生成访问令牌时为客户端连接配置属性:

说明 声明类型 声明值 说明
用于客户端连接的 userId sub userId 只允许一个 sub 声明。
令牌的生存期 exp 过期时间 exp(过期时间)声明指定只能在哪个时间(含)之前接受令牌的处理。
客户端连接最初具有的权限 role 权限中定义的角色值 如果客户端具有多个权限,请指定多个 role 声明。
客户端连接连接到 Azure Web PubSub 后加入的初始组 group 要加入的组 如果客户端加入多个组,请指定多个 group 声明。

还可以将自定义声明添加到访问令牌中,这些值将保留为连接上游请求正文中的 claims 属性。

服务器 SDK 提供用于为客户端生成访问令牌的 API。

简单的 WebSocket 客户端

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

例如,在 JavaScript 中,可以使用以下代码创建简单的 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')

PubSub WebSocket 客户端

PubSub WebSocket 客户端是使用由 Azure Web PubSub 服务定义的子协议的 WebSocket 客户端:

  • json.webpubsub.azure.v1
  • protobuf.webpubsub.azure.v1

通过使用服务支持的子协议,PubSub WebSocket 客户端在有相关权限时可以直接将消息发布到组。

json.webpubsub.azure.v1 子协议

查看此处详细了解 JSON 子协议

创建 PubSub WebSocket 客户端

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

直接从客户端加入组

let ackId = 0;
pubsubClient.send(    
    JSON.stringify({
        type: 'joinGroup',
        group: 'group1',
        ackId: ++ackId
    }));

直接从客户端向组发送消息

let ackId = 0;
pubsubClient.send(    
    JSON.stringify({
        type: 'sendToGroup',
        group: 'group1',
        ackId: ++ackId,
        dataType: "json",
        data: {
            "hello": "world"
        }
    }));

protobuf.webpubsub.azure.v1 子协议

协议缓冲区 (protobuf) 是一种语言中性、平台中性的基于二进制的协议,可简化二进制数据的发送。 Protobuf 提供的工具可为 Java、Python、Objective-C、C# 和 C++ 等多种语言生成客户端。

例如,在 JavaScript 中,可以通过以下代码创建使用 protobuf 子协议的 PubSub WebSocket 客户端:

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

查看此处详细了解 protobuf 子协议

AckId 和 Ack 响应

PubSub WebSocket 客户端支持 joinGroupleaveGroupsendToGroupevent 消息的 ackId 属性。 使用 ackId 时,你可以在你的请求被处理时收到确认响应消息。 可以在即发即弃场景中选择省略 ackId。 本文介绍了指定或不指定 ackId 时的行为差异。

未指定 ackId 时的行为

如果未指定 ackId,则是即发即弃。 即使中转消息时出现错误,你也无法收到通知。

指定 ackId 时的行为

幂等发布

ackId 是 uint64 数字,在具有相同连接 ID 的客户端中应是唯一的。 Web PubSub 服务会记录 ackId,具有相同 ackId 的消息将被视为同一消息。 服务拒绝多次中转同一消息,这有助于重试时避免重复的消息。 例如,如果客户端发送 ackId=5 的消息并且无法接收 ackId=5 的确认响应,那么客户端将再次重试并发送相同的消息。 在某些情况下,消息已中转,并且确认响应由于某种原因已丢失。 服务会拒绝重试并用 Duplicate 原因响应确认响应。

确认响应

Web PubSub 服务将为具有 ackId 的每个请求发送确认响应。

格式:

{
    "type": "ack",
    "ackId": 1, // The ack id for the request to ack
    "success": false, // true or false
    "error": {
        "name": "Forbidden|InternalServerError|Duplicate",
        "message": "<error_detail>"
    }
}
  • ackId 与请求相关联。

  • success 是一个布尔值,指示服务是否成功处理了请求。 如果为 false,客户端需要检查 error

  • error 仅在 successfalse 时存在,并且客户端针对不同的 name 应具有不同的逻辑。 你应假设将来可能有更多类型的 name

    • Forbidden:客户端没有请求权限。 需要向客户端添加相关角色。
    • InternalServerError:服务中出现了一些内部错误。 需要重试。
    • Duplicate:服务已处理 ackId 相同的消息。

权限

你可能已在前面的 PubSub WebSocket 客户端介绍中注意到,一个客户端只有在获得授权时才能发布到其他客户端。 当客户端建立连接或在连接的生存期内时,可以对其授予权限。

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

可以通过多种方式授予客户端的权限:

1. 生成访问令牌时将角色分配给客户端

客户端可以使用 JWT 令牌连接到服务。 令牌有效负载可以携带客户端 role 等信息。 将 JWT 令牌签名到客户端时,可以通过授予客户端特定角色向客户端授予权限。

例如,我们对有权将消息发送到 group1group2 的 JWT 令牌进行签名:

let token = await serviceClient.getClientAccessToken({
    roles: [ "webpubsub.sendToGroup.group1", "webpubsub.sendToGroup.group2" ]
});

2. 使用 connect 事件处理程序将角色分配给客户端

还可以在注册 connect 事件处理程序时设置客户端的角色,并且上游事件处理程序可以在处理 connect 事件时将客户端的 roles 返回给 Web PubSub 服务。

例如,在 JavaScript 中,可以配置 handleConnect 事件来这样做:

let handler = new WebPubSubEventHandler("hub1", {
  handleConnect: (req, res) => {
    // auth the connection and set the userId of the connection
    res.success({
      roles: [ "webpubsub.sendToGroup.group1", "webpubsub.sendToGroup.group2" ]
    });
  },
});

3. 在运行时期间通过 REST API 或服务器 SDK 将角色分配给客户端

let service = new WebPubSubServiceClient("<your_connection_string>", "test-hub");
await service.grantPermission("<connection_id>", "joinLeaveGroup", { targetName: "group1" });

后续步骤

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