Skip to the content.

Contents

Guide to use of frame

1. C++ Server

Here is a complete example of how to use TARS to implement your own services.

The following code describes an example of sending a hello world string to the server by a client and returning the Hello word string by the server.

1.1. Defining interface

Write the tars file as follows, Hello.tars:


module TestApp
{

interface Hello
{
    int test();
    int testHello(string sReq, out string sRsp);
};

}; 

1.2. Compile interface file

The C++ file is automatically generated by the tars2cpp tool: the /usr/local/tars/cpp/tools/tars2cpp hello.tars generates a hello.h file, which is included by the client and the server.

1.3. Interface implementation

The interface defined in the tars file is implemented in the code of the service:

HelloImp.h

#ifndef _HelloImp_H_
#define _HelloImp_H_

#include "servant/Application.h"
#include "Hello.h"

/**
 * HelloImp inherits the Hello objects defined in the hello.h
 *
 */
class HelloImp : public TestApp::Hello
{
public:
    /**
     *
     */
    virtual ~HelloImp() {}

    /**
     * Initialization, the virtual function in Hello is called when HelloImp initializes.
     */
    virtual void initialize();

    /**
     * Deconstruction, The virtual function in Hello is called when the service destruct HelloImp exits.
     */
    virtual void destroy();

    /**
     * Implement the test interface defined in the tars file
     */
    virtual int test(tars::TarsCurrentPtr current) { return 0;};

    /**
     * Implement the test interface defined in the tars file
     */
    virtual int testHello(const std::string &sReq, std::string &sRsp, tars::TarsCurrentPtr current);

};
/////////////////////////////////////////////////////
#endif

HelloImp.cpp

#include "HelloImp.h"
#include "servant/Application.h"

using namespace std;

//////////////////////////////////////////////////////
void HelloImp::initialize()
{
    //initialize servant here:
    //...
}

//////////////////////////////////////////////////////
void HelloImp::destroy()
{
    //destroy servant here:
    //...
}

int HelloImp::testHello(const std::string &sReq, std::string &sRsp, tars::TarsCurrentPtr current)
{
    TLOGDEBUG("HelloImp::testHellosReq:"<<sReq<<endl);
    sRsp = sReq;
    return 0;
}

The framework code of the server

HelloServer.h:

#ifndef _HelloServer_H_
#define _HelloServer_H_

#include <iostream>
#include "servant/Application.h"

using namespace tars;

/**
 * The Application class in the framework is inherited by HelloServer
 **/
class HelloServer : public Application
{
public:
    /**
     *
     **/
    virtual ~HelloServer() {};

    /**
     * The initialization interface of the server
     **/
    virtual void initialize();

    /**
     * The interface used to clean up when the server exits
     **/
    virtual void destroyApp();
};

extern HelloServer g_app;

////////////////////////////////////////////
#endif

The contents of the HelloServer.cpp are as follows:

#include "HelloServer.h"
#include "HelloImp.h"

using namespace std;

HelloServer g_app;

/////////////////////////////////////////////////////////////////
void
HelloServer::initialize()
{
    //initialize application here:

    //Adding Servant interface to implement binding between class HelloImp and routing Obj
    addServant<HelloImp>(ServerConfig::Application + "." + ServerConfig::ServerName + ".HelloObj");
}
/////////////////////////////////////////////////////////////////
void
HelloServer::destroyApp()
{
    //destroy application here:
    //...
}
/////////////////////////////////////////////////////////////////
int
main(int argc, char* argv[])
{
    try
    {
        g_app.main(argc, argv);
        g_app.waitForShutdown();
    }
    catch (std::exception& e)
    {
        cerr << "std::exception:" << e.what() << std::endl;
    }
    catch (...)
    {
        cerr << "unknown exception." << std::endl;
    }
    return -1;
}
////////////////////////////////////////////////////////////////

Illustration:

1.4. ServerConfig

There is a global structure ServerConfig in the service framework, which records the basic information of the server.

The member variables of ServerConfig are static, and these parameters are automatically initialized from the service configuration file when the service framework is initialized.

The definition of ServerConfig is as follows:

struct ServerConfig
{
    static std::string Application;         //Application name
    static std::string ServerName;          //Server name. A server name contains one or more server identities.
    static std::string BasePath;            //The application path is used to save the local directory of remote system configuration.
    static std::string DataPath;            //The application data path is used to save common data files.
    static std::string LocalIp;             //Local IP
    static std::string LogPath;             //Log path
    static int         LogSize;             //Log size (bytes)
    static int         LogNum;              //Log number
    static std::string LogLevel;            //Log level
    static std::string Local;               //Local sockets
    static std::string Node;                //Local node address
    static std::string Log;                 //Log center address
    static std::string Config;              //Configuration center address
    static std::string Notify;              //Information notification Center
    static std::string ConfigFile;          //Frame configuration file path
    static int         ReportFlow;          //Whether to open the server to report all the interface flow, 0 is not reported, and 1 is reported (for non tars protocol service traffic statistics).
    static int         IsCheckSet;          //Whether to check the legality of the call according to the set rules, 0 does not check, and 1 checks.
    static bool        OpenCoroutine;	    //Whether to open the coroutine
    static size_t      CoroutineMemSize;    //The maximum value of the coroutine occupied memory space
    static uint32_t    CoroutineStackSize;  //Stack size for each coroutine(default 128K)

Parameter Description:

The configuration of server is as below:

<tars>
  <application>
    <server>
       #Ip:port of local node
       node=tars.tarsnode.ServerObj@tcp -h 10.120.129.226 -p 19386 -t 60000
       #Application name
       app=TestApp
       #Server name
       server=HelloServer
       #Local ip
       localip=10.120.129.226
       #Management port
       local=tcp -h 127.0.0.1 -p 20001 -t 3000
       #The server's executable files, configuration files, and so on
       basepath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/bin/
       #Data directory of the server
       datapath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/data/
       #Log path
       logpath=/usr/local/app/tars/app_log/
       #Rolling log size
       logsize=10M
       #The address of the configuration center
       config=tars.tarsconfig.ConfigObj
       #The address of the report [optional]
       notify=tars.tarsnotify.NotifyObj
       #The address of the remote log [optional]
       log=tars.tarslog.LogObj
       #Timeout time of server stop
       deactivating-timeout=2000
       #Log level
       logLevel=DEBUG
    </server>
  </application>
</tars>

1.5. Adapter

Adapter corresponds to the TC_EpollServer:: BindAdapter in the code, which indicates the binding port.

If a new binding port is added to the server, a new BindAdapter is set up, and the relevant parameters and processed objects can be very convenient to complete the processing on this port, usually with this function to complete the support of other protocols.

For TARS servers, adding adapter items to the server’s configuration files means that TARS can be added to a Servant processing object.

The Adapter configuration is as follows:

<tars>
  <application>
    <server>
       #Configuration of bound ports
       <TestApp.HelloServer.HelloObjAdapter>
            #Allowed IP address
            allow
            #The IP address of the listener
            endpoint=tcp -h 10.120.129.226 -p 20001 -t 60000
            #Processing group
            handlegroup=TestApp.HelloServer.HelloObjAdapter
            #Maximum connection
            maxconns=200000
            #Protocol
            protocol=tars
            #Queue size
            queuecap=10000
            #The timeout time of the queue (milliseconds)
            queuetimeout=60000
            #Processing object
            servant=TestApp.HelloServer.HelloObj
            #Current thread number
            threads=5
       </TestApp.HelloServer.HelloObjAdapter>
    </server>
  </application>
</tars>

Focus on the servant item. In HelloServer:: Initialize (), the matching between the configuration and the object in the code is completed.``` void HelloServer::initialize () { //Add servant addServant(ServerConfig::Application+“.”+ ServerConfig::ServerName + ".HelloObj"); }

1.6. Server startup

The server’s boot command is as follows:

HelloServer --config=config.conf

Note: config.conf is the configuration file, the configuration files of the server and the client configuration files must be merged into this file. As for servers, they can be run separately and do not need to be published on the TARS system framework.

The complete configuration is as follows:

<tars>
  <application>
    enableset=n
    setdivision=NULL
    <server>
       #Ip:port of local node
       node=tars.tarsnode.ServerObj@tcp -h 10.120.129.226 -p 19386 -t 60000
       #Application name
       app=TestApp
       #Server name
       server=HelloServer
       #Local ip
       localip=10.120.129.226
       #Management port
       local=tcp -h 127.0.0.1 -p 20001 -t 3000
       #The server's executable files, configuration files, and so on
       basepath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/bin/
       #Data directory of the server
       datapath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/data/
       #Log path
       logpath=/usr/local/app/tars/app_log/
       #Rolling log size
       logsize=10M
       #The address of the configuration center
       config=tars.tarsconfig.ConfigObj
       #The address of the report [optional]
       notify=tars.tarsnotify.NotifyObj
       #The address of the remote log [optional]
       log=tars.tarslog.LogObj
       #Timeout time of server stop
       deactivating-timeout=2000
       #Log level
       logLevel=DEBUG
        #Configuration of bound ports
       <TestApp.HelloServer.HelloObjAdapter>
             #Allowed IP address
            allow
            #The IP address of the listener
            endpoint=tcp -h 10.120.129.226 -p 20001 -t 60000
            #Processing group
            handlegroup=TestApp.HelloServer.HelloObjAdapter
            #Maximum connection
            maxconns=200000
            #Protocol
            protocol=tars
            #Queue size
            queuecap=10000
            #The timeout time of the queue (milliseconds)
            queuetimeout=60000
            #Processing object
            servant=TestApp.HelloServer.HelloObj
            #Current thread number
            threads=5
       </TestApp.HelloServer.HelloObjAdapter>
    </server>
    <client>
       #Master's address
       locator=tars.tarsregistry.QueryObj@tcp -h 10.120.129.226 -p 17890
       #Synchronous timeout time
       sync-invoke-timeout=3000
       #Asynchronous timeout time
       async-invoke-timeout=5000
       #Refresh the time interval of the IP list
       refresh-endpoint-interval=60000
       #The time interval of the reported data
       report-interval=60000
       #sampling rate
       sample-rate=100000
       #Maximum sampling number
       max-sample-count=50
       #Asynchronous thread number
       asyncthread=3
       #Template name
       modulename=TestApp.HelloServer
    </client>
  </application>
</tars>

2. C++ Client

The client can complete the remote call without writing any code related to the protocol communication.The client code also needs to include the hello.h file.

2.1. Communicator

After the server is implemented, the client needs to send and receive data packets to the server. The client’s operation of sending and receiving data packets to the server is implemented by Communicator.

** Note: A Tars service can only have one Communicator variable, which can be obtained with Application::getCommunicator() (if it is not the Tars service, create a communicator yourself).

The communicator is a carrier of client resources and contains a set of resources for sending and receiving packets, status statistics and other functions.

The communicator is initialized as follows:

TC_Config conf("config.conf");
CommunicatorPtr c = new Communicator();
//Initialize the communicator with a configuration file
c-> setProperty(conf);
//Or initialize directly with attributes
c->setProperty("property", "tars.tarsproperty.PropertyObj");
c->setProperty("locator", "tars.tarsregistry.QueryObj@tcp -h ... -p ...");

Description:

Communicator attribute description:

The format of the communicator’s configuration file is as follows:

<tars>
  <application>
    #The configuration required by the proxy
    <client>
        #address
        locator                     = tars.tarsregistry.QueryObj@tcp -h 127.0.0.1 -p 17890
        #The maximum timeout (in milliseconds) for synchronous calls.
        sync-invoke-timeout         = 3000
        #The maximum timeout (in milliseconds) for asynchronous calls.
        async-invoke-timeout        = 5000
        #The maximum timeout (in milliseconds) for synchronous calls.
        refresh-endpoint-interval   = 60000
        #Used for inter-module calls
        stat                        = tars.tarsstat.StatObj
        #Address used for attribute reporting
        property                    = tars.tarsproperty.PropertyObj
        #report time interval
        report-interval             = 60000
        #The number of threads that process asynchronous responses
        asyncthread                 = 3
        #The module name
        modulename                  = Test.HelloServer
    </client>
  </application>
</tars>

Instructions for use:

2.2. Timeout control

The timeout control is for the client proxy. There are records in the configuration file of the communicator described in the previous section:

#The maximum timeout (in milliseconds) for synchronous calls.
sync-invoke-timeout          = 3000
#The maximum timeout (in milliseconds) for asynchronous calls.
async-invoke-timeout         = 5000

The above timeout is valid for all the proxies generated by the communicator.

If you need to set the timeout separately, as shown below:

Set the timeout period for the proxy:

ProxyPrx  pproxy;
//Set the timeout for the agent's synchronous call (in milliseconds)
pproxy->tars_timeout(3000);
//Sets the timeout for the agent's asynchronous call (in milliseconds)
pproxy->tars_async_timeout(4000);

Set the timeout for the calling interface:

//Set the timeout (in milliseconds) for this interface call of this agent. This setting will only take effect once.
pproxy->tars_set_timeout(2000)->a(); 

2.3. Call interface

This section details how the Tars client remotely invokes the server.

First, briefly describe the addressing mode of the Tars client. Secondly, it will introduce the calling method of the client, including but not limited to one-way calling, synchronous calling, asynchronous calling, hash calling, and so on.

2.3.1. Introduction to addressing mode

The addressing mode of the Tars service can usually be divided into two ways: the service name is registered in the master and the service name is not registered in the master. A master is a name server (routing server) dedicated to registering service node information.

The service name added in the name server is implemented through the operation management platform.

For services that are not registered with the master, it can be classified as direct addressing, that is, the ip address of the service provider needs to be specified before calling the service. The client needs to specify the specific address of the HelloObj object when calling the service:

that is: Test.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 9985

Test.HelloServer.HelloObj: Object name

tcp:Tcp protocol

-h:Specify the host address, here is 127.0.0.1

-p:Port, here is 9985

If HelloServer is running on two servers, HelloPrx is initialized as follows:

HelloPrx pPrx = c->stringToProxy<HelloPrx>("Test.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 9985:tcp -h 192.168.1.1 -p 9983");

The address of HelloObj is set to the address of the two servers. At this point, the request will be distributed to two servers (distribution method can be specified, not introduced here). If one server is down, the request will be automatically assigned to another one, and the server will be restarted periodically.

For services registered in the master, the service is addressed based on the service name. When the client requests the service, it does not need to specify the specific address of the HelloServer, but it needs to specify the address of the registry when generating the communicator or initializing the communicator.

The following shows the address of the registry by setting the parameters of the communicator:

CommunicatorPtr c = new Communicator();
c->setProperty("locator", "tars.tarsregistry.QueryObj@tcp -h .. -p ..")

Since the client needs to rely on the registry’s address, the registry must also be fault-tolerant. The registry’s fault-tolerant method is the same as above, specifying the address of the two registry.

2.3.2. One-way call

A one-way call means that the client only sends data to the server without receiving the response from the server, and whether the server receives the request data.

TC_Config conf("config.conf");
CommunicatorPtr c = new Communicator();
//Initialize the communicator with a configuration file
c-> setProperty(conf);
//Generate a client's service proxy
HelloPrx pPrx = c->stringToProxy<HelloPrx>("Test.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 9985");
//Initiate a remote call
string s = "hello word";
string r;
pPrx->async_testHello(NULL, s);

2.3.3. Synchronous call

Take a look at the code example below:

TC_Config conf("config.conf");
CommunicatorPtr c = new Communicator();
//Initialize the communicator with a configuration file
c-> setProperty(conf);
//Generate a client's service proxy
HelloPrx pPrx = c->stringToProxy<HelloPrx>("Test.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 9985");
//Initiate a remote synchronization call
string s = "hello word";
string r;
int ret = pPrx->testHello(s, r);
assert(ret == 0);
assert(s == r);

The above example shows that the client initiates a remote synchronization call to the HelloObj object of the HelloServer.

2.3.4. Asynchronous call

Define an asynchronous callback object:

struct HelloCallback : public HelloPrxCallback
{
//Callback
virtual void callback_testHello(int ret, const string &r)
{
    assert(r == "hello word");
}

virtual void callback_testHello_exception(tars::Int32 ret)
{
    assert(ret == 0);
    cout << "callback exception:" << ret << endl;
}
};

TC_Config conf("config.conf");
CommunicatorPtr c = new Communicator();
//Initialize the communicator with a configuration file
c-> setProperty(conf);
//Generate a client's service proxy
HelloPrx pPrx = c->stringToProxy<HelloPrx>("Test.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 9985");
//Define an object for a remote callback class
HelloPrxCallbackPtr cb = new HelloCallback;

//Initiate a remote synchronization call
string s = "hello word";
string r;
pPrx->async_testHello(cb, s);

note:

//The return code given by the TARS server
const int TARSSERVERSUCCESS       = 0;       //The server is successfully processed
const int TARSSERVERDECODEERR     = -1;      //Server decoding exception
const int TARSSERVERENCODEERR     = -2;      //Server encoding exception
const int TARSSERVERNOFUNCERR     = -3;      //The server does not have this function
const int TARSSERVERNOSERVANTERR  = -4;      //The server does not have the Servant object.
const int TARSSERVERRESETGRID     = -5;      //Inconsistent gray state on the server side
const int TARSSERVERQUEUETIMEOUT  = -6;      //Server queue exceeded limit
const int TARSASYNCCALLTIMEOUT    = -7;      //Asynchronous call timeout
const int TARSINVOKETIMEOUT       = -7;      //Call timeout
const int TARSPROXYCONNECTERR     = -8;      //Proxy link exception
const int TARSSERVEROVERLOAD      = -9;      //The server is overloaded and exceeds the queue length.
const int TARSADAPTERNULL         = -10;     //The client routing is empty, the service does not exist or all services are offline.
const int TARSINVOKEBYINVALIDESET = -11;     //The client is called by an invalid set rule
const int TARSCLIENTDECODEERR     = -12;     //Client decoding exception
const int TARSSERVERUNKNOWNERR    = -99;     //Server location is abnormal

2.3.5. Set mode call

Currently, the framework already supports the deployment of services in set mode. After deployment by set, calls between services are transparent to business development. However, because some services have special requirements, after the deployment by set, the client can specify the set name to invoke the server. So the framework adds the ability for the client to specify the set name to call those services deployed by set.

The detailed usage rules are as follows:

Assume that the service server HelloServer is deployed on two sets, Test.s.1 and Test.n.1. Then the client specifies the set mode to be called as follows:

TC_Config conf("config.conf");
CommunicatorPtr c = new Communicator();
//Initialize the communicator with a configuration file
c-> setProperty(conf);
//Generate a client's service proxy
HelloPrx pPrx_Tests1 = c->stringToProxy<HelloPrx>("Test.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 9985","Test.s.1");

HelloPrx pPrx_Testn1 = c->stringToProxy<HelloPrx>("Test.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 9985","Test.n.1");

//Initiate a remote synchronization call
string s = "hello word";
string r;

int ret = pPrx_Tests1->testHello(s, r);

int ret = pPrx_Testn1->testHello(s, r);

note:

2.3.6. Hash call

Since multiple servers can be deployed, client requests are randomly distributed to the server, but in some cases, it is desirable that certain requests are always sent to a particular server. In this case, Tars provides a simple way to achieve:

If there is a request for querying data according to the QQ number, as follows:

QQInfo qi = pPrx->query(uin);

Normally, for the same call to uin, the server address of each response is not necessarily the same. However, With the following call, it can be guaranteed that each request for uin is the same server response.

QQInfo qi = pPrx->tars_hash(uin)->query(uin);

note:

3. Asynchronous nesting

Asynchronous nesting represents the following:

Normally, B needs to return a response to A after B receives the request and finishes processing in the interface. Therefore, it is not can be implemented if B initiates an asynchronous request to C in the interface.

Therefore, it is necessary to implemente the asynchronous calls across services by using the following methods. You can see the examples/QuickStartDemo/ProxyServer example for details.

The following still uses the helloworld program to explain. Firstly, the client initiates a request to the proxy, and the proxy initiates testHello to the HelloServer asynchronously after receiving the request. Then the proxy returns the result by the HelloServer to the client after the request returns.

The key logic in this process is on the ProxyServer. The following code is the logical processing in B:

//Asynchronous callback object in ProxyServer
class HelloCallback : public HelloPrxCallback
{

public:
    HelloCallback(TarsCurrentPtr &current)
    : _current(current)
    {}

    virtual void callback_testHello(tars::Int32 ret,  const std::string& sOut)
    {
        Proxy::async_response_testProxy(_current, ret, sOut);
    }
    virtual void callback_testHello_exception(tars::Int32 ret)
    { 
        TLOGERROR("HelloCallback callback_testHello_exception ret:" << ret << endl); 

        Proxy::async_response_testProxy(_current, ret, "");
    }

    TarsCurrentPtr _current;
};

//The interface defined in ProxyServer
tars::Int32 ProxyImp::testProxy(const std::string& sIn, std::string &sOut, tars::TarsCurrentPtr current)
{
    try
    {
        current->setResponse(false);

        TestApp::HelloPrxCallbackPtr cb = new HelloCallback(current);

        _prx->tars_set_timeout(3000)->async_testHello(cb,sIn);
    }
    catch(std::exception &ex)
    {
        current->setResponse(true);

        TLOGERROR("ProxyImp::testProxy ex:" << ex.what() << endl);
    }

    return 0;
}

Description:

4. Dyeing

4.1. Funcational Overview

The main function of the dyeing function is to dye the message of a specific user number in an interface of a certain service, and conveniently view the log of all subsequent related call message flows caused by the user in real time.

After the dye log is opened, the dyed log can be viewd on the server where tarslog is located. For the specific path, please refer to:

The scrolling log which is written by the LOG macro in the program is all printed to the tarslog, its log file like: tars_dyeing.dyeing_roll_yyyymmdd.log. There is one file every day, such as:

/usr/local/app/tars/remote_app_log/tars_dyeing/dyeing/tars_dyeing.dyeing_roll_20161227.log

The daily log which is written by the DLOG, FDLOG and FFDLOG in the program is all printed to the tarslog, its log file like: tars_dyeing.dyeing_day_yyyymmdd.log. There is one file every day, such as:

/usr/local/app/tars/remote_app_log/tars_dyeing/dyeing/tars_dyeing.dyeing_day_20161227.log

4.2. Rule description

The dye log has two methods: active open and passive open.

The active open means that the dyed log switch in the framework is opened on the requesting client.

The specific steps are as follows:

The passive open means that, under the pre-set dyeing condition of the requested server, the server opens the dyeing log switch of the service according to the transmitted key value.

The specific steps are as follows:

4.3. The use case of active open

The usage of active open:

#include <iostream>
#include "servant/Communicator.h"
#include "Hello.h"
#include "servant/TarsLogger.h"
#include "util/tc_option.h"
#include "util/tc_file.h"

#include <string>
using namespace std;
using namespace TestApp;
using namespace tars;

int main(int argc,char ** argv)
{
try
{
    CommunicatorPtr comm =new Communicator();
    comm->setProperty("locator", "tars.tarsregistry.QueryObj@tcp -h 10.120.129.226 -p 17890 -t 10000");
    TarsRollLogger::getInstance()->setLogInfo("TestApp", "HelloServer", "./log", 100000, 10, comm, "tars.tarslog.LogObj");
    TarsRollLogger::getInstance()->sync(false);
    TarsTimeLogger::getInstance()->setLogInfo(comm, "tars.tarslog.LogObj", "TestApp", "HelloServer", "./log");
    {
            //This log will only be printed to the local log before the dye log is opened.
            TLOGDEBUG    (__FILE__ << "|" << __LINE__ <<"Test Before Dyeing"  <<endl);
            DLOG        <<__FILE__ << "|" << __LINE__ <<"D/Test Before Dyeing"<<endl;
            FDLOG("T_D")<<__FILE__ << "|" << __LINE__ <<"F/Test Before Dyeing"<<endl;
    }
         try
         {
        	{

        	   //Declare a class TarsDyeingSwitch, and call enableDyeing to open the dye log.
        	   TarsDyeingSwitch dye;
        	   dye.enableDyeing();

        	   //You can see the log in the local and dye logs after the dye log is opened.
        	   {
        	      TLOGDEBUG    (__FILE__ << "|" << __LINE__ <<"Test Before Dyeing before call other function"  <<endl);
        	      DLOG        <<__FILE__ << "|" << __LINE__ <<"D/Test Before Dyeing before call other function"<<endl;
        	      FDLOG("T_D")<<__FILE__ << "|" << __LINE__ <<"F/Test Before Dyeing before call other function"<<endl;
        	   }
        	   

        	   string sReq("hello");
        	   std::string sServant="TestApp.HelloServer.HelloObj";
        	   TestApp::HelloPrx prx = comm->stringToProxy<TestApp::HelloPrx>(sServant);
        	   tars::Int32 iRet = prx->test();
        	   string sRsp;
        	   prx->testHello(sReq,sRsp);

			
        	   TLOGDEBUG    (__FILE__ << "|" << __LINE__ <<"Test Before Dyeing after call other function"  <<endl);
        	   DLOG        <<__FILE__ << "|" << __LINE__ <<"D/Test Before Dyeing after call other function"<<endl;
        	   FDLOG("T_D")<<__FILE__ << "|" << __LINE__ <<"F/Test Before Dyeing after call other function"<<endl;
        	}
		
        	{
        	   //The dye log object has beed destructed, the dye function is invalid, and you can't see the dye log in the future.
        	   TLOGDEBUG    (__FILE__ << "|" << __LINE__ <<"~Dyeing"<<endl);
        	   DLOG        <<__FILE__ << "|" << __LINE__ <<"D/~Dyeing"<<endl;
        	   FDLOG("T_D")<<__FILE__ << "|" << __LINE__ <<"F/~Dyeing"<<endl;
        	}
        }
        catch(exception &ex)
        {
             cerr << "ex:" << ex.what() << endl;
        }
        catch(...)
        {
             cerr << "unknown exception." << endl;
        }
    }
    catch(exception& e)
    {
        cerr << "exception:" << e.what() << endl;
    }
    catch (...)
    {
        cerr << "unknown exception." << endl;
    }
    sleep(10); //Waiting for thread written log asynchronously to synchronize log data to logserver
    return 0;
}


in tars_dyeing.dyeing_roll_20161227.log

//This log is rolling log printed by the server using TLOGDEBUG.
10.120.129.226|TestApp.HelloServer|2016-12-27 11:30:47|7670|DEBUG|main.cpp|37Test Before Dyeing before call other function
//This log is rolling log printed by the server using TLOGDEBUG.
10.120.129.226|TestApp.HelloServer|2016-12-27 11:30:47|7670|DEBUG|main.cpp|59Test Before Dyeing after call other function

in tars_dyeing.dyeing_day_20161227.log

//Rolling log using DLOG
10.120.129.226|TestApp.HelloServer|2016-12-27 11:30:47|main.cpp|38D/Test Before Dyeing before call other function
10.120.129.226|TestApp.HelloServer|2016-12-27 11:30:47|main.cpp|60D/Test Before Dyeing after call other function
//Rolling log using FLOG
10.120.129.226|TestApp.HelloServer|2016-12-27 11:30:47|main.cpp|39F/Test Before Dyeing before call other function
10.120.129.226|TestApp.HelloServer|2016-12-27 11:30:47|main.cpp|61F/Test Before Dyeing after call other function

4.4. The Usage of passive open

Interface definition to be dyed

interface HelloRouteObj
{
    int testHello(routekey string sInput, out string sOutput);
};

Can be dyed by the frame command, the command format of the dye is:

tars.setdyeing dyeingKey dyeingServant [dyeingInterface]

This three parameters are the user number(the value corresponding to routekey), the remote object name, and the interface name(optional) Assume that the remote object of the above interface is TestApp.HelloServer.HelloObj testHello You can issue commands through the management platform: tars.setdyeing 123456 TestApp.HelloServer.HelloObj testHello

When a request with sInput is 123456 is sent to the service, it is not only having the normal log output, but also having the local system log printed:

/usr/local/app/tars/app_log/tars_dyeing/dyeing_20161227.log TestApp.HelloServer|2016-12-27 15:38:49|11454|DEBUG|HelloImp::testHello sReq:123456

local log one by one:

/usr/local/app/tars/app_log/tars_dyeing/dyeing_20161227.log TestApp.HelloServer|2016-12-27 15:38:49|11454|DEBUG|HelloImp::testHello sReq:123456

The remote log will be printed to the machine where tarslog is located: remote system log: /usr/local/app/tars/remote_app_log/tars_dyeing/dyeing/tars_dyeing.dyeing_roll_20161227.log

remote log by day: /usr/local/app/tars/remote_app_log/tars_dyeing/dyeing/tars_dyeing.dyeing_day_20161227.log

The first field in the log is the service name of the dye request processing process, and the related logs of other subsequent services are also printed to the same file, and the logs of different services are distinguished by the first field.

5. tars protocol packet size

Currently, the tars protocol limits the size of data packets.

The communicator (client) has no limit on the size of the delivered packet, and there is a limit on the received packet. The default is 10000000 bytes (close to 10M).

The server has no restrictions on the delivered packets , and has a size limit on the received packets. The default is 100000000 bytes (close to 100M).

5.1. Modify the client receiving packet size

Modify the size of the packet by modifying the tars_set_protocol of ServantProxy.

ProxyProtocol prot;
prot.responseFunc = ProxyProtocol::tarsResponseLen<100000000>;
prot.requestFunc  = ProxyProtocol::tarsRequest;
ccserverPrx -> tars_set_protocol(prot);

100000000 represents the size of the limit, in bytes.

ccserverPrx is globally unique, just set it once.

In order to write codes conveniently, it is recommended to set it once in the initialization of the business thread .

First call stringToProxy and then set it.

prot.requestFunc = ProxyProtocol::tarsRequest //Must exist, the default is not this function.

If it is called in tup mode. Set len

prot.responseFunc = ProxyProtocol:: tupResponseLen<100000000>;

5.2. Modify the server to receive the packet size

Modify the packet size by setting the form of ServantProtocol.

addServantProtocol(ServerConfig::Application + "." + ServerConfig::ServerName + ".BObj",AppProtocol::parseLenLen<100000000>);

It is recommended to set it in the initialize of the server and set it after addServant.

6. Tars defined return code

//Define the return code given by the TARS service
const int TARSSERVERSUCCESS       = 0;    //Server-side processing succeeded
const int TARSSERVERDECODEERR     = -1;   //Server-side decoding exception
const int TARSSERVERENCODEERR     = -2;   //Server-side encoding exception
const int TARSSERVERNOFUNCERR     = -3;   //There is no such function on the server side
const int TARSSERVERNOSERVANTERR  = -4;   //The server does not have the Servant object
const int TARSSERVERRESETGRID     = -5;   // server grayscale state is inconsistent
const int TARSSERVERQUEUETIMEOUT  = -6;   //server queue exceeds limit
const int TARSASYNCCALLTIMEOUT    = -7;   // Asynchronous call timeout
const int TARSINVOKETIMEOUT       = -7;   //call timeout
const int TARSPROXYCONNECTERR     = -8;   //proxy link exception
const int TARSSERVEROVERLOAD      = -9;   //Server overload, exceeding queue length
const int TARSADAPTERNULL         = -10;  //The client routing is empty, the service does not exist or all services are down.
const int TARSINVOKEBYINVALIDESET = -11;  //The client calls the set rule illegally
const int TARSCLIENTDECODEERR     = -12;  //Client decoding exception
const int TARSSERVERUNKNOWNERR    = -99;  //The server is in an abnormal position

7. Business Configuration

The Tars service framework provides the ability to pull the configuration of a service from tarsconfig to a local directory.

The method of use is very simple. In the initialize of the Server, call addConfig to pull the configuration file.

Take HelloServer as an example:

HelloServer::initialize()
{
      //Increase the object
      addServant<HelloImp>(ServerConfig::Application+"."+ ServerConfig::ServerName + ".HelloObj");

      //pull the configuration file
      addConfig("HelloServer.conf");
}

Description:

Note:

8. Log

Tars provides a number of macro for logging the system’s rolling logs and daily logs. They are thread-safe and can be used at will.

8.1. TLOGXXX tutorial

TLOGXXX is used to record rolling logs, mainly used for debugging services. XXX includes four levels of INFO/DEBUG/WARN/ERROR, the meanings are as follows.

Instructtions for use:

TLOGINFO("test" << endl);
TLOGDEBUG("test" << endl);
TLOGWARN("test" << endl);
TLOGERROR("test" << endl);

Directions:

TLOGXXX is a macro, which is defined as follows:

#define LOG             (TarsRollLogger::getInstance()->logger())

#define LOGMSG(level,msg...) do{if(LOG->IsNeedLog(level)) LOG->log(level)<<msg;}while(0)

#define TLOGINFO(msg...)  LOGMSG(TarsRollLogger::INFO_LOG,msg)
#define TLOGDEBUG(msg...) LOGMSG(TarsRollLogger::DEBUG_LOG,msg)
#define TLOGWARN(msg...)  LOGMSG(TarsRollLogger::WARN_LOG,msg)
#define TLOGERROR(msg...) LOGMSG(TarsRollLogger::ERROR_LOG,msg)

The return type of TarsRollLogger::getInstance()->logger() is TC_RollLogger*,so you can set the LOG through it. For example:

Set LOG to info level:

TarsRollLogger::getInstance()->logger()->setLogLevel(TC_RollLogger::INFO_LOG);

The LOG log is asynchronous by default, but can also be set to sync:

TarsRollLogger::getInstance()->sync(true);

You can also use LOG in any place where you use ostream. For example:

ostream &print(ostream &os);

It can also be used like this:

print(LOG->debug());

8.2. DLOG/FDLOG

Daily log, mainly used to record important business information:

8.3. Code example

CommunicatorPtr c = new Communicator();

string logObj = "tars.tarslog.LogObj@tcp -h 127.0.0.1 -p 20500";

//Initialize local scrolling logs
TarsRollLogger::getInstance()->setLogInfo("Test", "TestServer", "./");

//Initialize time log
TarsTimeLogger::getInstance()->setLogInfo(c, logObj , "Test", "TestServer", "./");

//If it is a Tars service, the above part of the code is not needed, the framework has been completed automatically.

//The default daily log does't need to be uploaded to the server
TarsTimeLogger::getInstance()->enableRemote("", false);

//The default daily log is scrolled by minute
TarsTimeLogger::getInstance()->initFormat("", "%Y%m%d%H%M");

//Set abc2 not to be uploaded to the server
TarsTimeLogger::getInstance()->enableRemote("abc2", false);

//set abc2 scrolls by hour
TarsTimeLogger::getInstance()->initFormat("abc2", "%Y%m%d%H");

//Set abc3 to not be recorded locally
TarsTimeLogger::getInstance()->enableLocal("abc3", false);

int i = 100000;
while(i--)
{
    //as same as last one
    TLOGDEBUG(i << endl);
    
    //error level
    TLOGERROR(i << endl);

    DLOG << i << endl;
    
    FDLOG("abc1") << i << endl;
    FDLOG("abc2") << i << endl;
    FDLOG("abc3") << i << endl;

    if(i % 1000 == 0)
    {
        cout << i << endl;
    }
    usleep(10);
}

9. Service management

The Tars server framework supports dynamic receiving commands to handle related business logic, such as dynamic update configuration.

Two macros are defined in the framework:

By using these two macros, you can register the command processing interface. When a command is sent to the service through the web management platform, the registered interface is called.

There are two types of processing interfaces that are usually registered: global processing interface and object-based processing interface.

The following uses HelloServer as an example to describe how to use commands.

9.1. Global processing interface

The so-called global processing interface means that the processing interface is service-dependent, not related to any objects such as HelloImp.

Assume that HelloServer needs to add a function to set a FDLOG configuration file to not be uploaded to the remote tarslog. This processing is independent of the HelloImp object, so it is a global change. The processing steps are as follows:

Add a handler to the HelloServer to do this. Note that the function must be declared in this way:

bool HelloServer::procDLOG(const string& command, const string& params, string& result)
{
    TarsTimeLogger::getInstance()->enableLocal(params, false);
    return false;
}

Register this function in HelloServer::initialize():

void HelloServer::initialize()
{
    addServant(...);
    addConfig();

	//Registration handler:
    TARS_ADD_ADMIN_CMD_NORMAL("DISABLEDLOG", HelloServer::procDLOG);
}

Instruction:

9.2. Object-based processing interface

The so-called object-based processing interface means that the command interface is for an object in the service.

For example, for the HelloImp object, if a command is sent to the service, the command interface of HelloImp in each thread will be executed once, and the process of executing these interfaces and the interface of executing HelloImp are mutually exclusive (ie, thread-safe)

Assuming that HelloImp has the member variable string _hello, you need to send a command to notify each HelloImp object to change _hello to a custom value. The steps are as follows:

Add handlers to HelloImp:

bool HelloImp::procHello(const string& command, const string& params, string& result)
{
    _hello = params;
    return false;
}

Register the function in the initialization of HelloImp:

void HelloImp::initialize()
{
    //Registration handler:
    TARS_ADD_ADMIN_CMD_NORMAL("SETHELLO", HelloImp::procHello);
}

After completing the above operation, send the “SETHELLO test” command on the web page, and _hello is assigned the value “test”.

9.3. Send management command

How to send management commands: Publish a TARS service to the platform through the web management platform, and then send commands through the management platform.

TARS currently has eight commands built in:

10. Statistical reporting

Reporting statistics information is the logic of reporting the time-consuming information and other information to tarsstat inside the Tars framework. No user development is required. After the relevant information is correctly set during program initialization, it can be automatically reported inside the framework (including the client and the server).

After the client call the reporting interface, it is temporarily stored in memory. When it reaches a certain time point, it is reported to the tarsstat service (the default is once reporting 1 minute). We call the time gap between the two reporting time points as a statistical interval, and perform the operations such as accumulating and comparing the same key in a statistical interval. The sample code is as follows:

//Initialize the communicator
CommunicatorPtr pcomm = new Communicator();
//Initialize the tarsregistry service address
pcomm->setProperty("locator", "tars.tarsregistry.QueryObj@tcp -h xxx.xxx.xxx.xx -p xxxx"
//Initialize the stat service
pcomm->setProperty("stat", "tars.tarsstat.StatObj");
//Set the reporting interval
pcomm->setProperty("report-interval", "1000");
//Set the report main call name
pcomm->setProperty("modulename", "Test.TestServer_Client");

Description:

11. Anormaly reporting

For better monitoring, the TARS framework supports reporting abnormal situdation directly to tarsnotify in the program and can be viewed on the WEB management page.

The framework provides three macros to report different kinds of exceptions:

// Report ordinary information
TARS_NOTIFY_NORMAL(info) 
// Report warning message
TARS_NOTIFY_WARN(info) 
// Report error message
TARS_NOTIFY_ERROR(info)

Info is a string, which can directly report the string to tarsnotify. The reported string can be seen on the page, subsequently, we can alarm according to the reported information.

12. Attribute Statistics

In order to facilitate business statistics, the TARS framework also supports the display of information on the web management platform.

The types of statistics currently supported include the following:

The sample code is as follows:

// Initialize the communicator
Communicator _comm;
// Initialize the property service address
_comm.setProperty("property", "tars.tarsproperty.PropertyObj@ tcp -h xxx.xxx.xxx.xxx -p xxxx");

// Initialize the distribution data range
vector<int> v;
v.push_back(10);
v.push_back(30);
v.push_back(50);
v.push_back(80);
v.push_back(100);

// Create test1 attribute, this attribute uses all the statistics above, and pay attention to the initialization of distrv
PropertyReportPtr srp = _comm.getStatReport()->createPropertyReport("test1", 
PropertyReport::sum(), 
PropertyReport::avg(), 
PropertyReport::count(),
PropertyReport::max(), 
PropertyReport::min(),
PropertyReport::distr(v));

// Report data, property only supports int type data reporting
int iValue = 0;
for ( int i = 0; i < 10000000; i++ )
{
        sleep(1);
     srp->report(rand() % 100 );
}

Description:

13. Tars call chain

Tars supports reporting rpc call path information to zipkin to help locate network call problems.
About zipkin instructions can be found at [https://zipkin.io/] (https://zipkin.io/).

Compile and Running dependencies:
The Tars call chain uses opentracking and zipkin-opentracking libraries. Since the zipkin-opentracking library depend on the libcurl library, you need to install libcurl. In addition, the compiler needs to support c++11.
Download link:
opentracing-cpp
zipkin-cpp-opentracing

Instructions for use:
1) Compile and install
The feature of tars call chain is controlled by the compile option _USE_OPENTRACKING, which is off by default.
Open mode: Execute export _USE_OPENTRACKING=1 in the shell before compile tars. After the framework is compiled, modify the 'servant/makefile/makefile.tars' file and add a line to the front before install tars:
_USE_OPENTRACKING=1
to indicates that the framework has opened the call chain switch. In addition, opentraking, curl, zipkin_opentracing install path need to be manually modified to the correct path (The default path is /usr/local/lib).

2) Configuration
When using the tars call chain function, you need to specify the address of the zipkin in the program configuration file. Sample configuration is as follows:

<tars>
    <application>
        …
        <client>
            …
            collector_host=127.0.0.1
            collector_port=9411
            sample_rate=1.0
        </client>
    </application>
</tars>

The collector_host and collector_port are mandatory (if the configuration is not configured, the call chain function will not be available), sample_rate is optional (the default value is 1.0, the interval is 0.0~1.0, which is used to specify the rate of call chain information that reported to the zipkin collector)