设计基于 Firebase 的可扩展提要模型
Designing a Firebase based scalable feed model
问题:
如何设计一个社交网络 "feed",以 Firebase 作为后端,可扩展?
可能的答案:
"MVP" 解决方案是设计一个 feeds
根 child,每个用户一个,并在每个关注者的提要中附加来自被关注用户的任何新 post。
users
user1
name: bob
user2
name: alice
follows:
user1: true
posts
post1
author: user1
text: 'Hi there'
feeds
user2
post1: true
这很好用,并在 Firefeed
项目中进行了演示。但它不能很好地扩展:如果 Katy Perry 想要 post 某些东西,她的手机 phone 将不得不写入数百万的提要。
因此 this SO question 中报告的解决方案将此操作委托给基于服务器的进程。
我的问题是,Firebase 是一个 "no-backend" 解决方案,这是我使用它的主要原因,所以我想确保在没有服务器的情况下绝对不可能实现此功能.
如果在上述架构中删除 feeds
child 会怎样?
然后这样做:
baseRef.child('posts')
.orderBy('author')
.whereIn(baseRef.child('users/user2/follows').keys())
不幸的是,whereIn
在 Firebase API 中不存在,子查询也不存在 :(
任何其他不需要服务器的模型结构?
谢谢
Firebase 人员有点在他们的博客上回复:https://www.firebase.com/blog/2015-10-07-how-to-keep-your-data-consistent.html
post 大约 "Data fanning"(在一个原子写入操作中将项目分散到多个节点)。
该技术极大地解决了原始问题的提要模型
post 实际上包含实现它的示例代码:
创建扇出对象的函数(实际上是一个简单的对象,键是API要写入的端点)
function fanoutPost({ uid, followersSnaphot, post }) {
// Turn the hash of followers to an array of each id as the string
var followers = Object.keys(followersSnaphot.val());
var fanoutObj = {};
// write to each follower's timeline
followers.forEach((key) => fanoutObj['/timeline/' + key] = post);
return fanoutObj;
}
以及使用此函数的逻辑:
var followersRef = new Firebase('https://<YOUR-FIREBASE-APP>.firebaseio.com/followers');
var followers = {};
followersRef.on('value', (snap) => followers = snap.val());
var btnAddPost = document.getElementById('btnAddPost');
var txtPostTitle = document.getElementById('txtPostTitle');
btnAddPost.addEventListener(() => {
// make post
var post = { title: txtPostTitle.value };
// make fanout-object
var fanoutObj = fanoutPost({
uid: followersRef.getAuth().uid,
followers: followers,
post: post
});
// Send the object to the Firebase db for fan-out
rootRef.update(fanoutObj);
});
注意:这比每次在一个关注者提要中循环写入更具可扩展性。然而,对于数百万的追随者来说,这仍然是不够的。在这种情况下,信任进行多次写入的服务器操作会更安全。我认为客户端最多可以用于数百个关注者,这是社交媒体上的平均关注者数量。 (不过这需要通过测试来验证)
问题:
如何设计一个社交网络 "feed",以 Firebase 作为后端,可扩展?
可能的答案:
"MVP" 解决方案是设计一个 feeds
根 child,每个用户一个,并在每个关注者的提要中附加来自被关注用户的任何新 post。
users
user1
name: bob
user2
name: alice
follows:
user1: true
posts
post1
author: user1
text: 'Hi there'
feeds
user2
post1: true
这很好用,并在 Firefeed
项目中进行了演示。但它不能很好地扩展:如果 Katy Perry 想要 post 某些东西,她的手机 phone 将不得不写入数百万的提要。
因此 this SO question 中报告的解决方案将此操作委托给基于服务器的进程。
我的问题是,Firebase 是一个 "no-backend" 解决方案,这是我使用它的主要原因,所以我想确保在没有服务器的情况下绝对不可能实现此功能.
如果在上述架构中删除 feeds
child 会怎样?
然后这样做:
baseRef.child('posts')
.orderBy('author')
.whereIn(baseRef.child('users/user2/follows').keys())
不幸的是,whereIn
在 Firebase API 中不存在,子查询也不存在 :(
任何其他不需要服务器的模型结构?
谢谢
Firebase 人员有点在他们的博客上回复:https://www.firebase.com/blog/2015-10-07-how-to-keep-your-data-consistent.html
post 大约 "Data fanning"(在一个原子写入操作中将项目分散到多个节点)。
该技术极大地解决了原始问题的提要模型
post 实际上包含实现它的示例代码:
创建扇出对象的函数(实际上是一个简单的对象,键是API要写入的端点)
function fanoutPost({ uid, followersSnaphot, post }) { // Turn the hash of followers to an array of each id as the string var followers = Object.keys(followersSnaphot.val()); var fanoutObj = {}; // write to each follower's timeline followers.forEach((key) => fanoutObj['/timeline/' + key] = post); return fanoutObj; }
以及使用此函数的逻辑:
var followersRef = new Firebase('https://<YOUR-FIREBASE-APP>.firebaseio.com/followers'); var followers = {}; followersRef.on('value', (snap) => followers = snap.val()); var btnAddPost = document.getElementById('btnAddPost'); var txtPostTitle = document.getElementById('txtPostTitle'); btnAddPost.addEventListener(() => { // make post var post = { title: txtPostTitle.value }; // make fanout-object var fanoutObj = fanoutPost({ uid: followersRef.getAuth().uid, followers: followers, post: post }); // Send the object to the Firebase db for fan-out rootRef.update(fanoutObj); });
注意:这比每次在一个关注者提要中循环写入更具可扩展性。然而,对于数百万的追随者来说,这仍然是不够的。在这种情况下,信任进行多次写入的服务器操作会更安全。我认为客户端最多可以用于数百个关注者,这是社交媒体上的平均关注者数量。 (不过这需要通过测试来验证)