mirror of
https://github.com/valitydev/thrift.git
synced 2024-11-07 10:48:51 +00:00
3b5dacba44
Client: All Patch: Anatol Pomozov This closes #281
321 lines
9.7 KiB
C++
321 lines
9.7 KiB
C++
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
#ifndef _FACEBOOK_THRIFT_SERVER_TCLIENTINFO_H_
|
|
#define _FACEBOOK_THRIFT_SERVER_TCLIENTINFO_H_ 1
|
|
|
|
// for inet_ntop --
|
|
#include <arpa/inet.h>
|
|
#include <thrift/server/TServer.h>
|
|
#include <thrift/transport/TSocket.h>
|
|
#include <thrift/concurrency/Mutex.h>
|
|
|
|
namespace apache { namespace thrift { namespace server {
|
|
|
|
using namespace apache::thrift;
|
|
using namespace apache::thrift::transport;
|
|
using namespace apache::thrift::concurrency;
|
|
using boost::shared_ptr;
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
/**
|
|
* StableVector -- a minimal vector class where growth is automatic and
|
|
* vector elements never move as the vector grows. Allocates new space
|
|
* as needed, but does not copy old values.
|
|
*
|
|
* A level vector stores a list of storage vectors containing the actual
|
|
* elements. Levels are added as needed, doubling in size each time.
|
|
* Locking is only done when a level is added. Access is amortized
|
|
* constant time.
|
|
*/
|
|
template <typename T>
|
|
class StableVector {
|
|
/// The initial allocation as an exponent of 2
|
|
static const uint32_t kInitialSizePowOf2 = 10;
|
|
/// The initial allocation size
|
|
static const uint32_t kInitialVectorSize = 1 << kInitialSizePowOf2;
|
|
/// This bound is guaranteed not to be exceeded on 64-bit archs
|
|
static const int kMaxLevels = 64;
|
|
|
|
/// Values are kept in one or more of these
|
|
typedef vector<T> Vect;
|
|
/// One or more value vectors are kept in one of these
|
|
typedef vector<Vect*> LevelVector;
|
|
|
|
Mutex mutex_;
|
|
/// current size
|
|
size_t size_;
|
|
_Atomic_word vectLvl_;
|
|
LevelVector vects_;
|
|
|
|
public:
|
|
/**
|
|
* Constructor -- initialize the level vector and allocate the
|
|
* initial storage vector
|
|
*/
|
|
StableVector()
|
|
: size_(0)
|
|
, vectLvl_(0) {
|
|
vects_.reserve(kMaxLevels);
|
|
Vect* storageVector(new Vect(1 << kInitialSizePowOf2));
|
|
vects_.push_back(storageVector);
|
|
}
|
|
|
|
private:
|
|
/**
|
|
* make sure the requested number of storage levels have been allocated.
|
|
*/
|
|
void expand(uint32_t level) {
|
|
// we need the guard to insure that we only allocate once.
|
|
Guard g(mutex_);
|
|
while (level > vectLvl_) {
|
|
Vect* levelVect(new Vect(1 << (vectLvl_ + kInitialSizePowOf2)));
|
|
vects_.push_back(levelVect);
|
|
// we need to make sure this is done after levelVect is inserted
|
|
// (what we want is effectively a memory barrier here).
|
|
__gnu_cxx::__atomic_add(&vectLvl_, 1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given an index, determine which level and element of that level is
|
|
* required. Grows if needed.
|
|
*/
|
|
void which(uint32_t n, uint32_t* vno, uint32_t* idx) {
|
|
if (n >= size_) {
|
|
size_ = n + 1;
|
|
}
|
|
if (n < kInitialVectorSize) {
|
|
*idx = n;
|
|
*vno = 0;
|
|
} else {
|
|
uint32_t upper = n >> kInitialSizePowOf2;
|
|
*vno = CHAR_BIT*sizeof(upper) - __builtin_clz(upper);
|
|
*idx = n - (1 << (*vno + kInitialSizePowOf2 - 1));
|
|
if (*vno > vectLvl_) {
|
|
expand(*vno);
|
|
}
|
|
}
|
|
}
|
|
|
|
public:
|
|
/**
|
|
* Given an index, return a reference to that element, perhaps after
|
|
* allocating additional space.
|
|
*
|
|
* @param n a positive integer
|
|
*/
|
|
T& operator[](uint32_t n) {
|
|
uint32_t vno;
|
|
uint32_t idx;
|
|
which(n, &vno, &idx);
|
|
return (*vects_[vno])[idx];
|
|
}
|
|
|
|
/**
|
|
* Return the present size of the vector.
|
|
*/
|
|
size_t size() const { return size_; }
|
|
};
|
|
|
|
|
|
/**
|
|
* This class embodies the representation of a single connection during
|
|
* processing. We'll keep one of these per file descriptor in TClientInfo.
|
|
*/
|
|
class TClientInfoConnection {
|
|
public:
|
|
const static int kNameLen = 32;
|
|
|
|
private:
|
|
typedef union IPAddrUnion {
|
|
sockaddr_in ipv4;
|
|
sockaddr_in6 ipv6;
|
|
};
|
|
|
|
char call_[kNameLen]; ///< The name of the thrift call
|
|
IPAddrUnion addr_; ///< The client's IP address
|
|
timespec time_; ///< Time processing started
|
|
uint64_t ncalls_; ///< # of calls processed
|
|
|
|
public:
|
|
/**
|
|
* Constructor; insure that no client address or thrift call name is
|
|
* represented.
|
|
*/
|
|
TClientInfoConnection();
|
|
|
|
/**
|
|
* A connection has been made; record its address. Since this is the
|
|
* first we'll know of a connection we start the timer here as well.
|
|
*/
|
|
void recordAddr(const sockaddr* addr);
|
|
|
|
/**
|
|
* Mark the address as empty/unknown.
|
|
*/
|
|
void eraseAddr();
|
|
|
|
/**
|
|
* Return a string representing the present address, or NULL if none.
|
|
* Copies the string into the buffer provided.
|
|
*/
|
|
const char* getAddr(char* buf, int len) const;
|
|
|
|
/**
|
|
* A call has been made on this connection; record its name. Since this is
|
|
* called for every thrift call processed, we also do our call count here.
|
|
*/
|
|
void recordCall(const char* name);
|
|
|
|
/**
|
|
* Invoked when processing has ended to clear the call name.
|
|
*/
|
|
void eraseCall();
|
|
|
|
/**
|
|
* Return as string the thrift call either currently being processed or
|
|
* most recently processed if the connection is still open for additional
|
|
* calls. Returns NULL if a call hasn't been made yet or processing
|
|
* has ended.
|
|
*/
|
|
const char* getCall() const;
|
|
|
|
/**
|
|
* Get the timespec for the start of this connection (specifically, when
|
|
* recordAddr() was first called).
|
|
*/
|
|
void getTime(timespec* time) const;
|
|
|
|
/**
|
|
* Return the number of calls made on this connection.
|
|
*/
|
|
uint64_t getNCalls() const;
|
|
|
|
private:
|
|
void initTime();
|
|
};
|
|
|
|
|
|
/**
|
|
* Store for info about a server's clients -- specifically, the client's IP
|
|
* address and the call it is executing. This information is indexed by
|
|
* socket file descriptor and in the present implementation is updated
|
|
* asynchronously, so it may only approximate reality.
|
|
*/
|
|
class TClientInfo {
|
|
private:
|
|
StableVector<TClientInfoConnection> info_;
|
|
|
|
public:
|
|
/**
|
|
* Return the info object for a given file descriptor. If "grow" is true
|
|
* extend the info vector if required (such as for a file descriptor not seen
|
|
* before). If "grow" is false and the info vector isn't large enough,
|
|
* or if "fd" is negative, return NULL.
|
|
*/
|
|
TClientInfoConnection* getConnection(int fd, bool grow);
|
|
|
|
size_t size() const;
|
|
};
|
|
|
|
/**
|
|
* This derivation of TServerEventHandler encapsulates the main status vector
|
|
* and provides context to the server's processing loop via overrides.
|
|
* Together with TClientInfoCallHandler (derived from TProcessorEventHandler)
|
|
* it integrates client info collection into the server.
|
|
*/
|
|
class TClientInfoServerHandler : public TServerEventHandler {
|
|
private:
|
|
TClientInfo clientInfo_;
|
|
|
|
public:
|
|
/**
|
|
* One of these is constructed for each open connection/descriptor and links
|
|
* to both the status vector (clientInfo_) and that descriptor's entry
|
|
* within it.
|
|
*/
|
|
struct Connect {
|
|
TClientInfo* clientInfo_;
|
|
TClientInfoConnection* callInfo_;
|
|
|
|
explicit Connect(TClientInfo* clientInfo)
|
|
: clientInfo_(clientInfo)
|
|
, callInfo_(NULL) {
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Generate processor context; we don't know what descriptor we belong to
|
|
* yet -- we'll get hooked up in contextProcess().
|
|
*/
|
|
void* createContext(boost::shared_ptr<TProtocol> input,
|
|
boost::shared_ptr<TProtocol> output);
|
|
|
|
/**
|
|
* Mark our slot as unused and delete the context created in createContext().
|
|
*/
|
|
void deleteContext(void* processorContext,
|
|
boost::shared_ptr<TProtocol> input,
|
|
boost::shared_ptr<TProtocol> output);
|
|
|
|
/**
|
|
* Called in the processing loop just before the server invokes the
|
|
* processor itself, on the first call we establish which descriptor
|
|
* we correspond to and set it to that socket's peer IP address. This
|
|
* also has the side effect of initializing call counting and connection
|
|
* timing. We won't know which call we're handling until the handler
|
|
* first gets called in TClientInfoCallHandler::getContext().
|
|
*/
|
|
void processContext(void* processorContext,
|
|
shared_ptr<TTransport> transport);
|
|
|
|
/**
|
|
* Get status report for server in the form of a vector of strings.
|
|
* Each active client appears as one string in the format:
|
|
*
|
|
* FD IPADDR CALLNAME DURATION NCALLS
|
|
*
|
|
* where "FD" is the file descriptor for the client's socket, "IPADDR"
|
|
* is the IP address (as reported by accept()), "CALLNAME" is the
|
|
* current or most recent Thrift function name, "DURATION" is the
|
|
* duration of the connection, while NCALLS is the number of Thrift
|
|
* calls made since the connection was made. A single space separates
|
|
* fields.
|
|
*/
|
|
void getStatsStrings(vector<string>& result);
|
|
};
|
|
|
|
/**
|
|
* This class derives from TProcessorEventHandler to gain access to the
|
|
* function name for the current Thrift call. We need two versions of
|
|
* this -- TClientInfoCallStatsHandler is the other -- since in the latter
|
|
* case we pass through to TFunctionStatHandler to perform Thrift call
|
|
* stats.
|
|
*/
|
|
class TClientInfoCallHandler : public TProcessorEventHandler {
|
|
public:
|
|
virtual void* getContext(const char* fn_name, void* serverContext);
|
|
};
|
|
|
|
} } } // namespace apache::thrift::server
|
|
|
|
#endif // !_FACEBOOK_THRIFT_SERVER_TCLIENTINFO_H_
|