Skip to the content.

目录

Tars.js 快速入门

本文档提供Tars nodejs几个开发场景的step by step快速入门,更多细节与特性可参考Tars nodejs开发文档,以及其中各个子模块的readme文档。

1. 脚手架及自动发布工具

Tars.js提供了 脚手架和自动发布工具

安装:npm install -g @tars/nodetools-cli
初始化项目:nodetools init
打包发布:nodetools upload

2. Tars rpc server开发

和其它nodejs项目一样初始化项目

"use strict";
var Hello = require("./Hello.js").Hello;
module.exports.Hello = Hello;
Hello.HelloProxyImp.prototype.initialize = function () {
    //TODO::
};
Hello.HelloProxyImp.prototype.add = function (current, a, b, c) {
    c = a + b;
    current.sendResponse(0, c);
};

接口实现的代码中,需要开发者自己实现的只有add函数体中的两行代码。

然后实现一个服务入口文件 app.js:

const Tars   = require("@tars/rpc");
const Hello  = require("./HelloImp.js").Hello;

const APP_NAME = "Hello", SERVER_NAME = "HelloServer", OBJ_NAME = "HelloObj";
let servantName = `${APP_NAME}.${SERVER_NAME}.${OBJ_NAME}`;
let impMap = {
    [servantName] : Hello.TestImp
};
Tars.server.getServant(process.env.TARS_CONFIG || `./${APP_NAME}.${SERVER_NAME}.config.conf`).forEach(function (config){
    let svr = Tars.server.createServer(impMap[config.servant]);
    svr.start(config);
});

大功告成,此时这个服务就能在Tars平台上跑起来了。

打包、部署、发布

代码打包
tars nodejs打发布包使用的是tars-deploy模块,这个模块通过命令行运行,需要全局安装:npm install @tars/deploy -g
安装完后在服务根目录下运行 tars-deploy 服务名,即可在服务根目录下生成 服务名.tgz发布包,注意这里的服务名和入口点文件中的服务名,以及平台上部署时所用的服务名,要保持一致。

服务部署
登录Tars管理平台,访问 运维管理 -> 服务部署。

应用名 = 入口点文件中的应用名
服务名 = 入口点文件中的服务名
服务类型 = tars_nodejs
模板 = tars.default

节点 = 要部署到tarsnode节点ip
端口 = 自动生成或手动填写
协议 = TARS

提交即可

节点发布
访问 服务管理 -> 服务管理 -> 左侧菜单中选中刚刚部署的服务 -> 发布管理 -> 勾选节点 -> 发布选中节点 -> 上传发布包 -> 发布
发布成功后,可以在服务管理页面看到节点状态是active。至此,打包部署发布的流程完毕,一个tars nodejs的rpc server可以提供服务了。

使用自动发布工具 使用自动发布工具可以省掉代码打包和节点发布的操作,直接在项目目录运行 nodetools upload即可。

3. Tars http server开发

第1节中介绍了如何开发一个tars协议的服务,并部署到Tars平台上运行。而tars平台对应用的通信协议是没有任何限制的,我们也可以将一个提供http协议的nodejs服务放到tars平台上运行。 除了数据编解码、组包协议与tars协议不同外,进程管理、监控上报、日志收集等特性都可以享受到。

http服务代码实现

根据业务需求以及您的喜好选择框架(!Tars 不限制您使用的框架!),编写业务代码:

Node.js 官网 的HTTP Server例子

const http = require('http');

const hostname = process.env.IP || '127.0.0.1';
const port = process.env.PORT || 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Koa 官网 的HTTP Server例子

const Koa = require('koa');
const app = new Koa();
const hostname = process.env.IP || '127.0.0.1';
const port = process.env.PORT || 3000;
app.use(async ctx => {
  ctx.body = 'Hello World';
});

app.listen(hostname, port);

上面的示例程序,在 Tars 平台上运行均只需要修改 IP 和端口即可,IP和端口可以从环境变量中获取。

打包与部署

代码打包
同rpc server打包流程。

服务部署
登录Tars管理平台,访问 运维管理 -> 服务部署。

应用名 = 服务所属的应用名
服务名 = 服务名
服务类型 = tars_nodejs
模板 = tars.default

节点 = 要部署到tarsnode节点ip
端口 = 自动生成或手动填写
协议 = 非TARS

提交即可

这里要注意的是:

节点发布
同rpc server节点发布流程。

4. Tars client rpc调用

本节介绍如何通过Tars rpc client来调用tars rpc server。被调的rpc server不限语言,这里就以第1节中发布的HelloServer为例。

协议文件转换

tars2node --client Hello.tars
得到协议的客户端代理文件

.
`-- HelloProxy.js

rpc client调用代码实现

const Tars  = require("@tars/rpc").client;
const Hello = require("./HelloProxy.js").Hello;

const proxy = Tars.stringToProxy(Hello.TestProxy, "Hello.HelloServer.HelloObj");

proxy.add(6, 7).then((data)=>{
    console.log("接口返回值:", data.response.return)
    console.log("接口返回字段:", data.response.arguments.c)
    console.log("调用耗时", data.response.costtime)
}).catch((e)=>{
    console.log("框架错误码:", e.response.error.code)
    console.log("框架错误信息:", e.response.error.message)
    console.log("调用耗时", e.response.costtime)
})

这里实际开发时要注意proxy对象应该做成单例,不要每次调用都生成一个proxy对象。

复杂数据结构的操作

第1节中示例协议只包含了很简单的int类型数据结构,实际应用中数据结构会相当复杂多变,Tars nodejs使用 @tars/stream 模块 来处理数据的编解码。

要记住一点,Tars rpc中接口调用过程中,除了基本类型数据可以直接使用js原生类型外,struct/map/list类型的数据都必须通过编解码模块封装的类型来操作,例如add接口的入参、出参变成这样:

module Hello
{
	struct AddReq {
    0 require iNum1;
    1 require iNum2;
    2 optional map<string, string> mContext;
	};

	struct AddRsp {
		0 require iRet;
		1 require iResult;
		2 optional map<string, string> mContext;
	};

	interface Test
	{
		int add(AddReq stReq, out AddRsp stRsp);
	};
}; 

那么我们调用接口时,入参需要从js的Object转换为AddReq结构体类,最方便的方法是通过结构体类的readFromObject成员函数来转换

let stReq = new Hello.AddReq()
stReq.readFromObject({
  iNum1: 1,
  iNum2: 2
})
proxy.add(stReq)...

而当拿到结果时,得到的是AddRsp类的对象,如果要使用js原生对象,则需要将其转换一下,可以通过结构体类的toObject成员函数来转换

proxy.add(stReq).then((data)=>{
   let rsp = data.response.arguments.stRsp.toObject()
   ...
})...

不光struct数据类型,vector、map也有这一对 原生 —— tars对象转换的函数。 除了这两个函数,@tars/stream模块还提供了更多细节的编解码函数,如果有必要请参阅其readme文档。

5. 其它

tars服务模板配置

tars服务启动时需要指定一个模板配置文件,我们在平台上部署服务时所设置的所有属性,以及一些系统默认的配置项,都会被写到此配置文件,可以在tars模板配置文档 中看到各种父模板的内容。

我们可以在 服务管理 -> 服务列表 -> 编辑 中按照模板配置格式添加自己的私有模板,服务发布时私有模板也会被合并到模板配置文件中。

服务中可以通过 utils模块 —— 模板解析器 来解析模板配置的内容,当服务在tars平台上运行时,可以通过process.env.TARS_CONFIG来获取模板配置的文件路径。

tars服务业务配置

除了tars平台为每个服务生成的模板配置文件外,每个服务可能还有自己的配置文件,存放例如业务db配置、某开放平台key配置等。

Tars平台提供业务配置文件管理的功能,配置文件添加方式为 服务管理 -> 服务配置 -> 添加配置

配置文件可以是任意格式,Tars nodejs通过 tars-config模块来读取在平台中添加的业务配置文件,这里不细讲,在模块readme中可看到详细用法。

一个服务启动多个Obj

当我们在一个服务中想启动多个Obj,有的实现tars协议提供rpc调用服务,有的实现http协议,可以这样操作:

在平台上,服务管理 -> 服务列表 -> 管理Servant -> 添加Servant,填写要添加的Servant信息,例如在第1节的服务中添加一个非Tars协议的Servant。

添加完,重新发布服务后,服务的模板配置就变成了这样

<server>
  node=tars.tarsnode.ServerObj@tcp -h 172.17.0.4 -p 19386 -t 60000
  app=Hello
  server=HelloServer
  localip=172.17.0.4
  local=tcp -h 127.0.0.1 -p 3032 -t 3000
  basepath=/usr/local/app/tars/tarsnode/data/Hello.HelloServer/bin/
  datapath=/usr/local/app/tars/tarsnode/data/Hello.HelloServer/data/
  logpath=/usr/local/app/tars/app_log/
  logsize=10M
  config=tars.tarsconfig.ConfigObj
  notify=tars.tarsnotify.NotifyObj
  log=tars.tarslog.LogObj
  deactivating-timeout=3000
  logLevel=DEBUG
  <Hello.HelloServer.HelloObjAdapter>
    allow
    endpoint=tcp -h 172.17.0.4 -p 3031 -t 60000
    handlegroup=Hello.HelloServer.HelloObjAdapter
    maxconns=200000
    protocol=tars
    queuecap=10000
    queuetimeout=60000
    servant=Hello.HelloServer.HelloObj
    threads=5
  </Hello.HelloServer.HelloObjAdapter>
  <Hello.HelloServer.HelloHttpObjAdapter>
    allow
    endpoint=tcp -h 172.17.0.4 -p 3032 -t 60000
    handlegroup=Hello.HelloServer.HelloHttpObjAdapter
    maxconns=200000
    protocol=not_tars
    queuecap=10000
    queuetimeout=60000
    servant=Hello.HelloServer.HelloHttpObj
    threads=3
  </Hello.HelloServer.HelloHttpObjAdapter>
</server>

与之前相比,多出了 <Hello.HelloServer.HelloHttpObjAdapter>配置节,这就是在管理平台上添加Servant在模板配置中的体现。

接下来,修改服务入口点代码

const Tars   = require("@tars/rpc");

Tars.server.getServant(process.env.TARS_CONFIG).forEach(function (config){
    if(config.protocol === "tars"){
      //TODO 实现tars rpc server启动逻辑
    } else {
      //TODO 实现http server启动逻辑
    }
});

Tars.server.getServant解析模板配置文件后返回的config就是各个Adapter配置节中的属性,开发者可以根据其中的endpoint属性来监听端口。
此时这个服务就启动了两个Obj,在两个端口上实现了两种不同的协议。