User Metadata
Storage
服务为你提供了 User Metadata 的能力,可以用来存储和分发你的应用中关于用户的上下文数据,例如:姓名、年龄、头像连接等一切自定义数据。User Metadata 在被设置、更新和删除的时候会触发 onStorageEvent
事件通知,订阅此 User Metadata 的用户会在 100ms 内收到此信息。
需要注意的是,User Metadata 属于永久存储,一旦被设置将会长期保存在 RTM 数据库,不会因为你的用户注销而丢失,直到你手动删除它们为止。知道这一点非常重要,因为它将影响你的存储计费项,详见计费规则。
设置 User Metadata
你可以为指定的用户设置一组 User Metadata 用以实现业务上用户级别的数据存储和实时通知,例如用户的姓名、年级、头像链接、自我介绍等任何自定义数据。每个用户只能有一组 User Metadata,但每组 User Metadata 可以包含一个或多个 Metadata Item,相关限制详见 API 使用限制。每个 Metadata Item 包含有 key
、value
、revision
预定义字段。
如果当前 User Metadata 或者 Metadata Item 不存在,该方法将为指定用户新增属性;如果 User Metadata 或者 Metadata Item 存在,则会使用新值覆盖原有值。
以下示例代码展示了如何置 User Metadata:
Metadata metadata;
std::vector<agora::rtm::MetadataItem> items;
items.emplace_back(MetadataItem("Name", "Tony"));
items.emplace_back(MetadataItem("Age", "40"));
items.emplace_back(MetadataItem("Avatar", "https://your-domain/avatar/tong.png"));
metadata.items = items.data();
metadata.items = items.size();
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
rtm_client->getStorage()->setUserMetadata("Tony", metadata, options, requestId);
调用该方法后,SDK 会触发 onSetUserMetadataResult
回调并返回 API 调用结果。
// 异步回调
class RtmEventHandler : public IRtmEventHandler {
void onSetUserMetadataResult(const uint64_t requestId, const char *userId, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("SetUserMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("SetUserMetadata success\n");
}
}
};
上述示例代码中,我们为用户 Tony
设置了一组 User Metadata,其中包含三个 Metadata Item,分别为 Name
、Age
和 Avatar
。同时我们设置了 options
参数要求 RTM 服务在存储上述三个 Metadata Item 时额外添加上时间戳和修改者信息。
关于 setUserMetadata
接口详见 Storage API 参考。
同时,RTM 还会触发一个 RTM_STORAGE_EVENT_TYPE_UPDATE
类型的 onStorageEvent
事件通知,并在 100 ms 内通知到订阅了此 User Metadata 的其他人,详见事件通知。
获取 User Metadata
你可以通过调用 getUserMetadata
方法获得指定用户的全部属性数据。以下示例代码展示如何获取 User Metadata:
uint64_t requestId;
rtmClient->getStorage()->getUserMetadata("Tony", requestId);
调用该方法后,SDK 会触发 onGetUserMetadataResult
回调并返回 API 调用结果。
// 异步回调
class RtmEventHandler : public IRtmEventHandler {
void onGetUserMetadataResult(const uint64_t requestId, const char *userId, const Metadata& data, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("GetUserMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("GetUserMetadata success user id: %s\n", userId);
for (int i = 0 ; i < data.itemCount; i++) {
printf("key: %s value: %s revision: %lld\n", data.items[i].key, data.items[i].value, data.items[i].revision);
}
}
}
};
当上述操作成功后,RTM SDK 会返回如下数据结构:
{
majorRevision: 734874892,
metadata:{
"Name":{
value:"Tony",
revision:734874872,
updated:1688978391900,
authorUid:"Tony"
},
"Age":{
value:"40",
revision:734874862,
updated:1688978390900,
authorUid:"Tony"
},
"Avatar":{
value:"https://your-domain/avatar/tong.png",
revision:734874812,
updated:1688978382900,
authorUid:"Tony"
}
}
}
更新 User Metadata
你可以使用 updateUserMetadata
来更新已存在的 User Metadata。如果该 User Metadata 不存在,则会返回错误。该接口可用于需要权限控制的业务场景,例如:应用的设置者预定义了 User Metadata 的数据字段和格式,用户只具备更新的权限。
以下示例代码展示了如何更新用户 Metadata Item:
Metadata metadata;
std::vector<MetadataItem> items;
items.emplace_back(MetadataItem("Age", "45"));
metadata.items = items.data();
metadata.itemCount = items.size();
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
rtmClient->getStorage()->updateUserMetadata("Tony", metadata, options, requestId);
调用该方法后,SDK 会触发 onUpdateUserMetadataResult
回调并返回 API 调用结果。
// 异步回调
class RtmEventHandler : public IRtmEventHandler {
void onUpdateUserMetadataResult(const uint64_t requestId, const char *userId, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("UpdateUserMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("UpdateUserMetadata success\n");
}
}
};
上述示例代码将 key
为 Age
的年龄数据更新为 45
。
同时,RTM 还会触发一个 RTM_STORAGE_EVENT_TYPE_UPDATE
类型的 onStorageEvent
事件通知,并在 100ms 内通知到订阅此 User Metadata 的其他人。onStorageEvent
详见事件通知。
删除 User Metadata
当你不再需要指定用户的 User Metadata 或者某几个 Metadata Item,你可以进行删除操作。以下示例代码展示了如何使用 removeUserMetadata
进行删除操作:
Metadata metadata;
std::vector<MetadataItem> items;
MetadataItem item;
item.key = "Age";
items.emplace_back(item);
metadata.items = items.data();
metadata.itemCount = items.size();
uint64_t requestId;
rtmClient->getStorage()->removeUserMetadata("Tony", metadata, MetadataOptions(), requestId);
调用该方法后,SDK 会触发 onRemoveUserMetadataResult
回调并返回 API 调用结果。
// 异步回调
class RtmEventHandler : public IRtmEventHandler {
void onRemoveUserMetadataResult(const uint64_t requestId, const char *userId, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("RemoveUserMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("RemoveUserMetadata success\n");
}
}
};
以上示例代码中 value
不论为何值都没有影响。
Metadata metadata;
MetadataOptions options;
uint64_t requestId;
rtmClient->getStorage()->removeUserMetadata("Tony", metadata, options, requestId);
销毁用户账户的过程中,经常见到删除整组 User Metadata 的操作。User Metadata 数据一旦被删除将无法恢复,如果你对数据具有恢复的需求,那么你需要谨慎使用该方法并做好数据备份。
同时,RTM 还会触发一个 RTM_STORAGE_EVENT_TYPE_UPDATE
类型的 onStorageEvent
事件通知,并在 100ms 内通知到订阅此 User Metadata 的其他人。onStorageEvent
详见事件通知。
订阅 User Metadata
与订阅 Channel Metadata 类似,如果你需要关注某个用户的属性更新,那么你可以订阅该用户的 User Metadata,参照以下示例代码:
uint64_t requestId;
rtmClient->getStorage()->subscribeUserMetadata("Tony", requestId);
调用该方法后,SDK 会触发 onSubscribeUserMetadataResult
回调并返回 API 调用结果。
// 异步回调
class RtmEventHandler : public IRtmEventHandler {
void onSubscribeUserMetadataResult(const uint64_t requestId, const char *userId, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("SubscribeUserMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("SubscribeUserMetadata success\n");
}
}
};
以上示例中我们订阅了用户名为 Tony
的 User Metadata,当 Tony
的 User Metadata 变化时 RTM 会触发一个 RTM_STORAGE_EVENT_TYPE_UPDATE
类型的 onStorageEvent
事件通知,并在 100 ms 内通知到你。
取消订阅 User Metadata
如果你对不再关注某个用户的 User Metadata 变更,可以取消订阅,参照以下示例代码:
uint64_t requestId;
rtm_client->getStorage()->unsubscribeUserMetadata("Tony", requestId);
调用该方法后,SDK 会触发 onUnsubscribeUserMetadataResult
回调并返回 API 调用结果。
// 异步回调
class RtmEventHandler : public IRtmEventHandler {
void onUnsubscribeUserMetadataResult(const uint64_t requestId, const char* userId, RTM_ERROR_CODE errorCode) override {
if (errorCode != RTM_ERROR_OK) {
printf("UnsubscribeUserMetadata failed error is %d reason is %s\n", errorCode, getErrorReason(errorCode));
} else {
printf("UnsubscribeUserMetadata success\n");
}
}
};
操作成功后,你将不再会收到此 User Metadata 变更通知。
CAS 控制
User Metadata 与 Channel Metadata 类似,也存在 CAS 控制机制。该方法提供两种独立的版本控制字段,你可以根据实际业务场景设置任意一种或多种:
- 通过
Metadata
中的majorRevision
属性开启整组 User Metadata 的版本号校验。
- 通过
MetadataItem
中的revision
属性开启某个 Metadata Item 的版本号校验。
你可以在需要权限控制的场景中应用 CAS 控制,例如:在交友 App 中同时只允许一位观众与主播连麦,在多人同时发起请求时,只有最先操作的人会成功。
以下示例代码展示了如何使用 majorRevision
和 revision
更新 User Metadata 和 Metadata Item:
Metadata metadata;
metadata.majorRevision = 734874892;
std::vector<MetadataItem> items;
items.emplace_back(MetadataItem("Avatar", "https://your-domain/avatar/tony.png", 734874812));
metadata.items = items.data();
metadata.itemCount = items.size();
MetadataOptions options;
options.recordTs = true;
options.recordUserId = true;
uint64_t requestId;
rtmClient->getStorage()->updateUserMetadata("Tony", metadata, options, requestId);
上述例子中,我们对 User Metadata 和 Metadata Item Avatar
开启 CAS 验证并设置 majorRevision
为 734874892
。服务器在接收到接口调用请求后,会首先验证接口调用提供的 majorRevision
与数据库最新值是否匹配,如果不匹配,则返回错误;如果匹配,则继续验证 Metadata Item 的 revision
,逻辑与验证 User Metadata 相同。
如果你开启了版本控制能力,那么你需要关注 onStorageEvent
事件通知以获取 majorRevision
和 revision
更新后的值,以便于在接口调用时提供最新值。