Nodejs Limbo: 简单访问远程数据库
Nodejs Limbo: 简单访问远程数据库
简单高效的项目协作工具 Teambition 自诞生以来,一直致力于用技术重塑人们的工作方式,希望为人们的工作方式带来最好的协作体验,除了 Teambition 之外,我们还有日程管理工具 「今天」,这一次,我们又有新的产品线 「简聊」 要上线了,正如它的slogan「简聊一下,轻松协作」所表达的,它是一款slack风格的,基于企业的简单聊天工具。虽说简单,但却是为效率和协作而生的企业IM工具,它继承了 Teambition 企业级基因,打通了项目协作工具 Teambition ,用户可以直接通过 Teambition 账号登录。它以话题为中心的群组讨论聊天模式,用slack的方式促进交流,将企业内沟通、任务、人员、文件等各种要素和流程打通。有利于基于目标向,提升专注度,完成任务,过滤出团队聊天中最有效的信息。如果有些话题讨论需要其他外部人员的参与,只需要发送邮件即可邀请对方加入话题讨论。
欢迎大家试用我们的 「简聊」,第一个版本上线难免很有多不足,请大家踊跃提需求和意见,帮助我们前进。
#Limbo: 简单访问远程数据库
对于 nodejs 生态来说,使用 mongoose 作为 Model 模块是再好不过的一件事,其一大特点就是简洁优雅的 Schema 定义,提供了每个键值的类型验证,数据验证,索引声明,虚拟键,并自带实例化方法的扩展,大大节省了开发的成本。但是在考虑开放数据的时候,一切就显得不那么美好了。
在打造 「简聊」 这款应用的过程中,我们就实实在在的遇到了这样的问题。由于需要使用 Teambition 的用户和团队数据,并且当 「简聊」 更新了用户数据之后,在 Teambition 中能实时的将这些更新推送到用户那里。按照惯例,我们最初使用的是 restful 接口。
第一阶段,使用 restful 接口
restful 接口的应用面最广,但是仍然存在很多不足,比如接口在参数和结构上限制较多,在考虑修改接口 api 的时候,往往会顾虑客户端的兼容性,而一旦客户端程序有新的需求,则需等待接口的更新。另一个麻烦的地方是需要做签名校验,对于内部的应用来说,我们完全可以通过防火墙来控制特定 ip 对端口的访问,签名在此处就显得有点多余。
第二阶段,单独拆封 Schema
然后我们想到了将 Schema 拆封成一个单独的仓库,nodejs 有良好的模块管理,在不同的应用中,我们只需要将这些模块引入进来,既做到同步更新,又做到 DRY。相对于 restful 接口的缺点就是,对于数据的调用入口过多,而且应用之间互相是不知情的。例如在 「简聊」 中有更新用户数据,在 Teambition 中就无法得知,并推送给其他客户端。
第三阶段,远程过程调用(rpc)
这个阶段和 restful 接口其实类似,我们在 Teambition 的后端进程中将一些接口方法暴露出来,这样我们的客户端程序就能通过简单的 rpc 方式调用这些接口。例如我们导出了 user.update
方法,在客户端代码中使用 rpc.call('user.update', params, callback)
即可调用相应的过程。这样的调用行为与使用本地代码无异,可能是目前能找到的最简单直接的方式了。
第四阶段,rpc 与 mongoose 的结合
事情可以变得更简单,由于目的主要是为了操作数据库,所以我们开发了一个模块 limbo,将 mongoose model 中所有方法暴露出来,以命名空间来划分,实现了在客户端与服务端程序一致的使用体验。
例如我们在服务端程序中使用 limbo 连接 mongodb,只需要做如下声明:(以下的代码都以 coffeescript 作为示例)
limbo = require 'limbo'
定义 Schema
UserSchame = (Schema) ->
这里的 Schema 即 mongoose.Schema
new Schema
name: String
email: String
use 方法用作区分不同数据库连接的命名空间,一般参数选择数据库名就行
db = limbo.use(‘test’).connect(‘mongodb://localhost:27017/test’).load ‘User’, UserSchema
使用方式就与 mongoose 一致了
user = db.user
# user 是一个 limbo 中用于封装 model 的一个对象,你可以直接使用 user.model 来直接调用 mongoose model
user.findOne _id: 'xxxx'
user.create name: 'xxx', email: 'yyy'
下面是 limbo 中最激动人心的地方,你可以导出一个 collection 中的所有方法到 rpc server 中,只需要通过一个简单的声明
limbo.use('test').bind(7001).enableRpc()
下面我们就要提到如何在客户端程序中调用这些方法
# 在客户端也需要初始化一个 limbo 命名空间,需要与服务端一致,链接改为服务端的域名和端口号
db = limbo.use('test').connect('tcp://localhost:7001')
# 下面有两种方式来使用 rpc
# 1. 使用 call 方法
db.call 'user.findOne', _id: 'xxxx', ->
# 2. 使用方法链
db.user.findOne _id: 'xxxx', ->
# 第二种方式存在一个延迟,必须要在 limbo 与服务端程序握手成功之后才可以使用,
# 否则会抛出一个对象不存在的异常,不过在一般的应用中,
# 初始化所需的时间都会长于这个链接所需时间,所以延迟可以忽略不计了
可以看出,上面的第二种方式与服务端在本地使用 mongoose 的方式一模一样,这种黑魔法式的调用方式应该是广大码农喜闻乐见的。
limbo 另一个值得称道的功能是可以在服务端程序监听这些远程调用的事件,这得益于 nodejs 的 event 对象,limbo 本身就继承于 EventEmitter 对象,所以我们在每次远程调用后会触发一个事件给服务端程序,而在服务端只需要简单的监听这个事件即可
limbo.on 'test.user.findOne', (user) -> ...
正是这种 rpc 加事件反馈的机制,让 「简聊」 和 Teambition 可以实现简单实时的数据交换。我们将 limbo 托管在 github 上开源,是深知它还存在很多可以改进的地方,所以不免庸俗的说一句,欢迎 issue 和 pr~
最后,欢迎使用我们的新产品 「简聊」 ,一款基于话题的轻量级协作应用。
Node.js Limbo: 简单访问远程数据库
在开发基于 Node.js 的应用程序时,如 Teambition 的新产品「简聊」,常常需要与远程数据库进行交互。本文将介绍一种简单高效的方法来实现这一目标,即使用 limbo
模块。
第一阶段:使用 RESTful 接口
最初,我们尝试使用 RESTful 接口来访问远程数据库。虽然 RESTful 接口应用广泛,但存在一些局限性,比如:
- 参数和结构限制较多,修改 API 需要顾及客户端的兼容性。
- 需要复杂的签名校验,对于内部应用来说,这显得多余。
第二阶段:单独拆分 Schema
接下来,我们将 Schema 拆分成一个独立的仓库,以便在不同的应用中复用。这种方式虽然简化了代码重复的问题,但在数据同步方面仍有不足,例如在简聊中更新用户数据后,Teambition 无法实时获取并推送这些更新。
第三阶段:远程过程调用(RPC)
为了更好地解决数据同步问题,我们转向使用 RPC(远程过程调用)。在 Teambition 的后端进程中,我们将一些接口方法暴露出来,客户端程序可以通过简单的 RPC 方式调用这些接口。例如:
// 导出一个方法
const user = { update: (params, callback) => /* ... */ };
// 在客户端调用此方法
rpc.call('user.update', params, callback);
这种调用方式与本地代码使用无异,是一种较为直接的方法。
第四阶段:RPC 与 Mongoose 的结合
为了更方便地操作数据库,我们开发了一个名为 limbo
的模块,将 Mongoose Model 中的所有方法暴露出来,以命名空间来划分,从而在客户端和服务端实现一致的使用体验。
示例代码
首先,在服务端初始化 limbo
并连接 MongoDB:
limbo = require 'limbo'
# 定义 Schema
UserSchema = (Schema) ->
new Schema
name: String
email: String
# 使用 use 方法来区分不同数据库连接的命名空间
db = limbo.use('test').connect('mongodb://localhost:27017/test').load 'User', UserSchema
使用方式与 Mongoose 一致:
user = db.user
# 直接调用 mongoose model 方法
user.findOne _id: 'xxxx'
user.create name: 'xxx', email: 'yyy'
导出方法到 RPC 服务器
我们可以将集合中的所有方法导出到 RPC 服务器:
limbo.use('test').bind(7001).enableRpc()
在客户端调用这些方法
客户端同样需要初始化 limbo
并连接到 RPC 服务器:
db = limbo.use('test').connect('tcp://localhost:7001')
# 使用 call 方法
db.call 'user.findOne', _id: 'xxxx', (err, user) ->
console.log user
# 或者使用方法链
db.user.findOne _id: 'xxxx', (err, user) ->
console.log user
事件反馈机制
limbo
还支持在服务端监听远程调用事件,从而实现实时数据交换。例如:
limbo.on 'test.user.findOne', (user) ->
console.log "Received user:", user
这种 RPC 加事件反馈的机制,使得简聊和 Teambition 之间的数据交换变得简单而高效。
总之,通过使用 limbo
模块,我们不仅简化了数据库操作,还能实现跨应用的数据同步,极大地提高了开发效率和用户体验。欢迎使用我们的新产品「简聊」,体验基于话题的轻量级协作应用。
为了简单高效地访问远程数据库,可以采用RPC(远程过程调用)的方式。这种方式可以让客户端像调用本地函数一样调用远程服务的方法,从而简化了操作流程。以下是具体的实现步骤:
1. 使用 limbo
模块连接数据库
首先需要在服务端定义数据库模型并将其绑定到RPC服务:
limbo = require 'limbo'
# 定义 Schema
UserSchema = (Schema) ->
new Schema
name: String
email: String
# 连接数据库
db = limbo.use('test').connect('mongodb://localhost:27017/test').load 'User', UserSchema
# 绑定 RPC 服务
db.bind(7001).enableRpc()
2. 在客户端调用远程方法
客户端需要初始化一个 limbo
实例并连接到RPC服务器:
# 客户端初始化
clientDb = limbo.use('test').connect('tcp://localhost:7001')
# 调用远程方法
clientDb.user.findOne _id: 'xxxx', (err, user) ->
console.log user
或者使用更简洁的方法链方式:
clientDb.user.findOne _id: 'xxxx', (err, user) ->
console.log user
3. 事件监听
在服务端,可以监听远程调用事件以便进行处理或反馈:
limbo.on 'test.user.findOne', (user) ->
console.log "Remote method called, user found: ", user
总结
使用 limbo
和 RPC 的组合,可以让开发变得非常方便,客户端和服务端之间的交互就像是本地方法调用一样简单。这种方式不仅提高了开发效率,还减少了网络通信的复杂性。