热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

TARSRPC通信框架|提供多种远程调用方式

作者|Eaton导语|TARS中提供了一套高性能RPC通信框架,实现了服务间的高效通信。RPC作为微服务的核心技术,支撑着移动互联网时代下不断增长的用

作者 | Eaton

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


目录

    • RPC 简介
    • 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
#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 17890client>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/building-microservice-platforms-with-tars

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

或扫码获取:

QR


推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了最长上升子序列问题的一个变种解法,通过记录拐点的位置,将问题拆分为左右两个LIS问题。详细讲解了算法的实现过程,并给出了相应的代码。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 本文介绍了指针的概念以及在函数调用时使用指针作为参数的情况。指针存放的是变量的地址,通过指针可以修改指针所指的变量的值。然而,如果想要修改指针的指向,就需要使用指针的引用。文章还通过一个简单的示例代码解释了指针的引用的使用方法,并思考了在修改指针的指向后,取指针的输出结果。 ... [详细]
author-avatar
xwu9052591
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有