NodeJS与Django协同应用开发(2)—— 业务框架


系列目录

  • NodeJS与Django协同应用开发(0) node.js基础知识
  • NodeJS与Django协同应用开发(1)原型搭建
  • NodeJS与Django协同应用开发(2)业务框架
  • NodeJS与Django协同应用开发(3)测试与优化
  • NodeJS与Django协同应用开发(4)部署

这篇文章主要会讲业务的框架设计,不过可讲内容不会那么多,毕竟不能将公司业务细节公布出来,权当抛砖引玉。

目前来说我们的业务只有一个实时性的要求,简单来说就是多人共同参与一场活动,每个人都能看到其他人的操作,此外还有一部分用户授权系统模拟其自身操作的业务。我们分别说一下开发过程中的几个问题。

首先是系统的入口。
为了灵活起见,前端在连接socket.io时所使用的地址是django后台指定的。这么做有两个好处:

  1. 分散业务
  2. 分散压力
    不过我们的项目目前还体现不出这么做的好处,因为体量比较小,不过体量变大后,一台服务器难以承担压力时,这么做就比较有必要了。我们可以把不同的业务划分在不同集群上,然后按id分配给不同的服务器。

这么做的代码量是会比较大,所以也不必一步做到位,目前对于业务的扩展我们采取的策略还是先扩展模块,并在node.js端做连接类型识别,然后分发给不同的模块。至于我们的服务器,目前还没涉及到集群。
不过这么做麻烦的地方是在部署的时候有些重复的工作,这个在后面的文章再讨论。

其次是用户身份验证。
在连接socket.io时,会由django后台生成identification作为合法用户认证用来连接node.js服务器。这里可以做的工作也比较多。

第一是登录用户和非登录用户的区别。
session管理是由django完成的,因此对于node.js来说一切的连接请求都不可信。所以这里安全的做法是将明文identification(比如用户名或者id)加盐后加密,node.js端再解密进行对比。不过并不确定解密是否会给node.js造成计算压力,node.js对于CPU密集型任务并不是很擅长,如果真的瓶颈在此那可以考虑用cluster.fork()专门开进程处理解密任务。

第二是同一登录用户多终端连接的问题。
非登录用户本身不可识别就不必考虑了,登录用户如果多开终端,那如何针对性推送是需要考虑的事情。如果按socket为单位处理用户,那当同一个用户的socketA触发了一个事件并有一些针对性的回调时,还需要同时调用socketB和socketC的回调。这在设计时非常容易被忽略,并造成一定的困扰。
在这种情况下一个可行的办法是同时只允许一个终端连接,如果有新终端连接那就把旧终端断开。不过这也得按照需求场景来制定策略,并不是所有的业务都可以这么做的。
当然更好的办法是按用户id为单位处理,同一个id下可以有多个socket,任何终端上的操作只要是同一id就视作同一来源。这样在逻辑上更加清晰,只是需要额外的数据结构来保存用户信息,并且要注意及时回收资源以免浪费内存。

第三是用户授权模拟自身操作时的验证。
这相当于用户扔了一个异步任务给后台,后台在某个时间或者某个事件之后会触发该任务,视同用户本身进行了某些操作。原本的方案是复用django api,但是这带来的问题是我们需要记录用户的sessionid和csrftoken,才能够在之后的时刻模拟这个用户。但是一旦用户在随后的时间内登出了或者在终端上切换了账户,sessionid就会失效,整个授权行为就变得无意义了。现在想想最初的方案真的是非常愚蠢,sessionid在任何时候都不该被记录下来并当作凭证,因为它可能在任何时候失效。
后来的做法是将相关的函数提取出纯业务逻辑,以用户id为凭证,再用RPC复用。由于RPC是部署在内网的,就不必担心安全问题,只用确保在授权时是用户本人即可。

再次是API设计
这里的API是指暴露给用户的操作。前文我们也说过在我们设计初衷里node.js服务器不承担或只承担小部分的业务逻辑,所以用户的操作都是不经过node.js而是直接提交给django端,再由django向node.js推送消息并最终将结果推送给用户。并且上文也说到了,对于node.js来说所有连接都是不可信的,这么做就更有必要了。
在我们的需求里,用户提交操作后要求其他用户立刻看到,但是部分操作又包含了大量的信息验证,不仅仅只是登录验证。这些验证与数据库里的数据交互非常多,所以这也是不能放在node.js端的原因之一。
因此我们客户端的代码里除了向node.js提交身份信息以外,其余任务只有接受推送而已。但是这不代表node.js不能够提供api或者socket.io不能够提供event,一些弱安全性的,比如聊天就不需要走django,这反而会给django带来不必要的压力。
也就是说,让node.js服务器变得单纯一些,甚至可以把node.js当作一个可替换组件,不要让它涉及核心业务。

最后是可持续化设计
有没有考虑过一旦node.js崩溃了,正在参与活动的用户怎么办?不像django,node.js可能保存了一定量的用户状态(这种情况可能是业务所需),一旦系统崩溃了这些状态都丢了。这里的解决办法很多,网络上类似的话题也不少,放开了说是一个很大的范畴,就说说我们是怎么办的吧。
我们的解决方案是,不在node.js端储存任何用户状态,一切需要储存的内容都经由django存入数据库。想要允许node.js使用数据库是可以的,但是要保持和django ORM一致却有一定的工作量,而且也不建议这么做。
在我们的业务里用户的状态是由用户操作和一些定时任务修改的,加上我们的api是走的django,所以在用户状态这一环节上node.js完全没有参与。还是那句话,node.js服务器不承担或只承担小部分业务逻辑,多数情况下只把它当作一个单纯的推送服务器。

总的来说开发过程里值得记录的就是这么些问题,然后就是对node.js的测试和优化了,这个我们后文再聊。

你可能感兴趣的:(NodeJS与Django协同应用开发(2)—— 业务框架)