TARS RPC 通讯框架|提供多种远程调用方式

做者 | Eaton ios

导语 | TARS 中提供了一套高性能 RPC 通讯框架,实现了服务间的高效通讯。RPC 做为微服务的核心技术,支撑着移动互联网时代下不断增加的用户和海量的请求。为了知足更多的需求,TARS 支持了同步、异步等多种调用方式。本文将会详细阐述 TARS 中的几种远程调用方式。git

目录

  • RPC 简介
  • TARS 服务寻址方式
  • TARS 远程调用方式github

    • 同步调用
    • 异步调用
    • 单向调用
    • Hash调用

RPC 简介

RPC,即远程过程调用,是一种经过网络向远程计算机请求服务,而不须要了解底层网络技术的思想。经过屏蔽消息打包、服务寻址等远程网络通讯细节,使远程调用就像调用本地函数或者本地对象的方法同样调用远程计算机的函数。服务器

服务寻址是远程调用的基础。实现服务的远程调用,先要知道服务的地址,找到可调用的服务后,才能对服务发起有效的远程调用。下面咱们先来了解一下 TARS 中的寻址方式。网络

TARS 服务寻址方式

TARS 服务的寻址方式,按照服务是否在主控节点 Registry 注册,一般能够分为两种方式:直接寻址和名字服务(主控路由服务)。app

直接寻址,顾名思义,就是直接指定要调用的服务的地址,例以下面代码中,咱们直接指定了要调用服务的具体地址,后续的调用都会访问这个服务。框架

auto prx = comm->stringToProxy<Demo::HelloPrx>(
    "Test.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 8088");
prx->testHello("abc");

名字服务,即咱们只需经过服务的名字就能调用某个服务,而不须要提供服务的具体地址,以下异步

auto prx = comm->stringToProxy<Demo::HelloPrx>("Test.HelloServer.HelloObj");
prx->testHello("abc");

因为代码中无需写具体IP配置,名字服务很大程度上提升了代码的可维护性。使用名字服务要求服务在主控节点 Registry 注册,即服务须要经过 TARS 框架部署,原理以下async

客户端经过调用 stringToProxy 向主控请求要调用服务的地址列表。主控将返回服务地址列表给客户端,以供客户端发起服务调用。tcp

远程调用方式

获取到服务地址列表后,客户端将发起服务调用。TARS 中提供了多种调用方式,使开发者可以根据具体的使用场景,选择合适的调用方式。

  • 同步调用:发起调用后,等待调用返回结果,再继续执行后续逻辑;
  • 异步调用:发起调用后,马上执行后续逻辑,经过回调函数处理返回结果;
  • 单向调用:只发起调用,不关心返回结果或被调服务是否接收;
  • Hash 调用:同一用户的屡次调用都请求同一服务器的服务。

让咱们用TarsCpp的例子来看看这几种调用方式是如何使用的。本部分使用的例子中,调用的服务名字为 Demo.HelloServer.HelloObj,其接口文件 Hello.tars 中接口定义以下

module Demo
{

interface Hello
{
    int testHello(string req, out string rsp);
};

};

接口直接将传入的字符串返回,实现以下

int HelloServerImp::testHello(const string & req, string & rsp, tars::TarsCurrentPtr current)
{
    rsp = req;
    return 0;
}

关于服务的具体开发和部署流程,请参阅官方文档,这里再也不赘述。

同步调用

同步调用是最多见的调用,也是最简单的调用。顾名思义,就是发起调用后,等待返回结果,可以知足大多数状况下的需求。

下面是一个客户端同步调用服务接口 testHello 的例子。调用过程和函数调用相似,经过服务通讯代理对象 prx 调用服务的接口 testHello,获取返回值。

#include <iostream>
#include "servant/Communicator.h"
#include "servant/ServantProxy.h"

#include "Hello.h"  // Hello.tars 生成的头文件

using namespace std;
using namespace tars;

static string helloObj = "Demo.HelloServer.HelloObj";

int main(int argc, char *argv[])
{
    CommunicatorPtr comm = new Communicator();
    try
    {
        // 加载配置
        TC_Config conf;
        conf.parseFile("config.conf");
        comm->setProperty(conf);

        // 生成代理
        auto prx = comm->stringToProxy<Demo::HelloPrx>(helloObj);
        
        string rsp;

        // 发起同步调用
        int ret = prx->testHello("Hello", rsp);
        if (ret == 0)
            cout << "Call successfully: " << rsp << endl;
    }
    catch (exception &e)
    {
        cerr << "error: " << e.what() << endl;
    }
    catch(...)
    {
        cerr << "Unknown Error" << endl;
    }
}
TC_Config 是 TARS 中提供的可以用于加载配置的工具类,相关使用方式能够参考文章 [微服务开源框架TARS 之 基础组件]()。

编译执行这个例子,结果以下

$ ./Client
Call successfully: Hello

上述例子中,经过加载配置文件 config.conf 初始化了客户端的通讯器 comm,配置文件具体内容以下

<tars>
  <application>
    <client>
      # 主控地址
      locator = tars.tarsregistry.QueryObj@tcp -h 127.0.0.1 -t 60000 -p 17890
    </client>
  </application>
</tars>

能够看到,配置文件中咱们配置了主控的地址。这样就像前面 TARS 寻址方式中介绍的,咱们就不须要指定服务的地址,可以经过名字服务找到服务。

异步调用

同步调用很简单很常见,但并不能适应全部场景。但遇到调用的接口耗时比较长,或是接口返回结果对后续逻辑没有影响等状况时,使用同步调用会阻塞后续过程,影响应用性能,咱们能够选择异步调用。

发起异步调用后,程序会马上执行后续逻辑,而不关心调用的返回结果。异步调用后,通常会在调用结果返回后,经过注册回调函数对它处理。TarsCpp 中,回调对象包含两个回调函数,分别处理调用成功和调用失败的逻辑。接口 testHello 回调对象的定义以下:

// 定义回调方法
struct HelloCallback : public Demo::HelloPrxCallback
{
    // 异步调用成功逻辑
    virtual void callback_testHello(int ret, const string & rsp)
    {
        cout << rsp << endl;
    }
    // 异步调用失败逻辑
    virtual void callback_testHello_exception(tars::Int32 ret)
    {
        cout << "callback exception: " << ret << endl;
    }
};

修改前面的同步调用逻辑,咱们能够经过调用 async_testHello 来进行异步调用,以下

...
        // 加载配置
        TC_Config conf;
        conf.parseFile("config.conf");
        comm->setProperty(conf);

        // 生成代理
        auto prx = comm->stringToProxy<Demo::HelloPrx>(helloObj);

        // 定义远程回调对象
        Demo::HelloPrxCallbackPtr cb = new HelloCallback;

        // 发起异步调用
        string req = "Hello";
        prx->async_testHello(cb, req);
        cout << "Call testHello async" << endl;
        // 等待调用完成
        sleep(1);
    ...

这里咱们添加 sleep(1) 等待远程调用完成并执行回调逻辑。编译执行这个例子,结果以下

$ ./Client
Call testHello async
Hello

单向调用

顾名思义,单向调用就是单方面发起调用,只管发送数据,彻底不关心调用返回结果。单向调用能够认为是不处理返回结果的异步调用的一种。

所以,单向调用的方式和异步调用的方式同样使用 async_testHello 便可,但不须要定义回调对象,传入 NULL 便可,以下

...
        string req = "Hello";
        // 发起单向调用
        prx->async_testHello(NULL, req);
    ...

Hash 调用

前面咱们介绍过 TARS 的名字服务,是经过主控获取对应服务的多个地址列表。所以一个服务能够部署多台,请求也是随机分发到这些服务器上。可是在某些场合下,但愿某些请求老是在某一台服务器上,对于这种状况 TARS 提供了简单的方式实现,即 Hash 调用。

假设咱们传入的参数 Hello 为用户 ID,经过 Hash 调用,每次传入的 ID 值为 Hello 时,每次Hello用户请求时,调用到的都是同一台服务器。

本文hash 调用的例子直接在同步调用的基础上进行修改。只须要在调用前链式调用 tars_hash 便可,修改的部分以下

...

#include "util/tc_hash_fun.h"

    ...

        int ret = prx->tars_hash(hash_new<string>()("Hello"))->testHello("Hello", rsp);
    ...
注意:
这种方式是有必定风险的。若是后台某台服务器挂了,这些请求就会迁移到其余服务器。等到服务器恢复后,会再迁移回来。

tars_hash 参数必须是 int。对于 string 类型来讲,能够像上述例子同样,使用 TARS 基础库(util目录下)中的 hash_new 方法,具体请参见 util/tc_hash_fun.h.

总结

TARS 除了支持直接寻址,还支持的名字服务路由的方式发现服务,提升了代码的可维护性。同时提供多种远程调用方式,开发者可以自由选择,知足多种场景下的需求。

TARS 能够在考虑到易用性和高性能的同时快速构建系统并自动生成代码,帮助开发人员和企业以微服务的方式快速构建本身稳定可靠的分布式应用,从而令开发人员只关注业务逻辑,提升运营效率。多语言、敏捷研发、高可用和高效运营的特性使 TARS 成为企业级产品。

TARS微服务助您数字化转型,欢迎访问:

TARS官网:https://TarsCloud.org

TARS源码:https://github.com/TarsCloud

Linux基金会官方微服务免费课程:https://www.edx.org/course/bu...

获取《TARS官方培训电子书》:https://wj.qq.com/s2/6570357/...

或扫码获取:

QR

相关文章
相关标签/搜索