Add option to select an IP randomly instead of using the order provided by getaddrinfo, which is proximity based.

This commit is contained in:
aljar 2020-11-13 17:10:30 +01:00
parent f75c848250
commit 4570496547
4 changed files with 43 additions and 10 deletions

View File

@ -192,8 +192,9 @@ public:
* Constructor * Constructor
* @param handler User implemented handler object * @param handler User implemented handler object
* @param hostname The address to connect to * @param hostname The address to connect to
* @param random Randomly select one of the IP addresses that belong to the hostname
*/ */
TcpConnection(TcpHandler *handler, const Address &address); TcpConnection(TcpHandler *handler, const Address &address, bool random = false);
/** /**
* No copying * No copying

View File

@ -1,12 +1,17 @@
/** /**
* AddressInfo.h * AddressInfo.h
* *
* Utility wrapper arround "getAddressInfo()" * Utility wrapper around "getAddressInfo()"
* *
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
* @copyright 2015 Copernica BV * @copyright 2015 Copernica BV
*/ */
/**
* Dependencies
*/
#include <sys/time.h>
/** /**
* Include guard * Include guard
*/ */
@ -35,8 +40,9 @@ public:
* Constructor * Constructor
* @param hostname * @param hostname
* @param port * @param port
* @param randomOrder
*/ */
AddressInfo(const char *hostname, uint16_t port = 5672) AddressInfo(const char *hostname, uint16_t port = 5672, bool randomOrder = false)
{ {
// store portnumber in buffer // store portnumber in buffer
auto portnumber = std::to_string(port); auto portnumber = std::to_string(port);
@ -63,6 +69,25 @@ public:
// store in vector // store in vector
_v.push_back(current); _v.push_back(current);
} }
// Do we want to have a random order of the addresses?
// This may be useful since getaddrinfo is sorting the addresses on proximity
// (e.g. https://lists.debian.org/debian-glibc/2007/09/msg00347.html),
// which may break loadbalancing..
if (randomOrder)
{
// We need to seed the random number generator. Normally time is taken
// for this. Since we want to have randomness within a second we use
// more precision via timeval
struct timeval time;
gettimeofday(&time, nullptr);
// We seed with the precision of miliseconds.
srand((time.tv_sec * 1000) + (time.tv_usec / 1000));
// shuffle the vector.
std::random_shuffle(_v.begin(), _v.end());
}
} }
/** /**
@ -99,4 +124,3 @@ public:
* End of namespace * End of namespace
*/ */
} }

View File

@ -23,10 +23,11 @@ namespace AMQP {
* Constructor * Constructor
* @param handler User implemented handler object * @param handler User implemented handler object
* @param hostname The address to connect to * @param hostname The address to connect to
* @param random Randomly select one of the IP addresses that belong to the hostname
*/ */
TcpConnection::TcpConnection(TcpHandler *handler, const Address &address) : TcpConnection::TcpConnection(TcpHandler *handler, const Address &address, bool random) :
_handler(handler), _handler(handler),
_state(new TcpResolver(this, address.hostname(), address.port(), address.secure(), address.option("connectTimeout", 5))), _state(new TcpResolver(this, address.hostname(), address.port(), address.secure(), address.option("connectTimeout", 5), random)),
_connection(this, address.login(), address.vhost()) _connection(this, address.login(), address.vhost())
{ {
// tell the handler // tell the handler
@ -286,4 +287,3 @@ void TcpConnection::onLost(TcpState *state)
* End of namespace * End of namespace
*/ */
} }

View File

@ -85,6 +85,12 @@ private:
*/ */
std::thread _thread; std::thread _thread;
/**
* Do we want to have random ordering
* @var bool
*/
bool _random;
/** /**
* Run the thread * Run the thread
@ -98,7 +104,7 @@ private:
if (_secure && !OpenSSL::valid()) throw std::runtime_error("Secure connection cannot be established: libssl.so cannot be loaded"); if (_secure && !OpenSSL::valid()) throw std::runtime_error("Secure connection cannot be established: libssl.so cannot be loaded");
// get address info // get address info
AddressInfo addresses(_hostname.data(), _port); AddressInfo addresses(_hostname.data(), _port, _random);
// the pollfd structure, needed for poll() // the pollfd structure, needed for poll()
pollfd fd; pollfd fd;
@ -188,13 +194,15 @@ public:
* @param portnumber The portnumber for the lookup * @param portnumber The portnumber for the lookup
* @param secure Do we need a secure tls connection when ready? * @param secure Do we need a secure tls connection when ready?
* @param timeout timeout per connection attempt * @param timeout timeout per connection attempt
* @param random Do we want to have random oredering of the address of the host to connect to
*/ */
TcpResolver(TcpParent *parent, std::string hostname, uint16_t port, bool secure, int timeout) : TcpResolver(TcpParent *parent, std::string hostname, uint16_t port, bool secure, int timeout, bool random) :
TcpExtState(parent), TcpExtState(parent),
_hostname(std::move(hostname)), _hostname(std::move(hostname)),
_secure(secure), _secure(secure),
_port(port), _port(port),
_timeout(timeout) _timeout(timeout),
_random(random)
{ {
// tell the event loop to monitor the filedescriptor of the pipe // tell the event loop to monitor the filedescriptor of the pipe
parent->onIdle(this, _pipe.in(), readable); parent->onIdle(this, _pipe.in(), readable);