以 Flux 角度从头考虑后端架构

4

笔记已经过时, 后面我开始了项目叫 Cumulo
http://cumulo.org/

这篇笔记写的是我对于后端的架构的思考
最近对 React Flux 架构对数据层进行思考, 感觉遇到了很多的问题
这些问题让我觉得服务端在这方面没有做好, 因此很怀疑后端架构
而且前端多种框架之间差别非常大, 而后端的似乎没有翻天覆地变化嘛

后端的结构

我做的是前端, 两年前入门时看了不少轻型的框架, 主要是动态语言的
大致进行的工作是这样一些:

  • 监听请求, 对路由进行判断

  • 解读请求, 进行数据库操作

  • 渲染页面或者 API, 返回给客户端

对于实时性有要求的页面, 比较让我关注的, 还要做另外一下操作:

  • 往相关客户端推送数据更新

这个流程中间还有不少我不清楚的, 估计为了程序方便是要做:

  • 对数据库进行抽象, 比如 Node.js 中 Mongoose 将 MongoDB 封装成 Model Collection

  • 使用缓存比如 Redis, 减少数据库访问, 减少页面绘制, 提高性能

其他恐怕还有很多, 我也不懂, 但这边的讨论总之限制在页面渲染相关的

React Flux 的改变

React 的 Flux 方案对于前端比如 Backbone 来说, 有着非常大的变化:

  1. 琐碎的 DOM 操作被限制, 尽量不让开发者手动进行控制

  2. 数据结构变简单, 同时特别引入不可修改的数据, 减少出错可能性

  3. 操作数据的方式从直接调用改成了事件触发, 避免外部操作数据

  4. 每次更新并不意味着对 View 进行一次更新, 而是设定时间段批量进行操作

  5. 尝试用不可变数据, 方便查找数据的变化部分, 再提交给 View

  6. 弃用事件触发的方式, 转而使用 state 改变来描述界面变更

当然不能说每一点都非常有效, 但是 Flux 确实能让前端开发简单很多
对于交互多的实时的应用, 在 DOM 层面带来的效率提升尤其显著
其次, 应用的内部逻辑相对过程式的写法更清晰, 也更便于重用

在后端进行类比

上边几点很多是我特意提出来的, 每一点我尝试在后端找一个 Node.js 上的对应:

  1. DOM 操作, 对于服务端来说, 就像是渲染 HTML 的部分
    考虑模版分离到了前端, 那么仅仅对应界面变更部分的数据
    通常这样的数据是服务端组装好格式发送到客户端的,, 类似手工写 jQuery 语句
    对应地, 这个步骤有没有可能交给框架去做呢?

  2. 数据结构上的封装, Mongoose 的 Model 就像是 Backbone 的 Model
    数据库部分其实有些像第一点的 DOM, 因为都是异步, 选择器, 这些琐碎的操作
    那么数据库是否能采用更简单的抽象呢?

  3. 修改数据的请求, 对于服务端来说都是 HTTP 的事件, 本身就很像

  4. 合并操作的问题, 因为 push 的网络传输本身是缓慢的, 也存在问题
    同时后端的 cache 策略和 React 用 DOM Diff 避免性能开销也非常像
    那么合并将要推送的数据是否能带来一些改善呢?

  5. 数据改变部分, 当一次请求带来多个数据改变, 往客户端推送增量相对繁琐
    只是换个角度考虑一下, 直接在服务端模拟 diff 的方案有没有可能?

  6. 事件部分, Express 的路由和 Backbone 的路由挺像, 监听事件调用操作
    对应在 React 当中, 逻辑部分的事件全给抽象掉了(DOM 的事件在所难免)
    而在 Node 当中 IO 几乎都抽象成了事件回调, 可想事件将是频繁出现的
    那么有没有可能将事件用状态在内部逻辑当中进行一些替换?

Flux 的方案如果用在后端

关于上边的问题目前没了解到好的答案, 不过倒可以设想下如果用 Flux 将如何?
界面上的一个操作, 到其他客户端更新界面, 整个流程:

  1. 界面操作事件被监听, 表单被通过 WebSocket 往服务器提交

  2. 服务器接收到数据, 在内部对数据进行更改

  3. 经历一个事件循环, 服务器开始计算每个客户端所需的全部数据,
    并且根据服务端保存的客户端的旧副本做 diff 查找出更新部分

  4. diff 被发送到所有关联的客户端, 客户端更新副本, 或者说是同步

  5. 数据更新触发 React 的 Virtual DOM 更新, 最终界面被更新

这样完整的一个流程变得跟简单的模版渲染页面一样清晰
而单次修改后组装数据的工作, 也不需要人工进行完成
单纯这样想是很好了, 同时也存在着明显的性能问题:

  1. 对数据库里的大量数据不可能做 diff, 最少也要根据时间排除旧数据
    或者其他方案, 总之现在没有个现成的方案这样跑下来同时还能保证性能

  2. 客户端的数据备份完全跟着服务端改变, 意味着客户端不处理数据的逻辑了
    因此服务端就需要维护所有客户端的数据部分
    一个事件循环后要为所有的客户端计算一遍数据, 并发量不小

  3. 服务端需要知道客户端完整的状态, 这已经不是 server 的概念了
    而是继续回到服务端 M 和 C, 服务端 V 这样的形态
    不同的是原先写在 url 里的状态, 现在需要在服务器保存和维护
    同时必须有 WebSocket 将两端紧密联系在一起

实时应用

前边写了那么多, 使用的范围估计也能看明白了, 这个是实时的应用
目前的方案是前端 MVC, 而前端 MVC 某种程度上是个奇葩的技术方案
因为在服务端和客户端分别有 Model 和 Controller, 就存在冗余代码
冗余代码还会导致分工不明确, 甚至修改逻辑时易于出现遗漏
此前前端 MVC 已经面临了模块前后端共用和分工的问题, 现在 M 和 V 也来了

对于实时应用呢, 我们知道 Meteor 和 Derby 已经探索很久了
比如 Meteor 能在客户端直接操作 MongoDB 触发其他的客户端更新
比如 Derby 的同步引擎 racer 能同步本地的数据操作到服务器
相对 React 来说, 区别在于这个逻辑是写在浏览器端的, 而不是服务端
我没有深入研究过两个框架, 具体架构如何只能等别人写文章讨论了

总之这里有一个问题, 就意味着有可能被人解决, 意味着 Web 开发可能更简单
React 处理单页面已经不错了, 但是多人参与的应用有个服务端是必不可少的
Flux 没怎么讲关于服务端的逻辑.. 不过, 他们作为 MVC 应该有一些通用的思想
最后就期待问题能早点有个可行的方案填上吧


如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

Fakefish · 2014年11月18日

想到一个事情,之前重构,一开始写的比较随意,按照jq的思路写,后来觉得不好,一旦需求越加越多代码变的很乱,后来把一个组件按照状态去写,然后再用事件去触发开启相对应的状态,感觉舒服很多。

回复

题叶 作者 · 2014年11月18日

React 认为事件还是有问题的.. 需要用状态替换事件作为模块之间拼接的手段

回复

knightli · 2015年06月10日

题叶的想法很超前啊,这个方向 facebook 已经有 Relay 和 GraphQL 配套了,不过现在公布的信息还不多。不管怎么说,这个方向未来需要一些协议上的支持(类似meteor 的 DDP),否则这些方案就算出来了,和现有后端的集成五花八门,集成成本和替换方案的成本都非常大。

回复

题叶 作者 · 2015年06月10日

后来搜集的的资料, 分布式计算领域, 两套算法 OT, CRDT 都是能用来处理同步的场景, 不过似乎都有不少的限制. 而 Diff/Patch 完全是简单粗暴适用场景很局限的一个简单方案. 也需要时间来等待方案成熟啊.

回复

载入中...