你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
Azure Web PubSub 服务 是一种 Azure 托管服务,可帮助开发人员轻松构建具有实时功能和发布-订阅模式的 Web 应用程序。 任何需要在服务器和客户端或客户端之间进行实时发布-订阅消息传送的方案都可以使用 Azure Web PubSub 服务。 通常需要从服务器轮询或提交 HTTP 请求的传统实时功能也可以使用 Azure Web PubSub 服务。
当 WebSocket 连接连接时,Web PubSub 服务会将连接生命周期和消息转换为 CloudEvents 格式的事件。 此库提供一个快速中间件来处理表示 WebSocket 连接的生命周期和消息的事件,如下图所示:
关于这里使用的术语的详细信息,详见 关键概念 部分。
源代码 | 包 (NPM) | API 参考文档 | 产品文档 | 样品
入门
目前支持的环境
- Node.js的 LTS 版本
- Express 版本 4.x.x 或更高版本
先决条件
- Azure 订阅。
- 现有的 Azure Web PubSub 终结点。
1.安装 @azure/web-pubsub-express 包
npm install @azure/web-pubsub-express
2. 创建 WebPubSubEventHandler
import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat");
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
重要概念
连接
连接,也称为客户端或客户端连接,表示连接到Web PubSub服务的单个WebSocket连接。 成功连接后,Web PubSub服务会为该连接分配一个唯一的连接ID。
Hub
中心是一组客户端连接的逻辑概念。 通常你用一个枢纽来做一个目的,比如聊天中心或通知中心。 创建客户端连接时,它会连接到中心,并在其生存期内,它属于该中心。 不同的应用程序可以使用不同的中心名称共享一个 Azure Web PubSub 服务。
组
组是与中心的连接子集。 可以随时向组添加客户端连接或者从组中删除客户端连接。 例如,当某个客户端加入聊天室,或某个客户端离开聊天室,此类聊天室可以看成是一个组。 一个客户端可以加入多个组,一个组可以包含多个客户端。
User
与 Web PubSub 的连接可以属于一个用户。 用户可能具有多个连接,例如当单个用户跨多个设备或多个浏览器选项卡进行连接时。
客户端事件
事件是在客户端连接生命周期内创建的。 例如,一个简单的WebSocket客户端连接在尝试连接服务时会触发 connect 事件,成功连接服务时触发 connected 事件,发送消息给服务时触发 message 事件,断开连接时触发 disconnected 事件。
事件处理程序
事件处理程序包含处理客户端事件的逻辑。 事件处理程序需要先通过门户或 Azure CLI 注册并配置。 托管事件处理逻辑的地方通常被视为服务器端。
示例
处理connect请求并分配<userId>
import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
handleConnect: (req, res) => {
// auth the connection and set the userId of the connection
res.success({
userId: "<userId>",
});
},
allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
处理 connect 请求,如果认证失败就拒绝连接
import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
handleConnect: (req, res) => {
// auth the connection and reject the connection if auth failed
res.fail(401, "Unauthorized");
// the following method is also a valid approach
// res.failWith({ code: 401, detail: "Unauthorized" });
},
allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
处理 connected 请求
import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
onConnected: (connectedRequest) => {
// Your onConnected logic goes here
},
allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
处理 onGroupJoined 请求
import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
onGroupJoined: (groupJoinedRequest) => {
console.log(
`Connection ${groupJoinedRequest.context.connectionId} joined group ${groupJoinedRequest.group}`,
);
},
allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
处理 onGroupLeft 请求
import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
onGroupLeft: (groupLeftRequest) => {
console.log(
`Connection ${groupLeftRequest.context.connectionId} left group ${groupLeftRequest.group}`,
);
},
allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
处理 onDisconnected 请求
import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
onDisconnected: (disconnectedRequest) => {
// Your onDisconnected logic goes here
},
allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
处理 connect mqtt 请求,并 <userId> 分配 和 <mqtt> 属性
import { WebPubSubEventHandler, MqttConnectRequest } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
handleConnect: (req, res) => {
if (req.context.clientProtocol === "mqtt") {
// return mqtt response when request is of MQTT kind
// get connect request as mqtt request and print it
const mqttRequest = req as MqttConnectRequest;
console.log(mqttRequest);
// auth the connection and return mqtt response
res.success({
userId: "user1",
mqtt: { userProperties: [{ name: "a", value: "b" }] },
});
} else {
res.success({
userId: "user1",
});
}
},
allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
处理 connect mqtt请求,如果认证失败则拒绝连接
import { WebPubSubEventHandler, MqttConnectRequest } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
handleConnect: (req, res) => {
// auth the connection and reject the connection if auth failed
if (req.context.clientProtocol === "mqtt") {
// return mqtt error response when request is of MQTT kind
// get connect request as mqtt request and print it
const mqttRequest = req as MqttConnectRequest;
console.log(mqttRequest);
// auth the connection and return mqtt failure response
res.fail(401, "Not Authorized");
// Or use below method for more fine-grained control over the MQTT return code
// res.failWith({ mqtt: { code: MqttV500ConnectReasonCode.NotAuthorized } });
} else res.success();
},
allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
处理 onDisconnected for mqtt 请求
import { WebPubSubEventHandler, MqttDisconnectedRequest } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
onDisconnected: (disconnectedRequest) => {
if (disconnectedRequest.context.clientProtocol === "mqtt") {
// get disconnect request as mqtt request and print it
const mqttRequest = disconnectedRequest as MqttDisconnectedRequest;
console.log(mqttRequest.mqtt);
// Your onDisconnected logic goes here
} else {
console.log(disconnectedRequest);
// Your onDisconnected logic goes here
}
},
allowedEndpoints: ["https://<yourAllowedService>.webpubsub.azure.com"],
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
仅允许指定的终结点
import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
allowedEndpoints: [
"https://<yourAllowedService1>.webpubsub.azure.com",
"https://<yourAllowedService2>.webpubsub.azure.com",
],
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
设置自定义事件处理程序路径
import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
path: "/customPath1",
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
// Azure WebPubSub Upstream ready at http://localhost:3000/customPath1
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
设置和读取连接状态
import { WebPubSubEventHandler } from "@azure/web-pubsub-express";
import express from "express";
const handler = new WebPubSubEventHandler("chat", {
handleConnect(req, res) {
// You can set the state for the connection, it lasts throughout the lifetime of the connection
res.setState("calledTime", 1);
res.success();
},
handleUserEvent(req, res) {
const calledTime = req.context.states.calledTime++;
console.log(calledTime);
// You can also set the state here
res.setState("calledTime", calledTime);
res.success();
},
});
const app = express();
app.use(handler.getMiddleware());
app.listen(3000, () =>
console.log(`Azure WebPubSub Upstream ready at http://localhost:3000${handler.path}`),
);
Troubleshooting
启用日志
启用日志记录可能有助于发现有关故障的有用信息。 若要查看 HTTP 请求和响应的日志,请将 AZURE_LOG_LEVEL 环境变量设置为 info。
export AZURE_LOG_LEVEL=verbose
或者,也可以通过在setLogLevel中调用@azure/logger来启用日志:
import { setLogLevel } from "@azure/logger";
setLogLevel("info");
有关如何启用日志的更详细说明,可以查看 @azure/记录器包文档。
实时跟踪
使用 Web PubSub 服务门户中的 实时跟踪 查看实时流量。
后续步骤
请查看 samples 目录,了解如何使用该库的详细示例。
Contributing
若要参与此库,请阅读 贡献指南 了解有关如何生成和测试代码的详细信息。