04-面试讲解
# 实时协作面试讲解
# 技术点图谱
涉及到的技术点有:
- WebSocket
- HTTP
- 安全验证
- 冲突算法
- OT
- CRDT
- 本地存储
# 难点描述
模拟问题:我看到你的这个项目里面,有一个项目亮点是实现实时协作编辑功能,具体是怎么实现的呢?
问题分析
- 先介绍项目整体的架构
- 项目里面涉及到关键节点有哪些?
- 针对一些关键的需求是如何设计和处理的?
- 设置一些钩子
参考答案
我先说一下我们项目的架构吧,当时我们整个项目采用的是 BFF 架构,有前端服务器和后端服务器。
客户端和前端服务器建立 WebSocket 连接,这样可以保障用户的操作能够及时同步到其他用户的视图中。
除此之外前端服务器的职责还有:
- 身份验证:验证客户端发送的 JWT 令牌。
- WebSocket 连接管理:处理来自客户端的 WebSocket 连接。
- 转发操作:将经过验证的操作转发给后端服务器。
这一块儿工作相当于都是属于我们前端职责范围内的工作。
而后端服务器主要负责:
- 请求处理:接收和处理来自前端服务器的操作请求。
- 冲突解决:使用相应的算法来解决并发冲突,确保文档一致性。
- 数据存储:将最终的编辑结果存储到数据库中。
实际上关于对冲突的处理,早期组内有讨论过究竟前端服务器处理还是后端服务器处理,后来我们参考了飞书以及 Google Docs 的架构模式,最终还是决定交给后端来处理。(体现在做这个项目的时候,是有深度思考的,并且设计是有依据的,增加真实性)
另外像离线编辑,联网后自动同步内容的功能,则用到了本地存储的相关技术,在我们这个项目里面使用的是 IndexedDB.
不知道面试官老师需不需要我把里面的哪一个点展开讲一讲?(钩子🪝 硬控)
# 技术点叙述
# 1. WebSocket 与 HTTP
模拟问题:你刚才说客户端和前端服务器采用的是 WebSocket 连接,你介绍一下什么是 WebSocket 连接呢?和 HTTP 连接有什么区别?
问题分析
- 大致介绍一下什么是 WebSocket
- 和 HTTP 之间的区别
- 紧扣自己的项目,说一下 WebSocket 在项目中的作用
- 设置一些钩子
参考答案
WebSocket 是一种全双工通信的协议,服务器端可以主动向客户端推送数据。(简单介绍WebScoket)与传统的 HTTP 不同,WebSocket 连接一旦建立,就会保持打开状态,直到客户端或服务器主动关闭连接。在这个连接期间,客户端和服务器可以随时发送和接收数据,而不需要像 HTTP 那样每次通信都重新建立连接。(和 HTTP 的区别)
在我们的项目里面,客户端和前端服务器之间的连接采用的是 WebSocket 连接,这样可以保障用户的操作能够非常轻松的同步到其他用户的视图中。
而前端服务器和后端服务器之间的连接则是采用的 WebSocket + HTTP 的混合方案,像保存草稿这样的普通操作用 HTTP,如果是实时编辑这样的操作则使用 WebSocket.(紧扣自己的项目,说一下项目中是如何用 WebSocket)
实时协作中最重要的冲突处理是在后端解决的,前端这边主要需要处理像身份验证、WebSocket连接管理、请求转发等操作。
现在想想,冲突处理其实也可以放在前端来做,不过当时我们主要还是参考了飞书和 Google Docs 的架构,决定将冲突处理放到后端服务器来做。
# 2. 冲突算法
模拟问题:你说冲突处理也可以放到前端来做,那么如果真的放到前端来做,你打算怎么来做呢?
问题分析
重点介绍冲突解决涉及到的算法,因为无法用那种算法,无论放到前端还是后端,其实都是用现成的库。
- 冲突算法有哪些?各自的特点?
- 结合项目讲一下你们选择了哪一种算法?
- 放到前端来做的话,你会怎么做?
- 放到前端和放到后端,处理上面会带来哪些变化
参考答案
首先肯定是要理解冲突处理的原理。冲突处理常见的两种算法就是 OT 算法和 CRDT 算法。
- OT:通过转换并发操作,使它们能够顺序执行,从而解决冲突并保持文档一致性
- CRDT:设计特定的数据结构,使得所有操作在不同副本上独立执行时都能自动合并,确保最终状态一致,无需转换。
(简单介绍冲突算法有哪些,各自的特点)
当时这个项目后端语言用的 Java,他们找的是 Java 生态里面的 Collab 这个库,这个库是一个用于实时协作编辑的 Java 库,使用的就是 OT 算法来解决并发冲突。(结合项目阐述项目里面具体是如何做的)
而我下来查了一下,JS 生态中其实也有实现了 OT 算法的库,例如 ShareDB、ot.js. 这也是为什么我说放在前端来处理也可以。
相比放在后端服务器,如果交由前端服务器来处理的话,能够提供更好的实时性,并且减少后端的负载。(如果放到前端,相比放到后端会有什么样的变化)不过当时项目 leader 还是决定稳妥起见,采用像飞书以及 Google Docs 这些知名项目的设计模式,将冲突处理放到后端来处理。
前端这一块儿把身份验证、离线存储、请求转发、日志记录这些功能做好就行了。(钩子🪝)
# 3. 安全验证与 JWT
模拟问题:那么在身份验证这一块儿,你是怎么做的呢?
问题分析
- 罗列常见的身份验证的方式
- 结合我们自己的项目说一下选择的是哪一种方式
- 设置下一个钩子
参考答案
身份验证这一块儿的方法有很多,像 OAuth、JWT、SSO,这些都是常见的身份验证的方式。(罗列常见的方式)
我们当时采用的是 JWT. 首先用户通过用户名和密码登录,服务器验证用户登录信息,如果验证通过,前端服务器会生成一个 JWT 给客户端,客户端将这个 JWT 做本地存储,存储这一块儿我选择的是存到 localstorage.(这句话是后面的一个铺垫)
之后每次客户端向服务器发送请求时,会将 localstorage 里面的 JWT 附加在请求头中,前端服务器接收到请求后,会去验证 JWT 的有效性,包括签名是否正确、令牌是否过期等。
其实早期我在做离线存储功能的时候,也是用的 localstorage,不过在后面换成了 IndexedDB. 一开始用 IndexedDB 还不是很习惯,也是通过这个项目,对 IndexedDB 的使用也更加熟悉了。(钩子🪝)
# 4. 本地存储
模拟问题:那你说一下为什么你会从一开始 localstorage 切换到 IndexedDB 呢?
问题分析
- IndexedDB 相较于 localstorage 有什么特点?
- 对项目亮点做最后的总结
参考答案
一开始使用 localstorage 是因为我对这个东西比较熟悉,一般大家都习惯于用自己熟悉的东西来做开发。
但是文档离线存储功能,要存储的是用户编辑的文档内容,这个内容就不像之前存储 JWT 那样只是一小段字符串,而是一大段内容,因此后面我就觉得用 localstorage 不太合适了。localStorage 的容量限制和它对数据结构的支持不足,使得它不太适合用于存储大量和复杂的文档内容。
之后我查了一下客户端常见的本地存储的几种方式,认为 IndexedDB 是比较合适的。IndexedDB 的容量远大于 localStorage,可以存储几百 MB 甚至更多的数据,适合存储大量数据和复杂数据结构。另外相较于 localstorage 的同步操作,IndexedDB 支持异步操作,不会阻塞主线程,性能更好,特别是在处理大量数据时。所以最终我选择了用 IndexedDB 来完成文档离线存储功能。
(阐述 IndexedDB 的优点,为什么选择了 IndexedDB)
总之这个项目里面的实时协作功能这一块儿,我作为前端负责的就是:
- 身份验证
- WebSocket 连接管理
- 转发请求
- 文档离线存储
不过后面有机会的话,我还是打算自己尝试写一个 demo,把冲突算法放在前端服务器来进行处理,也算是我自己对这一块儿的一个尝试和探索。不知道关于这一块儿,面试官老师您还有什么更好的建议不?
(完美收官)
-EOF-