基于 Thrift 的 Node.js 微服务

标签: node.js zookeeper thrift 微服务

当项目越来越大的时候,必然会做到服务化,而且大型项目下,极有可能调用跨语言服务。所以我写了这个通用的框架,用于发布/调用node服务, node-thrift-service

Thrift

Apache Thrift 是一款跨语言的服务框架,传输数据采用二进制格式,相对 XML 和 JSON 体积更小,对于高并发、大数据量和多语言的环境更有优势。

《基于 Thrift 的 Node.js 微服务》 client<->server

安装

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 的『节点』均可以存放数据,并且可以对节点的权限进行控制。

《基于 Thrift 的 Node.js 微服务》 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 的 Node.js 微服务》 zookeeper结构

Thrift Service 发布

Thrift 的服务由 thrift 文件定义而成,service 创建之后,将 thrift.createServer 的 host:port 发布到 ZooKeeper/Redis 上。

  • 自动获取机器内网 ipv4 地址,eth0(linux) en0(osx)
  • 自动查找一个可用的 port
  • 发布 Thrift 服务 或 JS 服务
  1. 对于 Thrift gen-js 的 service 单独发布
  2. 因为 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 。
  • 清理失效的连接。
  • 调用服务:
  1. .call(alias, action, params[, callback]) 返回 js service 调用的结果。
  2. .call(alias[, callback]) 返回 Thrift client 供 gen-js 的调用。
    原文作者:xun
    原文地址: https://www.jianshu.com/p/182413ee77fb
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞