Skip to content

Commit 6bfdf70

Browse files
committed
feat: chapter 13
1 parent 2f7154d commit 6bfdf70

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

docs/grokking/chapter-13.md

+99
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,102 @@ getUserFeed(api_dev_key, user_id, since_id, count, max_id, exclude_replies)
108108
下图展示了系统的高层架构,其中用户 B 和 C 关注了用户 A。
109109

110110
![图13-2](/grokking/f13-2.png)
111+
112+
## 7. 详细组件设计
113+
114+
我们来详细讨论系统的各个组件。
115+
116+
**a. 动态生成**
117+
118+
以新闻动态生成服务为例,假设该服务需要获取 Jane 关注的所有用户和实体的最新帖子,其查询可能如下:
119+
120+
```
121+
(SELECT FeedItemID FROM FeedItem WHERE UserID in (
122+
SELECT EntityOrFriendID FROM UserFollow WHERE UserID = <current_user_id> and type = 0(user)
123+
))
124+
UNION
125+
(SELECT FeedItemID FROM FeedItem WHERE EntityID in (
126+
SELECT EntityOrFriendID FROM UserFollow WHERE UserID = <current_user_id> and type = 1(entity)
127+
))
128+
ORDER BY CreationDate DESC
129+
LIMIT 100
130+
```
131+
132+
**该动态生成服务设计存在的问题:**
133+
134+
1. **速度极慢**:对于关注大量好友/实体的用户,需要对海量帖子进行排序、合并和排名,导致查询速度极慢。
135+
2. **高延迟**:在用户加载页面时才生成动态,响应速度慢,延迟较高。
136+
3. **实时更新的积压**:每次状态更新都会触发所有关注者的动态更新,可能导致新闻动态生成服务积压严重。
137+
4. **推送负载过高**:服务器向用户推送或通知新帖子时,尤其是对于拥有大量关注者的用户或页面,可能会产生极高的负载。
138+
139+
**改进方案:预生成动态**
140+
为提高效率,我们可以预生成用户的动态并存储在内存中。
141+
142+
**离线生成新闻动态**
143+
可以使用专门的服务器持续生成用户的新闻动态,并将其存储在内存中。这样,用户请求动态时,不需要即时计算,而是直接从预存的结果中获取。
144+
145+
当服务器需要为某个用户生成动态时,会先查询该用户上次生成动态的时间点,然后从该时间点开始计算新的动态数据。该数据可以存储在哈希表中,**键(Key)**为用户ID,**值(Value)**为如下结构体(STRUCT):
146+
147+
```
148+
Struct {
149+
LinkedHashMap<FeedItemID, FeedItem> feedItems;
150+
DateTime lastGenerated;
151+
}
152+
```
153+
154+
我们可以使用类似 **LinkedHashMap****TreeMap** 的数据结构来存储 **FeedItemID**,这样不仅可以快速跳转到任意动态项,还能方便地遍历整个映射。
155+
156+
当用户请求更多动态时,他们可以发送当前新闻动态中最后一个 **FeedItemID**,然后我们可以在哈希映射中找到该 **FeedItemID**,并从该位置返回下一批动态数据。
157+
158+
**内存中应存储多少条动态?**
159+
初始方案是为每个用户存储 **500 条动态**,但可以根据使用模式调整。例如:
160+
- 如果一页动态包含 **20 篇帖子**,且大多数用户不会浏览超过 **10 页**,那么我们可以将存储量减少至 **200 条动态**
161+
- 对于希望查看更多动态的用户,可以向后端服务器查询额外的帖子数据。
162+
163+
**是否应该为所有用户生成并缓存新闻动态?**
164+
并非所有用户都会频繁登录,因此需要优化存储策略:
165+
1. **LRU 缓存淘汰策略**:使用 **LRU(Least Recently Used)** 缓存机制,移除长时间未访问新闻动态的用户数据,以节省内存。
166+
2. **智能预测用户登录模式**:分析用户的访问习惯,预测他们活跃的时间段,并在适当时间点预生成新闻动态,例如:
167+
- 用户一天中活跃的时间
168+
- 用户每周访问新闻动态的频率
169+
170+
在接下来的部分,我们将探讨如何优化 **实时更新** 方案。
171+
172+
**b. 动态发布**
173+
174+
将帖子推送给所有关注者的过程称为 **Fanout(广播)**
175+
- **推送(Push)模式** 称为 **Fanout-on-write**(写时广播)。
176+
- **拉取(Pull)模式** 称为 **Fanout-on-load**(加载时广播)。
177+
178+
我们来探讨不同的动态发布方案。
179+
180+
**1. 拉取模式(Pull)——Fanout-on-load**
181+
该方法会将所有最新的动态数据存储在内存中,用户需要时可从服务器拉取。客户端可以定期拉取动态,或者手动刷新以获取最新内容。
182+
183+
**缺点**
184+
- **延迟问题**:新内容不会立即显示,用户必须主动请求才能看到新动态。
185+
- **资源浪费**:如果拉取请求没有新数据,服务器会返回空响应,导致资源浪费。
186+
187+
**2. 推送模式(Push)——Fanout-on-write**
188+
当用户发布新帖子时,服务器会立即将其推送给所有关注者。
189+
190+
**优点**
191+
- 取动态时无需遍历好友列表,大大减少 **读操作**,提高访问效率。
192+
193+
**缺点**
194+
- 需要维护一个 **长轮询(Long Polling)** 连接,以便服务器向客户端发送更新。
195+
- 对于拥有 **数百万关注者的明星用户**,服务器需要同时向大量用户推送数据,可能造成 **巨大负载**
196+
197+
**3. 混合模式(Hybrid)**
198+
混合方案结合了 **写时广播(Fanout-on-write)****加载时广播(Fanout-on-load)**,可以优化性能:
199+
- **针对普通用户**:继续使用 **推送(Push)模式**,确保关注者能及时收到更新。
200+
- **针对明星用户(大量关注者)****禁用推送**,让关注者自行拉取更新,以减少服务器开销。
201+
- **优化策略**
202+
- 只向 **在线好友** 推送动态,减少无效推送。
203+
- 结合 **推送通知(Push to Notify)****拉取获取(Pull to Serve)**,确保系统既高效又灵活。
204+
205+
**请求时返回多少条动态?**
206+
每次请求应设置最大返回条数(例如 **20 条**),但允许客户端根据设备类型(**移动端 vs. 桌面端**)自行调整请求数量。
207+
208+
**是否始终通知用户有新动态?**
209+
**桌面端** 可以开启通知,而 **移动端** 由于流量成本较高,可以采用 **“下拉刷新”(Pull to Refresh)** 机制,让用户主动获取新动态,减少不必要的流量消耗。

0 commit comments

Comments
 (0)