The basic components of the microservice open source framework TARS

The basic components of the microservice open source framework TARS

View Image

Author herman

Lead

This article is derived from one of herman's articles " Basic Components of Goose Factory Open Source Framework TARS ". The relevant code has been updated according to the latest version of the TARS open source community.

In the TARS open source framework library, more common components are implemented in C++, and these components are generally placed in a unified manner.

util
Folders can also be used freely at the application layer. If you want to do well, you must first sharpen your tools. Therefore, it is necessary to understand these tool components to better use and improve efficiency. Next, this article will analyze the following TarsCpp components:

Thread safe queue: TC_ThreadQueue

First look at the frame pair

TC_ThreadQueue
The use of the class is as follows:

typedef TC_ThreadQueue < tagRecvData * , the deque < tagRecvData * > > recv_queue ; //receive queue typedef TC_ThreadQueue < tagSendData * , the deque < tagSendData * > > send_queue ; //transmit queue

TC_ThreadQueue
The implementation of is relatively simple. In the implementation of the network layer of TARS, this class can be found to be more important, because the network packets received from the framework will be added to the cache queue, and then multiple business threads
ServantHandle
Will call
waitForRecvQueue
Get network packets from the queue, and then call
dispatch
Call the processing function corresponding to the protocol message, first look at the framework pair
TC_ThreadQueue
The realization:

/** * @brief thread safe queue */ Template < typename T , typename D = the deque < T > > class TC_ThreadQueue { public : TC_ThreadQueue ( ) : _size ( 0 ) { } ; public : typedef D queue_type ; /** * @brief gets data from the head, no data throws an exception * * @param t * @return bool: true, got data, false, no data */ T front ( ) ; /** * @brief gets data from the header, and waits if there is no data. * * @param t * @param millsecond (valid only when wait = true) blocking waiting time (ms) * 0 means no blocking * -1 wait forever * @param wait, whether to wait * @return bool: true, got data, false, no data */ BOOL pop_front ( T & T , size_t millsecond = 0 , BOOL the wait = to true ) ; . . . . . . }

TC_ThreadQueue
Used the C++11 standard library
<mutex>
with
<condition_variable>
Used to implement thread lock and wait, as follows, look at the member functions of the queue:
push_front
Add data to the front of the queue,

template < typename T , typename D > void TC_ThreadQueue < T , D > :: push_front ( const T & t , bool notify ) { if ( notify ) { std :: unique_lock < std :: mutex > lock ( _mutex ) ; _cond . notify_one ( ) ; _queue . push_front ( t ) ; ++ _size ; } else { std :: lock_guard < std :: mutex > lock ( _mutex ) ; _queue . push_front ( t ) ; ++ _size ; } }

Call as above

push_front
When the function is called
std::unique_lock<std::mutex> lock(_mutex)
Lock to avoid data conflict between the network layer receiving data and the business layer taking the same queue.
_cond.notify_one()
Notify waiting for a thread on the lock to wake up, and the lock must be locked before calling this function, because there is data coming, for example, a thread in the network layer needs to fetch packets and distribute them.

Look at another member function

pop_front
, Get data from the head, and wait if there is no data.
millisecond
Blocking waiting time (ms)

  • 0
    Means not blocking
  • -1
    Wait forever
template < typename T , typename D > bool TC_ThreadQueue < T , D > :: pop_front ( T & t , size_t millsecond , bool wait ) { if ( wait ) { std :: unique_lock < std :: mutex > lock ( _mutex ) ; IF ( _queue . empty ( ) ) { IF ( millsecond == 0 ) { return to false ; } IF ( millsecond == ( size_t ) - . 1 ) { _cond . the wait ( Lock ) ; } the else { //timeout IF ( _cond . wait_for ( lock , std :: chrono:: milliseconds ( millsecond ) ) == std :: cv_status :: timeout ) { return false ; } } } if ( _queue . empty ( ) ) { return false ; } t = _queue . front ( ) ; _queue . pop_front ( ) ; assert ( _size > 0 ) ; - _size ; return true ; } else { std :: lock_guard < std :: mutex > lock ( _mutex ) ; if ( _queue . empty ( ) ) { return false ; } t = _queue . front ( ) ; _queue . pop_front ( ) ; assert ( _size > 0 ) ; - _size ; return true ; } }

BindAdapter::waitForRecvQueue
The function is called
pop_front
Function, used to wait for the receiving queue, the function prototype is as follows:

bool TC_EpollServer :: BindAdapter :: waitForRecvQueue ( uint32_t handleIndex , shared_ptr < RecvContext > & data ) { bool bRet = getRecvQueue ( handleIndex ) . pop_front ( data ) ; if ( ! bRet ) { return bRet ; } - _iRecvBufferSize ; return bRet ; }

Here

BindAdapter::waitForRecvQueue
Used by the business thread to process the business packet after the adapter that is waiting for the server to monitor receives the network packet, the incoming
handleIndex
Represents the index of the receiving queue and obtains the corresponding
_rbuffer
.

Ordinary thread lock: TC_ThreadLock

TC_ThreadLock
The definition of the class is as follows

typedef TC_Monitor < TC_ThreadMutex , TC_ThreadCond > TC_ThreadLock ;

TC_Monitor
Thread lock monitoring template class. Usually thread locks are used through this class instead of directly
TC_ThreadMutex
,
TC_ThreadRecMutex
.

Class definition

template <class T, class P> class TC_Monitor
Need to pass in two template parameters,
TC_Monitor
Include the following member variables:

mutable int _nnotify ; //number of locks mutable P _cond ; //condition variable T _mutex ; //mutex lock /** * @brief defines the lock control object */ Typedef TC_LockT < TC_Monitor < T , P > > Lock ; typedef TC_TryLockT < TC_Monitor < T , P > > TryLock ;

The first parameter

TC_ThreadMutex
Represents thread lock: the same thread cannot be locked repeatedly, including member variables

mutable std :: mutex _mutex

For further reading, here

tc_thread_mutex.h
Also includes another loop lock class
TC_ThreadRecMutex
, That is, a thread can add multiple locks, defined as follows:

//Defined in typedef TC_Monitor < TC_ThreadRecMutex , TC_ThreadCond > TC_ThreadRecLock in tc_monitor.h ;

2.parameter

TC_ThreadCond
Represents the thread signal condition class: all locks can wait for a signal to occur on it, including thread condition member variables:

mutable std :: condition_variable_any _cond

Combined with actual usage scenarios,

TC_Monitor::timedWait()
Will call
TC_ThreadCond
Object of
timedWait
Function, called next
chrono
Library
milliseconds
TC_ThreadCond::signal()
To realize sending a signal, a thread waiting on the condition will wake up.

TC_LockT
Class definition:
template <typename T> class TC_LockT
The lock template class, used in conjunction with other specific locks, is locked when constructed, and unlocked when sufficient.

TC_LockT
Constructor, pass in the mutex to initialize the member variables
_mutex
,
TC_LockT
Constructor implementation:

TC_LockT ( const T & mutex ) : _mutex ( mutex ) { _mutex . Lock ( ) ; _acquired = true ; }

You can see here

TC_Monitor
Defined
typedef TC_LockT<TC_Monitor<T, P>> Lock
,Here
Lock
The type of template parameter is
TC_Monitor
class.

The actual usage scenarios are as follows:

Lock lock ( * this ) ;

TC_LockT
The constructor, passing in parameters
this
for
TC_Monitor
Subclass object of
TC_LockT
The constructor call
_mutex.lock()
; Is actually called
TC_Monitor
Object of
lock
function,
TC_Monitor
of
lock
Function implementation:

void lock ( ) const { _mutex . lock ( ) ; _nnotify = 0 ; }

Here

_mutex
for
TC_ThreadMutex
Object, further called
TC_ThreadRecMutex::lock()
Member functions are implemented as follows:

void TC_ThreadMutex :: lock ( ) const { _mutex . lock ( ) ; }

Then the lock stack variable defined above is called when the function exits

TC_LockT
The destructor: the implementation is as follows:

virtual ~ TC_LockT ( ) { if ( _acquired ) { _mutex . unlock ( ) ; //here will call the unlock function of TC_Monitor } }

TC_Monitor
of
unlock
Function implementation:

void unlock ( ) const { notifyImpl ( _nnotify ) ; _mutex . unlock ( ) ; // Unlock in the C++ standard library <mutex> will be called here }

Call here

notifyImpl
Function because
TC_Monitor
The class can not only implement a simple mutex lock function, but also a condition variable Condition function, where
notifyImpl
Is implemented as

void notifyImpl ( int nnotify ) const { if ( nnotify != 0 ) { if ( nnotify == - 1 ) { _cond . broadcast ( ) ; return ; } else { while ( nnotify > 0 ) { _cond . signal ( ) ; - - nnotify ; } } } }

Thread base class: TC_Thread

Still the same, let's take a look at the actual use of thread base classes in the project. In actual project use, we are

TC_Thread
I encapsulated it again and realized a
BasicThread
Class, see below
BasicThread
Definition:

class BasicThread : public tars :: TC_Thread , public tars :: TC_ThreadLock { . . . void terminate ( ) { _bTerm = true ; { Lock lock ( * this ) ; notifyAll ( ) ; } getThreadControl ( ) . join ( ) ; } }

BasicThread
Class, inherited
TC_Thread
with
TC_ThreadLock
,among them
TC_ThreadLock
The second point has already been explained, so here is the focus
TC_Thread
The use of classes,
TC_Thread
Definition

class TC_Thread : public TC_Runable { . . . /** * Use the C++11 standard thread library std::thread, the constructor passes the parameter threadEntry thread function, * Return TC_ThreadControl(_th), where _th is the std::thread object */ TC_ThreadControl start ( ) ; static void threadEntry ( TC_Thread * pThread ) ; //Static function, thread entry virtual void run ( ) = 0 ; . . . }

Next look at the thread control class

TC_ThreadControl
Definition:

class TC_ThreadControl { . . . Explicit TC_ThreadControl ( STD :: Thread * TH ) ; //configuration, std :: thread passing objects void join ( ) ; //call join ( ) of std::thread to block the current thread until the end of another thread static void SLEEP ( ) ; //call std :: this_thread :: sleep function thread will be suspended . . . }

Next look

TC_Runable
Definition:

class TC_Runable { public : virtual ~ TC_Runable ( ) { } ; virtual void run ( ) = 0 ; //The run pure virtual function is defined } ;

Finally, look at the use of thread classes in actual projects

class AntiSdkSyncThread : public BasicThread //This is equivalent to inheriting two classes of TC_Thread and TC_ThreadLock { void run ( ) //Implement the pure virtual function of the base class { Lock lock ( * this ) ; timedWait ( 10 * 1000 ) ; (Interval execution time, realizing thread timing execution function) IF ( NULL ! = g_busi_interf ) { Int32 RET = g_busi_interf - > proc_ ( ) ; function need to periodically perform// } } }

Defined

AntiSdkSyncThread g_antiSdkSyncThread;
Class, then it needs to be executed when the thread is started
g_antiSdkSyncThread.start();
Will naturally create threads, and
threadEntry
Thread function will call
pThread->run()
Polymorphic function, called when the process exits
g_antiSdkSyncThread.terminate();
.

Smart pointer class: TC_AutoPtr

The smart pointer here can be placed in the container, and the thread-safe smart pointer, the CPP11 standard library

auto_ptr
It can’t be placed in a container. It seems to have been eliminated. Currently, most of them use the CPP11 standard library.
shared_ptr
, But the compiler needs to support CPP11.

TC_HandleBase
The definition of the smart pointer base class is as follows, all classes that need smart pointers need to inherit from this object, which uses the C++11 standard library
<atomic>
Perform atomic counting.

class UTIL_DLL_API TC_HandleBase { public : /** * @brief copy * * @return TC_HandleBase& */ TC_HandleBase & operator = ( const TC_HandleBase & ) { return * this ; } /** * @brief increase count */ Void incRef ( ) { ++ _atomic ; } /** * @brief Decrease count */ Void decRef ( ) { IF ( ( - _atomic ) == 0 && ! _BNoDelete ) { _bNoDelete = to true ; Delete the this ; } } /** * @brief Get the count. * * @return int count value */ int getRef ( ) const { return _atomic ; } /** * @brief settings are not automatically released. * * @param b Whether to delete automatically, true or false */ void setNoDelete ( bool b ) { _bNoDelete = b ; } protected : /** * @brief constructor */ TC_HandleBase ( ) : _atomic ( 0 ) , _bNoDelete ( to false ) { } /** * @brief copy construction */ TC_HandleBase ( const TC_HandleBase & ) : _atomic ( 0 ) , _bNoDelete ( to false ) { } /** * @brief destruction */ virtual ~ TC_HandleBase ( ) { } protected : STD :: Atomic < int > _atomic ; //reference count BOOL _bNoDelete ; //whether to automatically delete } ;

Next look

TC_AutoPtr
The smart pointer template class is a thread-safe smart pointer that can be placed in a container. The smart pointer is implemented by reference counting. Its constructor and destructor are defined as follows:

template < typename T > class TC_AutoPtr { TC_AutoPtr ( T * p = 0 ) { _ptr = p ; IF ( _ptr ) { _ptr - > incRef ( ) ; //constructor reference counting plus 1 } } . . . ~ TC_AutoPtr ( ) { IF ( _ptr ) { _ptr - > decRef ( ) ; //Save reference counting destructor 1 } } }

Example: Use in actual combat projects

struct ConnStruct : public TC_HandleBase { . . . } typedef TC_AutoPtr < ConnStruct > ConnStructPtr ;

TC_AutoPtr
Copy construction call
_ptr->incRef();
Here
ptr
for
ConnStruct
,
ConnStruct
Inherited from
TC_HandleBase
, Which is equivalent to calling
TC_HandleBaseT<int>::incRef() {++_atomic;}

The reference count atomic operation is incremented by 1, and the destructuring reference count atomic operation is decremented by 1. When the reference count is reduced to 0, it is determined whether to trigger delete according to whether the set switch is to be deleted.

Example: This is a typical example of TARS using asynchronous rpc callbacks, here the callback class uses smart pointers

//Define the callback function smart pointer, where the SessionCallback parent class inherits from TC_HandleBase typedef TC_AutoPtr < SessionCallback > SessionCallbackPtr ; //Create the callback class SessionCallbackPtr, and pass in the initialization parameters uin gameid, etc.; SessionCallbackPtr cb = new SessionCallback ( iUin , iGameId , iSeqID , iCmd , sSessionID , theServant , current , cs , this ) ; //Asynchronously call the sessionserver remote interface getSessionPrx ( ) - > async_getSession ( cb , iUin , iGameId ) ;

The interface returns complete, callback

SessionCallback::callback_getSession(tars::Int32 ret, const MGComm::SessionValue& retValue)
Function, receive
sessionserver
Return of the interface
SessionValue
structure.

because

SessionCallbackPtr
Smart pointers are used, so the business does not need to manually release the front
new
from
SessionCallbackPtr
, It is quite convenient.

MySQL operation class: TC_Mysql

The mysql operation class encapsulated by TC_Mysql is not thread-safe, and there can be better function encapsulation for insert/update to prevent SQL injection

How to use:

TC_Mysql mysql ; //Initialize mysql, do not link when init, and automatically establish a link when requested; //The database can be empty; //The port defaults to 3306 mysql . Init ( "192.168.1.2" , "pc" , "pc@sn " , "db_tars_demo" ) ;

Usually used:

void init(const TC_DBConf& tcDBConf);
Initialize the database directly. E.g:
stDirectMysql.init(_stZoneDirectDBConf);

Take a look

TC_DBConf
Definition

struct TC_DBConf { string _host ; string _user ; string _password ; string _database ; string _charset ; int _port ; int _flag ; //client identification TC_DBConf ( ) : _port ( 0 ) , _flag ( 0 ) { } /** * @brief Read database configuration. * * @param mpParam stores the map of the database configuration * dbhost: host address * dbuser: username * dbpass: password * dbname: database name * dbport: port */ void loadFromMap ( const map < string , string > & mpParam ) { map < string , string > mpTmp = mpParam ; _host = mpTmp [ "dbhost" ] ; _user = mpTmp [ "dbuser" ] ; _password = mpTmp [ "dbpass" ] ; _database = mpTmp [ "dbname" ] ; _charset = mpTmp [ "charset" ] ; _port = atoi ( mpTmp [ "dbport" ] . c_str () ) ; _flag = 0 ; if ( mpTmp [ "dbport" ] == "" ) { _port = 3306 ; } } } ;
//Further look at the use of data acquisition TC_Mysql :: MysqlData data ; data = mysql . QueryRecord ( "select * from t_app_users" ) ; for ( size_t i = 0 ; i < data . Size ( ) ; i ++ ) { //If the ID field does not exist, throw an exception cout << data [ i ] [ "ID" ] << endl ; }

Use the queried mysql data

MysqlData
Encapsulation

class MySQLdata { . . . Vector < Map < String , String > > & Data ( ) ; . . . }
//insert data, specified data type: string or numeric, string for automatically escapes Map < String , pair < TC_Mysql :: the FT , String > > m ; m [ "ID" ] = the make_pair ( TC_Mysql :: DB_INT , "2334" ) ; m [ "USERID" ] = make_pair ( TC_Mysql :: DB_STR , "abcttt" ) ; m [ "APP" ] = make_pair (TC_Mysql :: DB_STR , "abcapbbp" ) ; m [ "LASTTIME" ] = make_pair ( TC_Mysql :: DB_INT , "now()" ) ; mysql . replaceRecord ( "t_user_logs" , m ) ;

Network components

The entire TARS core provides a very complete network framework, including RPC functions, here only a few commonly used network components are introduced.

TC_Socket: encapsulates the basic method of socket

Provide socket operation class; support tcp/udp socket; support local domain socket.

The next layer of TARS encapsulation

TC_TCPClient
with
TC_UDPClient
Two classes are used to actually operate tcp and udp applications.

How to use:

For example: tcp client

TC_TCPClient stRouterClient ; stRouterClient . the init ( sIP , iPort , iTimeOut ) ; //ip and port where incoming calls then a message transceiver sendRecv RET int32 = stRouterClient . Sendrecv ( Request . The c_str ( ) , Request . Length ( ) , recvbuf , iRecvLen ) ;

Note that when using multiple threads, you cannot send/recv multiple threads at the same time. Be careful of stringing packets.

TC_Epoller

Provides the operation class of network epoll, the default is ET mode, when the state changes, it is notified, and provides basic operations such as add, mod, del, and wait.

TC_ClientSocket: client socket related operation base class

Provide key member functions

init(const string &sIp, int iPort, int iTimeout)
, Incoming IP port and timeout

TC_TCPClient
Inherited from
TC_ClientSocket
Provide member functions:

  • sendRecv
    (Send to the server, and return from the server no more than iRecvLen bytes)
  • sendRecvBySep
    (Send to the server, and wait for the server until the end character, including the end character)

example:

stRouterClient . init ( sIP , iPort , iTimeOut ) ; iRecvLen size_t = the sizeof ( recvbuf ) - . 1 ; Int32 RET = stRouterClient . sendrecv ( Request . the c_str ( ) , Request . length ( ) , recvbuf , iRecvLen ) ;

For the same reason

TC_UDPClient
Implement UDP client.

Command parsing class: TC_Option

  1. Command parsing class;
  2. Usually used to parse command line parameters;
  3. Only supports double-parameter form
  4. analysis
    main
    The input parameters of, support the following forms of parameters:
./main.exe --name=value --param1 param2 param3
TC_Option op ; //Parse the command line op . Decode ( argc , argv ) ; //Get paired parameters, that is, get all parameter pairs represented by-- map < string , string > mp = op . GetMulti ( ) ; //Represents non-parameters: param2, param3 vector < string > d = op . GetSingle ( ) ;

If value and param have spaces or

-
, Enclose it in quotation marks.

Configuration file class: TC_Config

  1. Configuration file parsing class (compatible with wbl mode);
  2. Support parsing configuration files from string;
  3. Support generating configuration files;
  4. Parsing error throws an exception;
  5. Use [] to get the configuration, if there is no configuration, an exception will be thrown;
  6. Use get to get the configuration, and return empty if it does not exist;
  7. Reading configuration files is thread-safe, and functions such as insert fields are not thread-safe

example:

TC_Config config ; config . ParseFile ( ServerConfig :: BasePath + ServerConfig :: ServerName + ".conf" ) ; stTmpGameServerConfig . IGameId = TC_Common :: strto < UInt32 > ( config [ "/Main/<GameId>" ] ) ;

Sample configuration file

< Main > GameId = 3001 ZoneId = 102 AsyncThreadCheckInterval = 1000 ... </ Main >

use

get
Method example: If the configuration cannot be read, the default value is returned
sDefault
, Which is in the example below
20000000

stTmpGameServerConfig . iMaxRegNum = TC_Common :: strto < Int32 > ( config . get ( "/Main/<MaxRegNum>" , "20000000" ) ) ;

General functor class: TC_Functor

TC_Functor
reference
loki
Library design

  1. The function object call method, that is, for the above several methods, you can add a pair of parentheses on the right side, and put a set of appropriate parameters inside the parentheses to call, for example

    a(p1,p2);

  2. Encapsulate the entire call (including parameters) with a function object. The parameters are passed in when the call object is created, and no parameters are passed in when calling, for example

    A a(p1, p2); a();

Simple and easy-to-use package, see the following usage examples for details:

C function call

void TestFunction3 ( const string & s , int i ) { cout << "TestFunction3('" << s << "','" << i << "')" << endl ; } //Using function pointer construction Object TC_Functor < void , TL :: TLMaker < const string & , int > :: Result > cmd3 ( TestFunction3 ) ; string s3 ( "s3" ) ; cmd3 ( s3 , 10 ) ;

C function calls are encapsulated with wrapper:

//Call package, pass in parameters when constructing TC_Functor < void , TL :: TLMaker < const string & , int > :: Result > :: wrapper_type fwrapper3 ( cmd3 , s3 , 10 ) ; fwrapper3 ( ) ; //The parameters have been passed in when constructing, and they are not used when calling Passed parameters

Description:

  • void
    : The return value of the function
  • TL::TLMaker<const string&, int>::Result
    : Represents the parameter type

For the encapsulation of the call, pay attention to the reference type, and ensure the existence of the referenced object when calling.

C++ points to the call of class member function

struct TestMember { void mem3 ( const string & s , int i ) { cout << "TestMember::mem3(" << s << "," << i << ") called" << endl ; } } TC_Functor < void , TL :: TLMaker < const string & , int > :: Result > cmd3 ( &tm , & TestMember :: mem3 ) ; cmd3 ( "a" , 33 ) ;

The call to the class member function is encapsulated with wrapper:

TC_Functor < void , TL :: TLMaker < const string & , int > :: Result > :: wrapper_type fwrapper3 ( cmd3 , "a" , 10 ) ; fwrapper3 ( ) ;

Practical example: Registering a protocol parser

When the service initializes initialize, it will generally call

addServantProtocol ( sRouterObj , AppProtocol :: parseStream < 0 , uint16_t , false > , iHeaderLen ) ;

Set here

BindAdapter
Protocol analysis function
protocol_functor _pf
for
parseStream
Function, as follows:

/** * @param T * @param offset * @param netorder * @param in * @param out * @return int */ template < size_t offset , typename T , bool netorder > static TC_NetWorkBuffer :: PACKET_TYPE parseStream ( TC_NetWorkBuffer & in , vector < char > & out ) { size_t len = offset + sizeof ( T ) ; if ( in . getBufferLength ( ) < len ) { return TC_NetWorkBuffer :: PACKET_LESS ; } string header ; in . getHeader ( len , header ) ; assert ( header . size ( ) == len ) ; T iHeaderLen = 0 ; :: memcpy ( & iHeaderLen , header . c_str ( ) + offset , sizeof ( T ) ) ; if ( netorder ) { iHeaderLen = net2host < T > ( iHeaderLen ) ; } //Length protection if ( iHeaderLen < ( T ) ( len ) || ( uint32_t ) iHeaderLen > TARS_NET_MAX_PACKAGE_SIZE ) { return TC_NetWorkBuffer :: PACKET_ERR ; } if ( in . getBufferLength ( ) < ( uint32_t ) iHeaderLen ) { return TC_NetWorkBuffer :: PACKET_LESS ; } in . getHeader ( iHeaderLen , out ) ; assert ( out . size ( ) == iHeaderLen ) ; in . moveHeader ( iHeaderLen ) ; return TC_NetWorkBuffer :: PACKET_FULL ; }

After registering the parsing function, the network layer receives the packet and calls

parseProtocol
function

int TC_EpollServer :: Connection :: parseProtocol ( TC_NetWorkBuffer & rbuf ) { . . . TC_NetWorkBuffer :: packet_type B = _pBindAdapter - > getProtocol ( ) ( rbuf , RO ) ; //this callback function front-set protocol analysis protocol that enables parsing . . . }

hash algorithm

util/tc_hash_fun.h
Contains the implementation of the hash algorithm, using
hash_new
, You can hash the input byte stream to get a fairly uniform hash value, the usage is as follows

# include "util/tc_hash_fun.h" # include <iterator> # include <iostream> # include <sys/time.h> using namespace tars ; using namespace std ; int main ( int argc , char * * argv [ ] ) { unsigned int i = tars :: hash_new < string > ( ) ( "abcd" ) ; cout << i << endl ; return 0 ; }

Exception class: TC_Exception

class TC_Exception : public exception { /** * @brief Constructor, provides a constructor that can be passed in errno, * Error information directly obtained when an exception is thrown * * @param buffer abnormal warning information * @param err error code, you can use strerror to get error information */ TC_Exception ( const string & buffer , int err ) ; }

summary

This article introduces and analyzes the common basic components implemented in C++ in the TARS framework, deepens the understanding of these tool-like basic components, reduces the problems that arise in the process of using these components, and improves development efficiency.

TARS can quickly build a system and automatically generate code while taking into account ease of use and high performance, helping developers and enterprises to quickly build their own stable and reliable distributed applications in the form of microservices, so that developers only focus on business logic. Improve operational efficiency. The characteristics of multilingualism, agile R&D, high availability and efficient operation make TARS an enterprise-level product.

TARS microservices help you with digital transformation, welcome to visit:

TARS official website: https://TarsCloud.org

TARS source code: https://github.com/TarsCloud

Get the "TARS Official Training eBook": https://wj.qq.com/s2/6570357/3adb/

Or scan the code to get:

View Image


Reference : https://blog.csdn.net/TARSFoundation/article/details/108342084