当项目越来越大的时候,必然会做到服务化,而且大型项目下,极有可能调用跨语言服务。所以我写了这个通用的框架,用于发布/调用node服务, node-thrift-service 。
Thrift
Apache Thrift 是一款跨语言的服务框架,传输数据采用二进制格式,相对 XML 和 JSON 体积更小,对于高并发、大数据量和多语言的环境更有优势。
安装
在 Thrift 官网上下载相应的版本,按步骤一步一步安装即可。
类型
数据类型
Thrift 支持 8 种数据类型:
bool: true or false
byte: signed byte
i16/i32/i64: 16/32/64位 signed integer
double: 64位
binary: byte array
string
3 种容器:
list<t1>: 排序数组,可以重复
set<t1>: 集合,每个元素唯一
map<t1, t2>: t1 唯一
命名空间
支持命名空间(Namespaces)// 如 Java 的包名,C++ 的 namespaces
namespace cpp com.hello
namespace java com.hello
引用
可以引用其他 thrift 文件
include "hello.thrift"
...
常量
const i32 GOOD = 1024;
const list<string> ABC = ["a", "b", "c"];
数据结构
struct Animal {
1: required double age;
2: required double weight;
}
struct Cat {
1: required string name;
2: required string food;
3: required Animal common;
8: optional bool curl;
}
接口
Thrift 通过语法规范定义接口,形式类似于伪代码,例如:
exception TypeError {
1: required string err
2: optional string message
}
service Hello {
string say(1: string name);
Cat getCat(1: string name) throws (1: TypeError err)
}
更详细的教程可以看这里。
生成代码
上述代码定义了一个 Hello
服务,将其保存为 hello.thrift
。
使用 thrift
命令可以生成代码。
thrift --gen <language> <Thrift filename>
// nodejs
// ls ./gen-nodejs => Hello.js hello_types.js
thrift --gen js:node hello.thrift
// java
// ls ./gen-java => Animal.java Cat.java Hello.java TypeError.java
thrift --gen java hello.thrift
使用
示例
'use strict';
const thrift = require('thrift');
const Hello = require('./gen-nodejs/Hello'),
types = require('./gen-nodejs/hello_types');
...
let server = thrift.createServer(Hello, {
say(name, callback){
callback(null, 'Hello ' + name);
}
...
}, {});
server.listen(7800);
server.on('error', console.error);
server.on('listening', () => {
let conn = thrift.createConnection('127.0.0.1', 7800);
let client = thrift.createClient(Hello, conn);
client.say('Thrift', console.log);
// null 'Hello Thrift'
});
API
// processor 即生成的 ./gen-nodejs/Hello.js
// handler 为接口的实现,参数在 hello.thrift 定义的基础上多一个 callback(error, result)
// options 可以定义 传输协议 以及 tls
// var transport = (options && options.transport) ? options.transport : TBufferedTransport;
// var protocol = (options && options.protocol) ? options.protocol : TBinaryProtocol;
let server = thrift.createServer(processor, handler, options)
// 这个就一目了然,options 与 server 创建时的 options 用法一致
let connection = thrift.createConnection(host, port, options)
let client = thrift.createClient(processor, connection)
ZooKeeper
ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,是 Google 的 Chubby 一个开源的实现,是 Hadoop 和 Hbase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
ZooKeeper 的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
ZooKeeper 包含一个简单的原语集,提供Java和C的接口。
ZooKeeper 代码版本中,提供了分布式独享锁、选举、队列的接口,其中分布锁和队列有Java和C两个版本,选举只有Java版本。
详细原理教程可以参见官网。
Node.js 的 ZooKeeper 版本为 C 接口的封装。
使用
Zookeeper 提供一个类似于文件系统的服务。
Zookeeper 每一级节点分为永久节点和临时节点,只有永久节点才可以创建子节点,类似于文件系统一级一级的『目录』。
Zookeeper 的『节点』均可以存放数据,并且可以对节点的权限进行控制。
client 与 Zookeeper 服务器为 TCP 长连接,服务器为每个客户端生成一个 session,断开时则清理 session 。
在这样的机制下,可以使用 ZooKeeper 进行监控。
如上图所示,client 1、2、3 在连接 Zookeeper 服务器可以注册一个临时节点,断开连接时 Zookeeper 就会清理临时节点,这样便能做到监控。
监视
ZooKeeper 对三类操作提供监视器注册(Watches):
getData()
getChildren()
exist()
监视器均为一次触发!如果一直要监控节点变化,则需要在监视器触发后,再次注册监视器!
监视器事件:
ZOO_CREATED_EVENT // 节点被创建(此前该节点不存在)
ZOO_DELETED_EVENT // 节点被删除
ZOO_CHANGED_EVENT // 节点发生变化
ZOO_CHILD_EVENT // 子节点事件
ZOO_SESSION_EVENT // 会话丢失
ZOO_NOTWATCHING_EVENT // 监视被移除。
None // None事件会触发,但是不会使当前监视器失效
Thrift && Zookeeper 的微服务架构
将 Thrift Server 发布 ZooKeeper 上(注册临时节点),Client 端根据 serviceName(alias) 在 ZooKeeper 上查找 Thrift Server 主机,连接所有提供该服务的 Thrift Server 主机,并自动管理所有的 TCP 连接。
ZooKeeper 结构
设置 Zookeeper 的结构如下,service 的每个节点均为临时节点。
Thrift Service 发布
Thrift 的服务由 thrift 文件定义而成,service 创建之后,将 thrift.createServer 的 host:port 发布到 ZooKeeper/Redis 上。
自动获取机器内网 ipv4 地址,eth0(linux) en0(osx)
自动查找一个可用的 port
发布 Thrift 服务 或 JS 服务
对于 Thrift gen-js 的 service 单独发布
因为 JS 语言支持
.apply
方式的调用,所以调用 remote service 的时候,可以只传输alias
action
params
,结果返回时,传输err
result
,所以可以用一个通用的msg.thrift
用于传输。并且可以管理actions
的调用权限。
Thrift Client 订阅
Client 连接ZooKeeper/Redis,根据 service alias
查找所有提供服务的 server 地址,并管理所有 server 连接。
action
调用权限控制。轮询调用 alias 的所有连接。
使用
Watcher
订阅${service}
子节点的变化,并自动添加新 server 。清理失效的连接。
调用服务:
.call(alias, action, params[, callback])
返回 js service 调用的结果。.call(alias[, callback])
返回 Thrift client 供 gen-js 的调用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。