Tars框架进阶

总结Tars中一些重要的原理与使用方式,内容可能会有部分内容与官方docs重合,但绝对不会是官方docs的搬运。

本文适合已经可以自行安装好Tars,并且使用过一段时间Tars,对其有一些基本感知的人阅读。当然,同时也欢迎Tars新鸟、老鸟来交流。

Tars中一些重要的概念

Tars核心点有四个:

  • 一套服务器框架(特点:高稳定性、高可用性、高性能);
  • 十分方便的内建RPC机制(特点:开发使用简单方便);
  • 丰富的公用库(特点:封装了大部分常用方法,易于使用);
  • 服务治理平台(功能:服务部署、发布、配置、监控、调度管理、容灾);

Tars中的客户端:

这是一个比较容易混淆的概念,Tars是一个服务器框架,这里的客户端只是对调用接口者一种统称:

  • Tars里的客户端,做个比喻:有两个Tars Server – ServerA与ServerB,ServerA需要调用ServerB提供的接口获取信息,那么此时ServerA看做成客户端,ServerB看做成服务端;

在Tars内部,对于客户端,提供了四种调用服务端方式:

  • 同步阻塞,官方有样例,代码结构最简单;
  • 异步非阻塞,官方有样例,需要自己继承CallBack实现;
  • Future/Promise,官方无样例,异步代码同步化;
  • Coroutine,官方无样例,在配置里面[OpenCoroutine]默认是关闭的;

一句经验之谈:Tars的客户端一般是属于被服务端“利用”的状态,即在服务端的代码中利用上述四种方式调用其他Tars服务端的接口(隐藏了客户端的概念)。

所以,仅仅只是使用Tars,我们不必纠结客户端的概念,只需要知道它大概是一个什么角色即可,若是想要深入源码去剖析、学习Tars原理就必须得要区分清楚。

需要注意是:Tars客户端支持多线程,可在[运维管理]->[模版管理]->[编辑tars.default]中的<client>标签下面加入netthread = n修改客户端线程的数量(默认值是1)。

每个客户端线程下面会持有m个异步处理线程,异步处理线程主要是处理上述接口调用方式后三种的调用结果,其值可以在[服务列表]->[编辑]->[异步线程数]输入框里面修改线程数量。

为什么需要知道这些设置?因为根据这些设置,再结合机器资源与自己要实现的业务属性,可以定制化自己需要的线程数,达到资源利用最优!

Tars中的服务端:

在Tars中,具体的业务服务都从Servant继承实现,一个Tars节点可以拥有多个不同的Servant,这些Servant可以根据需要自主的选择Tars/非Tars协议,Tcp/Udp协议。

推荐的做法是:Tars内部之间的服务提供可以选择Tars协议 + Tcp(在同一IDC基本网络耗时在1-2ms);对外非Tars提供服务选择非Tars协议 + Tcp/Udp

需要注意是:Tars的Udp是原生的,若想实现Udp的有序和可靠,且不修改Tars源码,可以选择嵌入KCP

Tars服务端也支持多线程,可在[管理Servant]->[编辑]->[线程数]输入框里面修改线程数量。

 

Tars客户端使用

抽象一个场景:Tars应用Lan,其中一个服务名字叫TestServer,该服务提供业务接口的Servant叫TestServant,提供Hello()这个接口,这个服务有n个节点。

现在在同样的应用Lan里面,有一个Tars服务作为客户端想调用Hello()接口:

    TestServantPrx testPrx = Application::getCommunicator()->stringToProxy<TestServantPrx>("Lan.TestServer.TestServant");
    //这个方法会自动找到有效节点进行轮询调用
    testPrx->Hello();

    //可以获得刚才调用节点的IP、PORT相关信息
    //const TC_Endpoint ep = testPrx->tars_invoke_endpoint();

 

这次调用默认是按轮询的,tars也提供了hash方式的调用,保证相同key,会调用相同的active节点(这里隐含的重点是会自动屏蔽掉inactive的节点):

    TestServantPrx testPrx = Application::getCommunicator()->stringToProxy<TestServantPrx>("Lan.TestServer.TestServant");
    testPrx->tars_hash(0)->Hello();
    testPrx->tars_consistent_hash(0)->Hello();

 

也可以指定节点调用,前提你需要知道它的协议,IP,PORT:

    TestServantPrx testPrx = Application::getCommunicator()->stringToProxy<TestServantPrx>("Lan.TestServer.TestServant@tcp -h 10.120.129.226 -p 20001");

 

获得对应TestServer所有节点的IP列表:

    TestServantPrx testPrx = Application::getCommunicator()->stringToProxy<TestServantPrx>("Lan.TestServer.TestServant");
    vector<EndpointInfo> actives;   //有效节点列表
    vector<EndpointInfo> inactives; //失效节点列表
    testPrx->tars_endpointsAll(actives, inactives);
    vector<EndpointInfo>::const_iterator it = actives.begin();
    for ( ; it != actives.end(); it++) {
        const EndpointInfo &ep = *it;
        //ep里面包含了协议、IP、PORT等等信息
    }

 

Tars服务端一些方法

场景和客户端相同,我们想获取当前TestServant节点的协议、IP、PORT等信息:

    //需要在TestServant的子类里调用
    TC_EpollServer *e = getHandle()->getEpollServer();
    const TC_Endpoint ep = e->getBindAdapter(ServerConfig::Application + "." + ServerConfig::ServerName + ".TestServantAdapter")->getEndpoint();
    //ep里面包含了协议、IP、PORT等等信息

 

获得当前SET相关信息

    if (tars::ClientConfig::SetOpen && !tars::ClientConfig::SetDivision.empty()) {
        vector<string> set = TC_Common::sepstr<string>(tars::ClientConfig::SetDivision, ".");
        //set[0]-Set名  set[1]-Set区域   set[2]-Set组
    }

怎么看待Tars服务端协议接口?

Tars所有的接口都是socket在TCP/UDP应用层的封装,不论你用的什么语言,Java,C++,PHP……只要有socket存在的地方Tars接口就可以被调用。所以任何系统任何语言只要通过socket按照特定的协议格式都可以与Tars服务进行连接。具体原理与步骤可以参考官方的文档:TUP

现在Tars加入了接口鉴权,不好评价这个功能,我觉得Tars最需要的是一个易扩展的前置验证,就是在进入www.yourtars.com:8080的时候需要验证身份才可以进入,临时的方案可以使用resin自带的验证功能,缺点是不太利于集中管理权限。当然也可以自己在Tars管理页面前面套一层验证管理,这个成熟方案比较多,没什么特别要求的话大家选取合适和喜欢就好。

 

Tars的缺点

说了这么多Tars的亮点,下面也谈谈个人觉得Tars不太妥的地方。

一台服务器(物理机、虚拟机、云主机)不能部署同应用的同种Server

比如我都轻量级的微服务,AServer我想要部署4个进程,那么必须分布在4个不同的服务器上。资源有一定的浪费,好处是提高了服务可用性。

部署对于Tars新手门槛略高

虽然现在官方在推直接部署好的Tars云服务器,然后也有民间制作的docker,但是原生的安装步骤边边角角的坑实在太多了,尤其是对于linux不太熟悉的人。

小BUG依旧很多

Tars服务自带的配置文件框无法拉大,adminRegistry线程过多(官方已经修复),Tars自带服务QueryxxxServer的coredump(官方已经修复)……虽然都是不影响核心业务的小问题,总之还是需要不断的完善,不断的有人趟坑。

文档不全,缺少核心社区

官方文档很多都是早期腾讯内部就已存在的,开源以来更新的文档屈指可数,个人觉得少了几处比较关键的:客户端Future/Promise与Coroutine的使用样例、服务扩/缩容原理与注意事项、核心线程的协作关系;社区就不用说了,Tars群基本都是伸手党,官方少了一点带头作用。

最后希望Tars能越来越好!

(全文结束)


转载文章请注明出处:漫漫路 - lanindex.com

6 Comments

 Add your comment
  1. 博主请问关于Tars框架的负载均衡中的权重轮询算法能不能举个场景,讲解一下?

    • Tars负载均衡有Hash、一致性Hash、轮询、权重三种方式(Hash是第一优先,如果设置了Hash会无视其他负载均衡设置)

      Hash和一致性Hash原理就不多解释了,使用场景举一个简单例子,比如id:1,3,5->进入A机器处理,id:2,4,6进入B机器处理;
      在id分布均匀的情况下,每台机器的负载都差不多。

      轮询,跟字面意思一样,挨个来处理,负载分布最均匀,对于机器的增减不敏感。

      为什么轮询分布均匀还需要Hash?原因是Hash有额外的好处:可以把同一id每次固定到同一台机器上处理。

      权重,理解为可以调控的分布,对于有线上灰度的场景和机器资源不均匀的场景非常适用。

  2. “Tars自带服务QueryxxxServer的coredump(我已经提交了Pull Request,但是没反应)”

    这个在1月份已经merge了

  3. 博主后面会对tars源码做个分析吗?

Leave a Comment

Your email address will not be published.