> For the complete documentation index, see [llms.txt](https://tarscloud.gitbook.io/tarsdocs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tarscloud.gitbook.io/tarsdocs/tars.js/tars-rpc.md).

# @tars/rpc

### 00 - 安装

> $ npm install @tars/rpc

### 01 - tars 简介

tars 是 Tars4NodeJS 项目底层的 RPC 调用框架，提供了一个多服务器进程间进行 RPC 调用的基础设施。简单来说我们可以用这个模块做这些事情：

* 使用 tars2node 将 Tars 文件翻译成客户端代理类代码后，供客户端调用任意的 Tars 服务。
* 使用 tars2node 将 Tars 文件翻译成服务端代码后，可以实现标准的 Tars 服务，该服务可被任意使用 TARS/TUP 协议的客户端直接调用。
* 远程日志、染色日志、属性上报、告警上报、tarsnode 与服务通信等框架内服务。
* 创建自定义通信协议的客户端代理类(比如使用 JSON 格式的协议)。
* 创建自定义通信协议的服务端(比如使用 JSON 格式的协议)。
* 模块：@tars/registry，功能：根据服务 Obj 名字到主控查询该服务可用的 IP 列表。

tars 分为客户端和服务器端两个部分。 客户端部分提供了 rpc 代理生成，消息路由和网络通讯等功能。 服务器端提供了远程服务暴露，请求派发，网络通讯等功能。

### 02 - 关于协议、Tars 文件以及翻译工具 tars2node 的说明

**在深入学习 tars 的相关知识之前，我们先理清`TARS编码协议`、`TUP组包协议`、`TARS组包协议`三者之间的关系：**

* TARS 编码协议是一种数据编解码规则，它将整形、枚举值、字符串、序列、字典、自定义结构体等数据类型按照一定的规则编码到二进制数据流中。对端接收到二进制数据流之后，按照相应的规则反序列化可得到原始数值。
* TARS 编码协议使用一种叫做 TAG 的整型值(unsigned char)来标识变量，比如某个变量 A 的 TAG 值为 100(该值由开发者自定义)，我们将变量值编码的同时，也将该 TAG 值编码进去。对端需要读取变量 A 的数值时，就到数据流中寻找 TAG 值为 100 的数据段，找到后按规则读出数据部分即是变量 A 的数值。
* TARS 编码协议的定位是一套编码规则。tars 协议序列化之后的数据不仅可以进行网络传输，同时还可以存储到数据库或者 DCache 中。
* TUP 组包协议是 TARS 编码协议的上层封装，定位为通信协议。它使用变量名作为变量的关键字，编码时，客户端将变量名打包到数据流中；解码时，对端根据变量名寻找对应的数据区，然后根据数据类型对该数据区进行反序列化得到原始数值。
* TUP 组包协议内置一个 TARS 编码协议的 Map 类型，该 Map 的关键字就是变量名，Map 的值是将变量的数据值经过 TARS 编码序列化的二进制数据。
* TUP 组包协议封装的数据包可以直接发送给 Tars 服务端，而服务端可以直接反序列化得到原始值。
* TARS 组包协议是对 RequestPacket（请求结构体）和 ResponsePacket（结果结构体）使用 TARS 编码协议封装的通信协议。结构体包含比如请求序列号、协议类型、RPC 参数序列化之后二进制数据等重要信息。

TARS 编码协议的编解码规则以及 Tars 文件的编写方法，请参考 [@tars/steam 文档](https://github.com/tars-node/stream/blob/master/README.md)。

**由 Tars 文件生成客户端或者服务端的代码的方法：**

首先需要编译得到 tars2node 工具，工具代码地址: [https://github.com/Tencent/Tars/tree/master/cpp/tools/tars2node](https://github.com/tars-node/rpc/blob/e1ea70acc87d6b93238675476141b7580fc66078)

**tars2node 工具简介：**

学习 Tars 文件的编写方法之后，我们就可以自己来定义通信描述文件，然后使用 tars2node 的不同命令行选项生成不同的代码文件：

> $ tars2node Protocol.tars

上述命令将忽略 interface 描述段，只转换文件中定义的“常量”、“枚举值”、“结构体”等数据类型，供开发者当不使用 Tars 框架作为调用工具时的编解码库文件。生成的文件名称为“ProtocolTars.js”。

> $ tars2node Protocol.tars --client

上述命令不仅转换文件中定义的“常量”、“枚举值”、“结构体”等数据类型，同时将 interface 的描述段翻译成 RPC 调用框架。生成的文件名称为“ProtocolProxy.js”，该文件供调用方使用。开发者引入该文件之后，可以直接调用服务端的服务。具体的使用方法请参考“npm install tars”模块的说明文档。

> $ tars2node Protocol.tars --server

上述命令不仅转换文件中定义的“常量”、“枚举值”、“结构体”等数据类型，同时将 interface 的描述段翻译成服务端的接口文件。生成的文件名称为“Protocol.js”以及“ProtocolImp.js”，开发者不要改动“Protocol.js”，只需要继续完善“ProtocolImp.js”，实现文件中具体的函数，即可作为 Tars 服务端提供服务。具体的使用方法请参考“npm install tars”模块的说明文档。

tars2node 支持的命令行参数及其作用：

| 选项                           | 作用                                                                                               |
| ---------------------------- | ------------------------------------------------------------------------------------------------ |
| --tars-lib-path=\<DIRECTORY> | 指定@tars/stream 模块的路径，默认使用 NodeJS 的目录。                                                            |
| --with-tars                  | 是否允许“tars”作为命名空间（因为 tars 这个命名空间主要用于框架服务的 tars 文件定义）。                                             |
| --dir=\<DIRECTORY>           | 生成文件的输出目录。                                                                                       |
| --relative                   | 限定所有的 Tars 文件都在当前目录寻找。                                                                           |
| --tarsBase=\<DIRECTORY>      | 指定 Tars 文件的搜索目录。                                                                                 |
| --r                          | 转换嵌套的 Tars 文件（比如在 A.tars 中包含了 B.tars 和 C.tars，使用该参数，在翻译 A.tars 的同时，也将 B.tars 和 C.tars 翻译成 JS 代码。 |
| --client                     | 生成客户端的调用类代码。                                                                                     |
| --server                     | 生成服务端的框架代码。                                                                                      |

### 03 - tars 示例和开发步骤

**文档看不下去了，马上动手实测！**

**第一步**，[下载 rpc 模块代码](https://github.com/tars-node/rpc)：

**第二步**，在 rpc 模块根目录

> $ npm install

**第三步**，在`/rpc/examples/rpc-tars/demo.1/server.node.1`目录下

> $ node main.js

启动 rpc 服务端程序

**第四步**，在`/rpc/examples/rpc-tars/demo.1/client.node.proxy`目录下

> $ node main.js

启动 rpc 客户端程序

**使用 tars 模块的开发步骤**

**第一步**，编写 tars 文件，定义客户端与服务端通信用到的常量、枚举值、结构体、函数等通信协议。我们使用如下 tars 文件作为示例：

> 一般而言 Tars 文件通常由`服务端开发`制定、维护和提供。

```cpp
module TRom
{
    struct User_t
    {
        0 optional int id = 0;
        1 optional int score = 0;
        2 optional string name = "";
    };

    struct Result_t
    {
        0 optional int id = 0;
        1 optional int iLevel = 0;
    };

    interface NodeJsComm
    {
        int test();

        int getall(User_t stUser, out Result_t stResult);

        int getUsrName(string sUsrName, out string sValue1, out string sValue2);

        int secRequest(vector<byte> binRequest, out vector<byte> binResponse);
    };
};
```

将上述内容保存为：NodeJsComm.tars。

**第二步**，根据 tars 文件生成客户端的调用代码

> $ tars2node --client NodeJsComm.tars

**第三步**，客户端程序

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

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

//STEP03 生成服务端调用代理类实例
var prx = Tars.stringToProxy(
  TRom.NodeJsCommProxy,
  "TRom.NodeJsTestServer.NodeJsCommObj@tcp -h 127.0.0.1 -p 14002 -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.stResult:",
    result.response.arguments.stResult
  );
};

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接口
var stUser = new TRom.User_t();
stUser.name = "tencent-mig";

prx.getall(stUser).then(success, error).done();
```

将上述代码保存为 client.js，使用如下命令即可调用服务端。

> $ node client.js

> result.response.costtime: 7

> result.response.return: 200

> result.response.arguments.stResult: { id: 10000, iLevel: 10001 }

如果我们只是调用方，写到这里已经足矣。按照刚才的示例，拿到相应 Tars 文件我们就可以调用 C++的 Tars 服务、Java 的 Tars 服务或者 NodeJS 的 Tars 服务。

**第四步**，实现一个 NodeJS 版本的 Tars 服务。

首先，完形填空。完成 Tars 文件中定义的 RPC 函数，实现自己的业务逻辑。

tars2node 的`--erver`选项将 Tars 文件生成服务端的代码。使用该选项翻译工具不仅转换文件中定义的“常量”、“枚举值”、“结构体”等数据类型，同时将 interface 描述段翻译成服务端的接口文件。主要生成两个文件，比如在当前例子中会生成`NodeJsComm.js`和`NodeJsCommImp.js`。开发者 **不需要也尽量不要** 改动`NodeJsComm.js`，该文件主要实现了结构体编解码、函数参数编解码、函数分发等功能。`NodeJsCommImp.js`继承与`NodeJsComm.js`，该文件主要供开发者填补定义的 RPC 函数，实现业务逻辑。

```javascript
var TRom = require("./NodeJsComm.js").TRom;
module.exports.TRom = TRom;

TRom.NodeJsCommImp.prototype.initialize = function () {
  //TODO::
};

TRom.NodeJsCommImp.prototype.test = function (current) {
  //TODO::
};

TRom.NodeJsCommImp.prototype.getall = function (current, stUser, stResult) {
  //TODO::
  //初始时，每个RPC函数都为空，需要开发者自己完形填空，补齐这里缺失的业务逻辑。
  //补齐业务逻辑之后，开发者调用current的sendResponse函数，返回数据给调用方。
  //需要注意:每个函数的sendResponse都是不一样的，它的参数与当前函数的 返回值 和 出参 相对应。
  //         如果当前函数有返回值，那么current.sendResponse的第一个参数应该是该返回。示例中当前函数的返回值为int类型，我们返回200作为示例。
  //         解决返回值的问题之后，我们按顺序写入当前的出参即可。参数的编解码和网络传输由框架解决。

  stResult.id = 10000;
  stResult.iLevel = 10001;

  current.sendResponse(200, stResult);
};

TRom.NodeJsCommImp.prototype.getUsrName = function (
  current,
  sUsrName,
  sValue1,
  sValue2
) {
  //TODO::
};

TRom.NodeJsCommImp.prototype.secRequest = function (
  current,
  binRequest,
  binResponse
) {
  //TODO::
};
```

接下来，创建一个服务入口文件。它主要负责读取配置文件、配置端口、设置协议解析器、启动服务等等工作。

```javascript
var Tars = require("@tars/rpc").server;
var TRom = require("./NodeJsCommImp.js").TRom;

var svr = Tars.createServer(TRom.NodeJsCommImp);
svr.start({
  name: "TRom.NodeJsTestServer.NodeJsCommObjAdapetr",
  servant: "TRom.NodeJsTestServer.NodeJsCommObj",
  endpoint: "tcp -h 127.0.0.1 -p 14002 -t 10000",
  protocol: "tars",
  maxconns: 200000,
});

console.log("server started.");
```

将上述代码保存为 server.js，使用如下命令启动。

> $ node server.js

> server started.

### 04 - 客户端的初始化函数\[tars].client.initialize

在演示代码中我们提到 initialize 不一定要显示调用，我们用其他方式同样可以设置我们需要的参数。

首先我们看下配置文件的格式和必要参数：

```xml
<tars>
    <application>
        <client>
            locator = tars.tarsregistry.QueryObj@tcp -h 127.0.0.1 -p 14002 ##定义主控地址
            async-invoke-timeout=60000										  ##异步调用的超时时间（ms）
        </client>
    </application>
</tars>
```

这个配置文件正是由 tarsnode 生成的，我们主要使用"tars.application.client.locator"和"tars.application.client.async-invoke-timeout"这个两个配置项。

> 什么情况下可以不用调用 initialize 函数？

> 如果我们在生成服务端代理时，每个服务端都使用直连的模式，也就是在 stringToProxy 中指定 IP 地址就可以不用初始化了。

除了使用配置文件设置这两个参数之外，我们可以调用\[tars].client 对外暴露的函数进行设置：

```javascript
var Tars = require("@tars/rpc").client;

Tars.set("locator", "tars.tarsregistry.QueryObj@tcp -h 127.0.0.1 -p 14002");
Tars.set("timeout", 60000);
```

上述的调用方法，与使用 initialize+配置文件的方式等价。

### 05 - Tars 服务的创建方法

tars 有三种方法创建一个标准的 Tars 服务：

**第一种**，使用 tarsnode 生成的配置文件。

使用这种方法与 TARS4C++的使用方式一样。

首先需要我们在 TARS 管理平台配置服务的 Obj，然后在启动程序时由 tarsnode 生成包含监听端口的配置文件，然后服务框架再依赖该配置绑定端口+启动服务。

![](/files/ZLgxSnaErI7uyLFn8tSJ)

tarsnode 生成的配置文件类似与如下：

```xml
<tars>
	<application>
		enableset=n
		setdivision=NULL
		<server>
			node=tars.tarsnode.ServerObj@tcp -h 127.0.0.1 -p 14002 -t 60000
			app=TRom
			server=NodeJsTestServer
			localip=127.0.0.1
			netthread=2
			local=tcp -h 127.0.0.1 -p 10002 -t 3000
			basepath=/usr/local/app/tars/tarsnode/data/MTT.NodeJSTest/bin/
			datapath=/usr/local/app/tars/tarsnode/data/MTT.NodeJSTest/data/
			logpath=/usr/local/app/tars/app_log//
			logsize=15M
			config=tars.tarsconfig.ConfigObj
			notify=tars.tarsnotify.NotifyObj
			log=tars.tarslog.LogObj
			deactivating-timeout=3000
			openthreadcontext=0
			threadcontextnum=10000
			threadcontextstack=32768
			closeout=0
			<TRom.NodeJsTestServer.NodeJsCommObjAdapter>
				allow
				endpoint=tcp -h 127.0.0.1 -p 14002 -t 60000
				handlegroup=TRom.NodeJsTestServer.NodeJsCommObjAdapter
				maxconns=200000
				protocol=tars
				queuecap=10000
				queuetimeout=60000
				servant=TRom.NodeJsTestServer.NodeJsCommObj
				shmcap=0
				shmkey=0
				threads=5
			</TRom.NodeJsTestServer.NodeJsCommObjAdapter>
		</server>
		<client>
			locator=tars.tarsregistry.QueryObj@tcp -h 127.0.0.1 -p 14002:tcp -h 127.0.0.1 -p 14003
			refresh-endpoint-interval=60000
			stat=tars.tarsstat.StatObj
			property=tars.tarsproperty.PropertyObj
			report-interval=60000
			sample-rate=1000
			max-sample-count=100
			sendthread=1
			recvthread=1
			asyncthread=3
			modulename=TRom.NodeJsTestServer
			async-invoke-timeout=60000
			sync-invoke-timeout=3000
		</client>
	</application>
</tars>
```

我们使用该配置文件创建一个服务，代码如下：

```javascript
//STEP01 引入关键模块
var Tars = require("@tars/rpc");
var TRom = require("./NodeJsCommImp.js");

//STEP02 创建一个服务的实例
//注意这里的配置，在正式环境时，用 process.env.TARS_CONFIG 来表示配置文件的路径
//也就是：svr.initialize(process.env.TARS_CONFIG, function (server){ ... });
var svr = new Tars.server();
svr.initialize("./TRom.NodeJsTestServer.config.conf", function (server) {
  server.addServant(
    TRom.NodeJsCommImp,
    server.Application + "." + server.ServerName + ".NodeJsCommObj"
  );
});

//STEP03 上步初始化服务之后，开始启动服务
svr.start();
```

**第二种**，显示化服务端信息

```javascript
//STEP01 引入关键模块
var Tars = require("@tars/tars").server;
var TRom = require("./NodeJsCommImp.js").TRom;

//STEP02 创建一个服务的实例
//注意这里的“endpoint”和“protocol”为必选项，格式必须如下示例相同
var svr = Tars.createServer(TRom.NodeJsCommImp);
svr.start({
  name: "TRom.NodeJsTestServer.AdminObjAdapetr",
  servant: "TRom.NodeJsTestServer.AdminObj",
  endpoint: "tcp -h 127.0.0.1 -p 14002 -t 10000",
  maxconns: 200000,
  protocol: "tars",
});

console.log("server started.");
```

**第三种**，从 tarsnode 生成的配置文件中，选取部分服务来启动

```javascript
//STEP01 引入关键模块
var Tars = require("@tars/rpc");
var TRom = require("./NodeJsCommImp.js");

Tars.server
  .getServant("./TRom.NodeJsTestServer.config.conf")
  .forEach(function (config) {
    var svr, map;
    map = {
      "TRom.NodeJsTestServer.NodeJsCommObj": TRom.NodeJsCommImp,
    };

    svr = Tars.server.createServer(map[config.servant]);
    svr.start(config);
  });
```

### 06 - Tars 客户端的实现原理

![](/files/4jLRK24ztyS5ho6qpUUM)

### 07 - Tars 服务端的实现原理

![](/files/8NwXW8KD59Txi12LeSU4)

### 08 - tars 作为客户端调用第三方协议服务的示例

首先我们先定一个双方认可的通信协议，比如我们以 Json 格式作为通信协议，格式假定：

```
//客户端 --> 服务端
{
	P_RequestId : 0, 					//本次调用的序列号
	P_FuncName  : 'test'				//本次调用的函数名称
	P_Arguments : ['aa', 'bb'.....]		//本次调用的函数参数
}

//客户端 <-- 服务端
{
	P_RequestId : 0, 					//本次调用的序列号
	P_FuncName  : 'test'				//本次调用的函数名称
	P_Arguments : ['ee', 'ff'.....]		//本次调用的返回参数
}
```

实现协议解析类：

```javascript
//将文件保存为Protocol.js
var EventEmitter = require("events").EventEmitter;
var util = require("@tars/util");

var stream = function () {
  EventEmitter.call(this);
  this._data = undefined;
  this._name = "json";
};
util.inherits(stream, EventEmitter);

stream.prototype.__defineGetter__("name", function () {
  return this._name;
});

module.exports = stream;

/**
 * 根据传入数据进行打包的方法
 * @param request
 * request.iRequestId : 框架生成的请求序列号
 * request.sFuncName  : 函数名称
 * request.Arguments  : 函数的参数列表
 */
stream.prototype.compose = function (data) {
  var str = JSON.stringify({
    P_RequestId: data.iRequestId,
    P_FuncName: data.sFuncName,
    P_Arguments: data.Arguments,
  });

  var len = 4 + Buffer.byteLength(str);
  var buf = new Buffer(len);
  buf.writeUInt32BE(len, 0);
  buf.write(str, 4);

  return buf;
};

/**
 *
 * 网络收取包之后，填入数据判断是否完整包
 * @param data 传入的data数据可能是TCP的各个分片，不一定是一个完整的数据请求，协议解析类内部做好数据缓存工作
 *
 * 当有一个完整的请求时，解包函数抛出事件，需按照如下格式补充事件的数据成员：
 *
 * {
 *     iRequestId :  0,	//本次请求的序列号
 *     sFuncName  : "",	//本次请求的函数名称
 *     Arguments  : []	//本次请求的参数列表
 * }
 *
 */
stream.prototype.feed = function (data) {
  var BinBuffer = data;
  if (this._data != undefined) {
    var temp = new Buffer(this._data.length + data.length);
    this._data.copy(temp, 0);
    data.copy(temp, this._data.length);
    this._data = undefined;
    BinBuffer = temp;
  }

  for (var pos = 0; pos < BinBuffer.length; ) {
    if (BinBuffer.length - pos < 4) {
      break;
    }
    var Length = BinBuffer.readUInt32BE(pos);
    if (pos + Length > BinBuffer.length) {
      break;
    }
    var result = JSON.parse(BinBuffer.slice(pos + 4, pos + Length).toString());
    var request = {
      iRequestId: result.P_RequestId,
      sFuncName: result.P_FuncName,
      Arguments: result.P_Arguments,
    };

    this.emit("message", request);
    pos += Length;
  }

  if (pos != BinBuffer.length) {
    this._data = new Buffer(BinBuffer.length - pos);
    BinBuffer.copy(this._data, 0, pos);
  }
};

/**
 * 重置当前协议解析器
 */
stream.prototype.reset = function () {
  delete this._data;
  this._data = undefined;
};
```

客户端使用该协议解析器，调用服务端的代码：

```javascript
var Tars = require("@tars/tars").client;
var Protocol = require("./ProtocolClient.js");

var prx = Tars.stringToProxy(
  Tars.ServantProxy,
  "test@tcp -h 127.0.0.1 -p 14002 -t 60000"
);
prx.setProtocol(Protocol);
prx.rpc.createFunc("echo");

var success = function (result) {
  console.log("success");
  console.log("result.response.costtime:", result.response.costtime);
  console.log("result.response.arguments:", result.response.arguments);
};

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

prx.rpc.echo("tencent", "mig", "abc").then(success, error);
```

另外，如果想要请求根据某个特征来发到某台固定的机器，可以使用如下方法：

```javascript
prx
  .getUsrName(param, {
    hashCode: userId,
  })
  .then(success, error)
  .done();
```

获得客户端代理对象之后，调用服务端接口函数，此时可以传入传入 hashCode 参数，tars 会根据 hashCode 来把请求分配到连接列表中固定的一台机器。 需要注意的是：

* 这里的 userId 是一个数字，二进制、八进制、十六进制都可以，但是转换成 10 进制的数字最好在 16 位数以下。javascript 处理高精度数字会有精度丢失的问题。
* 服务端机器列表固定时，同一 hashCode 会被分配到固定的一台机器，当服务端机器列表发生变化时，会重新分配 hashCode 对应的机器。

### 09 - tars 作为第三方协议服务的示例

首先实现 RPC 函数处理类，注意框架的分发逻辑：

A.如果客户端传来的函数名，是处理类的函数，那么框架有限调用对应函数

B.如果客户端传来的函数不是处理的函数，那么调用该处理类的 \*\*`onDispatch`\*\*函数，由该函数负责处理该请求

C.如果也没有 onDispatch 函数，则报错

```javascript
//将该文件保存为：EchoHandle.js
var Handle = function () {};

Handle.prototype.initialize = function () {};
Handle.prototype.echo = function (current, v1, v2, v3) {
  console.log("EchoHandle.echo::", v1, v2, v3);

  current.sendResponse("TX", "TX-MIG");
};

Handle.prototype.onDispatch = function (v1, v2, v3) {
  console.log("EchoHandle.onDispatch::", v1, v2, v3);
};

module.exports = Handle;
```

服务端启动函数的代码示例：

```javascript
var Tars = require("@tars/tars").server;
var Protocol = require("./ProtocolClient.js");
var Handle = require("./EchoHandle.js");

var svr = Tars.createServer(Handle);
svr.start({
  endpoint: "tcp -h 127.0.0.1 -p 14002 -t 10000",
  protocol: Protocol,
});
```

### 09 - tars 客户端请求参数

tars 客户端代理对象调用协议接口函数时，最后一个参数可以传入一个对象，配置一些请求参数，目前支持 4 种请求参数。

#### dyeing

请求染色对象。染色对象生成方式详见[@tars/dyeing](https://github.com/tars-node/dyeing)。

#### context

请求上下文对象。

#### packetType

请求类型参数，1 为单向请求，其他为普通请求

#### hashCode

请求 hash，必须填入 js 精度安全范围内的数字（Math.pow(2, 53) - 1）

示例：

```javascript
prx
  .getUsrName(param, {
    dyeing: dyeingObj,
    context: { xxx: xxx },
    packetType: 1,
    hashCode: userId,
  })
  .then(success, error);
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://tarscloud.gitbook.io/tarsdocs/tars.js/tars-rpc.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
