频道属性
Storage
服务为你提供了频道属性的能力,可以用来存储和分发你的应用中的上下文数据,例如:道具、公告、成员列表和关系链等数据。频道属性在被设置、更新和删除的时候会触发 eventType
事件通知,频道中的其他人会在 100ms 内收到此信息。Storage
服务对于 Message Channel 和 Stream Channel 都有效,在使用的时候可以通过 channelType
参数区分。
频道属性属于永久存储数据,一旦被设置将会长期保存在 RTM 数据库,不会因为你的频道被销毁而丢失,除非你手动删除。这将影响你的存储计费项,详见计费规则。
设置频道属性
你可以为指定频道设置一组频道属性,用以实现业务上房间级别的数据存储和实时通知,例如房间内的道具信息、拍卖品价格更新、群公告等。每个频道只能有一组频道属性,但每组频道属性可以包含一个或多个子属性,相关限制详见 API 使用限制。每个子属性包含 key
、value
、revision
等预定义字段。
如果当前频道属性或者子属性不存在,该方法将为指定频道新增属性;如果频道属性或者子属性已存在,则会使用新值覆盖原有值。关于 setChannelMetadata
接口的更多信息详见 Storage API 参考。
以下示例展示了如何设置频道属性:
const properties = {
key : "Quantity" ,
value : "20"
};
const announcement = {
key : "Announcement",
value : "Welcome to our shop!"
};
const price = {
key : "T-shirt",
value : "100"
};
const data = [properties, announcement, price];
const options = { addTimeStamp : true , addUserId : true };
try {
const result = await rtm.storage.setChannelMetadata("channel1", "MESSAGE", data, options);
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}
上述示例代码中,我们为 Message Channel 类型的频道 channel1
设置了一组频道属性,其中包含三个频道子属性,分别为 properties
、announcement
和 quotation
。 同时我们设置了 options
参数,要求 RTM 服务在存储上述三个子属性的同时,在每个子属性上都额外添加上时间戳(addTimeStamp
)和修改者(addUserId
)信息。上述代码使用了 await/async
编程模式,所以需要被定义在一个异步函数中。
当上述操作成功后,RTM SDK 会返回如下数据结构:
{
timeToken : "", // 本次操作成功时间戳
channelName : "channel1", // 频道名
channelType : "MESSAGE", // 频道类型
totalCount : 3 // 频道子属性个数
}
同时, RTM 还会触发一个 UPDATE
类型的 eventType
事件通知,并在 100 ms 内通知到频道中的其他人。eventType
详见事件通知。
获取频道属性
你可以通过指定频道名和频道类型调用 getChannelMetadata
方法获得指定频道的全部属性数据。关于 getChannelMetadata
接口的更多信息详见 Storage API 参考。
以下示例代码展示了如何获得频道属性:
try {
const result = await rtm.storage.getChannelMetadata("channel1","MESSAGE");
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}
当上述操作成功后,RTM SDK 会返回如下数据结构:
{
totalCount: 3,
majorRevision: -1,
metadata:{
"Quantity":{
value:"20",
revision:-1,
updated:"2022-05-20T23:11:20.893755",
authorUid:"Tony"
},
"Announcement":{
value:"Welcome to our Shop!",
revision:-1,
updated:"2022-05-20T23:11:20.893755",
authorUid:"Tony"
},
"T-shirt":{
value:"100",
revision:-1,
updated:"2022-05-20T23:11:20.893755",
authorUid:"Tony"
}
},
channelName: "channel1",
channelType: "MESSAGE"
}
更新频道属性
你可以使用 updateChannelMetadata
来更新更新频道中已存在的子属性。如果该子属性不存在,则会返回错误。该接口可用于如下需要权限控制的业务场景:
- 在电商拍卖的场景中,只有管理员或者商品的拥有者才具备上架新商品和设置新属性的权限,而竞拍者只有修改价格属性(竞价)的权限。
- 在游戏场景中,只有管理员才能设置房间内道具的权限。
以下示例代码展示了如何更新子属性值:
const price = {
key : "T-shirt",
value : "299"
};
const data = [price];
const options = { addTimeStamp : true , addUserId : true };
try {
const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}
上述示例代码将 price
子属性中 T-shirt
对应的值更新为 299
,即把 T 恤的价格更改为 299。当调用成功后,RTM SDK 会返回如下数据结构:
{
timeToken : "", // 本次操作成功时间戳
channelName : "channel1", // 频道名
channelType : "MESSAGE", // 频道类型
totalCount : 3 // 频道子属性个数
}
同时, RTM 还会触发一个 UPDATE
类型的 eventType
事件通知,并在 100ms 内通知到频道中的其他人。eventType
详见事件通知。
删除频道属性
如果你不再需要频道的属性或子属性,你可以参考以下示例代码使用 removeChannelMetadata
进行删除操作:
const announcement = {
key : "Announcement",
value : ""
};
const data = [announcement];
try {
const result = await rtm.storage.removeChannelMetadata("channel1", "MESSAGE", data);
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}
上述示例代码中,通过把 announcement
子属性中 Announcement
对应的值设为任何数值都没有影响。
如果你在调用该接口时不填写 data
参数,则会删除该频道中整组频道属性,示例代码如下:
try {
const result = await rtm.storage.removeChannelMetadata("channel1", "MESSAGE");
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}
通常在销毁频道的过程中你会需要删除整组频道属性。频道属性数据一旦被删除将无法恢复,如果你对数据具有恢复的需求,那么你需要在删除频道属性之前做好数据备份。
同时, RTM 还会触发一个 UPDATE
类型的 eventType
事件通知,并在 100ms 内通知到频道中的其他人。eventType
详见事件通知。
CAS 控制
频道属性同时也引进了版本控制逻辑 CAS(Compare And Set),该方法提供两种独立的版本控制字段,你可以跟根据实际业务场景设置任意一种或多种:
- 通过
MetadataOptions
中的majorRevision
属性开启整组频道属性的版本号校验。 - 通过
MetadataItem[]
中的revision
属性开启每个频道子属性的版本号校验。
设置频道属性或频道子属性时,配合版本属性可以控制本次调用是否开启版本号校验,逻辑如下:
- 版本属性为
-1
时,本次调用不开启 CAS 验证。如果频道属性或频道子属性已存在,则该值会被最新值覆盖;如果频道属性或频道子属性不存在,则 SDK 会创建该值。 - 版本属性为正整数时,本次调用开启 CAS 验证。如果频道属性或频道子属性已存在,则 SDK 会在版本号验证成功后更新对应的值;如果频道属性或频道子属性不存在,则 SDK 会返回错误码。
你可以在以下场景中应用 CAS 控制:
- 在竞拍场景中,多人同时对一件商品竞拍时,最先出价者操作成功,而其余人返回错误,同时也能获得最新价格信息。
- 在抢红包场景中, 红包被第一个人抢到且只会被占有一次,而其余人返回错误。
以下示例代码展示了如何使用 majorRevision
和 revision
更新频道属性和子属性:
const properties = {
key : "Quantity" ,
value : "20",
revision : 1000
};
const announcement = {
key : "Announcement",
value : "Welcome to our shop!"
};
const price = {
key : "T-shirt",
value : "100",
revision : 1000
};
const data = [properties, announcement, price];
const options = { addTimeStamp : true , addUserId : true, majorRevision : 100 };
try {
const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}
上述例子中,我们对频道属性和子属性 properties
和 price
开启 CAS 验证并设置 majorRevision
为 100
。服务器在接收到接口调用请求后,会首先验证接口调用提供的 majorRevision
与数据库最新值是否匹配,如果不匹配,则返回错误;如果匹配,则继续验证子属性的 revision
,逻辑与验证频道属性相同。
如果你开启了版本控制能力,那么你需要关注 eventType
事件通知以获取 majorRevision
和 revision
更新后的值,以便于在接口调用时提供最新值。
Lock 的使用
Lock 为用户提供了对临界资源互斥访问的能力。常见场景例如:在频道中特定时间只能允许一位管理员存在,且只有管理员才能对频道属性进行设置、删除和修改等操作。
相比于 CAS 对于频道属性数据版本的控制能力,Lock 为你提供了更为上层的控制能力,它直接决定了你是否具备调用 setChannelMetadata
、updateChannelMetadata
、removeChannelMetadata
三个接口的权限。如果你不是 Lock 的拥有者,则无法通过调用上述三个接口对频道属性进行设置和修改。
以下代码展示了如何使用 Lock 来更新频道属性:
const properties = {
key : "Quantity" ,
value : "20"
};
const announcement = {
key : "Announcement",
value : "Welcome to our Shop!"
};
const price = {
key : "T-shirt",
value : "100"
};
const data = [properties, announcement, price];
const options = { addTimeStamp : ture , addUserId : ture, lockName : "manage" };
try {
const result = await rtm.storage.updateChannelMetadata("channel1", "MESSAGE", data, options);
console.log(JSON.stringify(result));
} catch (status) {
console.log(JSON.stringify(status));
}
上述示例代码的前提条件之一是调用 updateChannelMetadata
的用户必须是锁 manage
的拥有者,否则会返回错误。