admin管理员组

文章数量:1551684

最近在学习腾讯Tars框架,在学习和使用过程中遇到了很多的问题,由于网上信息太少和本身介绍的不完整,因此在这里开贴来讲述以下自己在使用中遇到的问题和解决的方法,新人一枚,本身也在学习使用中,有不对的地方还望指正。

既然是讲Tars的就不能免俗,想摘一段git上tars的描述(https://github/TarsCloud/Tars/blob/1b8a29a59f1104b6bb16bde5fb2b1de3c5d8cfbd/README.zh.md)

Tars
Tars这个名字取自于电影"星际穿越"中的机器人,它是基于名字服务使用Tars协议的高性能RPC开发框架,配套一体化的运营管理平台,并通过伸缩调度,实现运维半托管服务。

Tars是腾讯从2008年到今天一直在使用的后台逻辑层的统一应用框架TAF(Total Application Framework),目前支持C++,Java,PHP,Nodejs语言。该框架为用户提供了涉及到开发、运维、以及测试的一整套解决方案,帮助一个产品或者服务快速开发、部署、测试、上线。 它集可扩展协议编解码、高性能RPC通信框架、名字路由与发现、发布监控、日志统计、配置管理等于一体,通过它可以快速用微服务的方式构建自己的稳定可靠的分布式应用,并实现完整有效的服务治理。

目前该框架在腾讯内部,各大核心业务都在使用,颇受欢迎,基于该框架部署运行的服务节点规模达到上万个。

这里Tars有一个管理后台TarsFramework,一个web管理控制台TarsWeb。官方有比较详细的安装介绍(https://github/TarsCloud/Tars/blob/1b8a29a59f1104b6bb16bde5fb2b1de3c5d8cfbd/Install.zh.md),这里就不再赘述了。然而关于平台启动,需要读懂说明中的这句话

手工部署的核心基础服务:tarsAdminRegistry, tarsregistry, tarsnode, tarsconfig, tarspatch

对没有错,tarsAdminRegistry, tarsregistry, tarsnode, tarsconfig, tarspatch这几个服务安装好后,需要手动去util目录下挨个启动,可以写一个一起启动的脚本来完成启动。

之后在管理平台上配置并上传其他服务对应的tgz包发布。最后结果如下图所示,testapp是我后来加的应用

然后让我们进入正题,编写并发布nodejs rpc服务。首先来一段tars.js的说明(https://github/tars-node/Tars.js)

Tars.js 在腾讯内部经过 5 年多的沉淀与迭代(Node.js@0.10版本即提供支持),广泛运用于腾讯QQ浏览器、腾讯桌面浏览器、腾讯地图、应用宝、腾讯手机管家、互联网+、腾讯医疗、腾讯觅影、保险、彩票等几十个重要业务中,日承担了上百亿流量。

Tars.js 包含下述特性:
100% 由 JavaScript 编写,不包含任何 C/C++ 代码。
多进程负载均衡与管理。
代码异常监控与重启。
服务日志搜集与处理。
HTTP(s) 服务监控与用量自动上报,并支持用户自定义维度上报(PP 监控)。
符合 Tars(IDL) 规范的编解码模块。
支持 Tars RPC 调用与染色(模调自动上报)。
支持在线发送管理命令、拉取服务配置。
独创 LongStackTrace™ 异常跟踪机制。
…… 更多特性可访问 @tars/node-agent 了解

OK,首先我们来看下RPC(https://github/tars-node/rpc)

先来个本地rpc看看情况。服务端按照要求先写个hello.tars

/**
 * Tencent is pleased to support the open source community by making Tars available.
 *
 * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * https://opensource/licenses/BSD-3-Clause
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

module TestApp
{
	interface Hello
	{
	    int hello(int no, string name,out string back);
	};
};

tars2node 工具会生成 hello.js 与 helloImp.js(

tars2node --server hello.tars

编辑helloImp.js,写个业务逻辑

// **********************************************************************
// Parsed By TarsParser(1.1.0), Generated By tars2node(20180713)
// TarsParser Maintained By <TARS> and tars2node Maintained By <superzheng>
// Generated from "hello.tars" by Imp Mode
// **********************************************************************

/* eslint-disable */

"use strict";

var TestApp = require("./hello.js").TestApp;
module.exports.TestApp = TestApp;

TestApp.HelloImp.prototype.initialize = function () {
    //TODO::
};

TestApp.HelloImp.prototype.hello = function (current, no, name, back) {
    //TODO::
	current.sendResponse(200, 'hello '+no+':'+name);
};


之后写个启动类localserver.js(别忘了npm install @tars/rpc)

var Tars  = require("@tars/rpc").server;
var TestApp = require("./helloImp.js").TestApp;

var svr  = Tars.createServer(TestApp.HelloImp);
svr.start({
    name     : "testapp.hello.HelloObjAdapetr",
    servant  : "testapp.hello.HelloObj",
    endpoint : "tcp -h 127.0.0.1 -p 12345 -t 10000",
    protocol : "tars",
	maxconns : 200000
});

console.log("server started.");

可以看到我们这个本地服务名为testapp,业务名为hello,Servant名为HelloObj 监听端口12345

node  localserver.js 启动 会看到如下

好了服务端启动后我们来写个客户端调用

tars2node 工具会生成 helloProxy.js(

tars2node --client hello.tars

写个本地调用localclient.js 

//STEP01 引入系统模块以及工具生成的代码
var Tars  = require("@tars/rpc").client;
var TestApp = require("./helloProxy.js").TestApp;

//STEP02 初始化Tars客户端
//       该步骤非必选项,后续文档将介绍[tars].client.initialize函数在什么情况下需要调用以及它做了哪些工作
//       initialize函数只需调用一次,初始化之后全局可用
//       在演示程序中我们不需要使用过多的特性,所以先将其注释
//Tars.initialize("./config.conf");

//STEP03 生成服务端调用代理类实例
var prx = Tars.stringToProxy(TestApp.HelloProxy, "testapp.hello.HelloObj@tcp -h 127.0.0.1 -p 12345 -t 60000");

//STEP04 客户端调用采用Promise机制进行回调,这里编写成功以及失败的回调函数
var success = function (result) {
	console.log("result.response.costtime:",    		result.response.costtime);
	console.log("result.response.return:",      		result.response.return);
	console.log("result.response.arguments.back:",  result.response.arguments.back);
}

var error = function (result) {
	console.log("result.response.costtime:",			result.response.costtime);
    console.log("result.response.error.code:",         	result.response.error.code);
    console.log("result.response.error.message:",       result.response.error.message);
}

//STEP05 初始化接口参数,开始调用RPC接口

prx.hello(1,'nodeclient').then(success, error).done();

调用hello这个方法,node localclient.js

好了,至此我们可以看到本地的一个rpc就OK了,客户端通过rpc请求服务端的hello方法

 

接下来我们就把我们的localserver.js改一改放到平台上发布吧。

1. 一定要注意既然把这个服务以模块形式发布,那么这个localserver.js就要改名为index.js作为模块入口(我就是被坑了)

2.index.js内容如下

//tars online
var Tars  = require("@tars/rpc");
var TestApp = require("./helloImp.js").TestApp;
 
var svr = new Tars.server();
svr.initialize("../../conf/testapp.hello.config.conf", function (server){
    server.addServant(TestApp.HelloImp, server.Application + "." + server.ServerName + ".HelloObj");
});
 
//STEP03 上步初始化服务之后,开始启动服务
svr.start();
console.log("server started.");

这其中要特别注意  "../../conf/testapp.hello.config.conf"文件路径是node上发布后配置文件的路径(血泪实践的经验)

3. 打包   

tars-deploy hello

(没安装@tars/deploy记得安装上

npm i -g @tars/deploy

),特别提醒windows用户看下打包时的本地路径下是否有个node_modules文件夹,如果没有就看是不是在C:\Users\你的账号\node_modules中,如果在就考到你要打包的文件路径中,否则会报找不到@tars/rpc的错误(血泪实践的经验)

4. 然后就去创建个app配置业务名和Servant  然后把包发布上去

如果不是两个绿色的Active就是有问题,自己去tarsnode节点data或者logs下看下日志时什么错误

好了,这下之后的重点来了,如何写个客户端远程RPC去调用服务端的这个hello服务呢

我们可以把我们之前的localclient中的ip和端口改成我们部署的服务器的ip和hello rpc的端口。没错,这样是可以访问的。

但是真的是这样吗?我们来看看官方关于rpc client的结构介绍图(https://github/TarsCloud/Tars/blob/master/Introduction.md)

看到了吗,是通过tarsregistry获取HelloObj的ip和端口的。

那么好,我们来看看如何通过tarsregistry来获取HelloObj的ip和端口。我非常开心的点开了github上tars-node/registry

然后我惊呆了

readme.md呢,

@tars/registry:用于 Tars 名字服务查询(Servant ===> Endpoint)

说好的(“每个模块(点击名称跳转)均有极为详细的文档(README)方便您在任何时候查阅。”)呢

好吧,没有就没有吧,我们就看看源码吧,作为一个nodejs菜鸟,可想而知看这些代码有多么让我吐血。

经过了一段时间的研读,我终于破解了模块。。。的冰山一角

直接上代码

var a=require('@tars/registry');
a.setLocator("tars.tarsregistry.QueryObj@tcp -h 192.168.36.151 -p 17890");

console.log(a.initialize);
console.log(a._locator);
console.log(typeof(a));
var c=a.findObjectById('testapp.hello.HelloObj');
//console.log(c)
c.then(function(value) {
    // body
   // console.log(value)
   //  console.log(value.response)
   //  console.log(value.response.return.length)
    console.log(value.response.return.value[0].host)
    console.log(value.response.return.value)

}).catch(function(reason) {
    console.log(reason)
})

我看了promise的使用,看了模块的exprot不断的测试,最后中遇拿到了我想要的数据

[Function]
tars.tarsregistry.QueryObj@tcp -h 192.168.36.151 -p 17890
object
192.168.36.151
[ { host: '192.168.36.151',
    port: 10010,
    timeout: 60000,
    istcp: 1,
    grid: 0,
    groupworkid: -1,
    grouprealid: -1,
    setId: '',
    qos: 0,
    bakFlag: 0,
    weight: 0,
    weightType: 0,
    _classname: 'tars.EndpointF' } ]

看到了吗,host: '192.168.36.151', port: 10010,那一刻我热泪盈眶

不过还有个问题,我们如何从文件获取tarsregistry的地址呢,这里我们就用到了tars/utils(https://github/tars-node/utils)

先写个配置文件config.conf

<tars>
    <application>
        <client>
            locator = tars.tarsregistry.QueryObj@tcp -h 192.168.36.151 -p 17890 ##定义主控地址
            async-invoke-timeout=60000									   ##异步调用的超时时间(ms)
        </client>
    </application>
</tars>

然后读取locator

var Config = require('@tars/utils').Config;
var config = new Config();
config.parseFile('./config.conf', 'utf8');
console.log('get: tars.application.server.local: ', config.get('tars.application.client.locator'));
var locator=config.get('tars.application.client.locator');

好了,我们至此将客户端远程rpc通过registry的访问中的问题都解决了,接下来看完整的客户端代码

//STEP01 从配置文件中获取locator地址
var Config = require('@tars/utils').Config;
var config = new Config();
config.parseFile('./config.conf', 'utf8');
console.log('get: tars.application.server.local: ', config.get('tars.application.client.locator'));
var locator=config.get('tars.application.client.locator');
//STEP02 从regist中获取到对应对象的地址列表
var a=require('@tars/registry');
a.setLocator("tars.tarsregistry.QueryObj@tcp -h 192.168.36.151 -p 17890");

console.log(a.initialize);
console.log(a._locator);
console.log(typeof(a));
var c=a.findObjectById('testapp.hello.HelloObj');
//console.log(c)
c.then(function(value) {
    // body
   // console.log(value)
   //  console.log(value.response)
   //  console.log(value.response.return.length)
    console.log(value.response.return.value)
	
	return value.response.return.value
	
}).then(function(value) {
	
	var host=value[0].host
	var port=value[0].port
//STEP03 rpc
	var Tars  = require("@tars/rpc").client;
    var TestApp = require("./helloProxy.js").TestApp;
	
	
	var prx = Tars.stringToProxy(TestApp.HelloProxy, "testapp.hello.HelloObj@tcp -h "+host+" -t 60000 -p "+port);
 
//STEP04 客户端调用采用Promise机制进行回调,这里编写成功以及失败的回调函数
var success = function (result) {
    console.log("result.response.costtime:",    		result.response.costtime);
    console.log("result.response.return:",      		result.response.return);
    console.log("result.response.arguments.back:",  result.response.arguments.back);
}
 
var error = function (result) {
    console.log("result.response.costtime:",			result.response.costtime);
    console.log("result.response.error.code:",         	result.response.error.code);
    console.log("result.response.error.message:",       result.response.error.message);
}

prx.hello(1,'nodeclient').then(success, error).done();
	
}).catch(function(reason) {
    console.log(reason)
})

至此nodejs的客户端请求rpc就介绍完了,之后我会再在学习中不断开新的介绍Tars的帖子。

 

本文标签: 腾讯Tarsnodejs