关于后端:TARS-RPC-通信框架|提供多种远程调用方式

33次阅读

共计 4476 个字符,预计需要花费 12 分钟才能阅读完成。

作者 | Eaton

导语 | TARS 中提供了一套高性能 RPC 通信框架,实现了服务间的高效通信。RPC 作为微服务的核心技术,撑持着挪动互联网时代下一直增长的用户和海量的申请。为了满足更多的需要,TARS 反对了同步、异步等多种调用形式。本文将会具体论述 TARS 中的几种近程调用形式。

目录

  • RPC 简介
  • TARS 服务寻址形式
  • TARS 近程调用形式

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

RPC 简介

RPC,即近程过程调用,是一种通过网络向近程计算机申请服务,而不须要理解底层网络技术的思维。通过屏蔽音讯打包、服务寻址等近程网络通信细节,使近程调用就像调用本地函数或者本地对象的办法一样调用近程计算机的函数。

服务寻址是近程调用的根底。实现服务的近程调用,先要晓得服务的地址,找到可调用的服务后,能力对服务发动无效的近程调用。上面咱们先来理解一下 TARS 中的寻址形式。

TARS 服务寻址形式

TARS 服务的寻址形式,依照服务是否在主控节点 Registry 注册,通常能够分为两种形式:间接寻址和名字服务(主控路由服务)。

间接寻址,顾名思义,就是间接指定要调用的服务的地址,例如上面代码中,咱们间接指定了要调用服务的具体地址,后续的调用都会拜访这个服务。

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 框架部署,原理如下

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

近程调用形式

获取到服务地址列表后,客户端将发动服务调用。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/…

或扫码获取:

正文完
 0