- Carlo Contavalli added support for the glibc "rotate" option, as documented

in man resolv.conf:

  causes round robin selection of nameservers from among those listed.  This
  has the effect of spreading the query load among all listed servers, rather
  than having all clients try the first listed server first every time.

  You can enable it with ARES_OPT_ROTATE
This commit is contained in:
Daniel Stenberg 2008-11-01 18:35:19 +00:00
Родитель 128418b214
Коммит 3e3d10824f
6 изменённых файлов: 62 добавлений и 27 удалений

Просмотреть файл

@ -1,5 +1,15 @@
Changelog for the c-ares project Changelog for the c-ares project
* Nov 1 2008 (Daniel Stenberg)
- Carlo Contavalli added support for the glibc "rotate" option, as documented
in man resolv.conf:
causes round robin selection of nameservers from among those listed. This
has the effect of spreading the query load among all listed servers, rather
than having all clients try the first listed server first every time.
You can enable it with ARES_OPT_ROTATE
* Oct 21 2008 (Yang Tse) * Oct 21 2008 (Yang Tse)
Charles Hardin added handling of EINPROGRESS for UDP connects. Charles Hardin added handling of EINPROGRESS for UDP connects.

Просмотреть файл

@ -114,6 +114,7 @@ extern "C" {
#define ARES_OPT_SOCK_SNDBUF (1 << 11) #define ARES_OPT_SOCK_SNDBUF (1 << 11)
#define ARES_OPT_SOCK_RCVBUF (1 << 12) #define ARES_OPT_SOCK_RCVBUF (1 << 12)
#define ARES_OPT_TIMEOUTMS (1 << 13) #define ARES_OPT_TIMEOUTMS (1 << 13)
#define ARES_OPT_ROTATE (1 << 14)
/* Nameinfo flag values */ /* Nameinfo flag values */
#define ARES_NI_NOFQDN (1 << 0) #define ARES_NI_NOFQDN (1 << 0)
@ -184,6 +185,7 @@ struct ares_options {
int timeout; /* in seconds or milliseconds, depending on options */ int timeout; /* in seconds or milliseconds, depending on options */
int tries; int tries;
int ndots; int ndots;
int rotate;
unsigned short udp_port; unsigned short udp_port;
unsigned short tcp_port; unsigned short tcp_port;
int socket_send_buffer_size; int socket_send_buffer_size;

Просмотреть файл

@ -144,6 +144,7 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
channel->timeout = -1; channel->timeout = -1;
channel->tries = -1; channel->tries = -1;
channel->ndots = -1; channel->ndots = -1;
channel->rotate = -1;
channel->udp_port = -1; channel->udp_port = -1;
channel->tcp_port = -1; channel->tcp_port = -1;
channel->socket_send_buffer_size = -1; channel->socket_send_buffer_size = -1;
@ -159,6 +160,7 @@ int ares_init_options(ares_channel *channelptr, struct ares_options *options,
channel->sock_state_cb = NULL; channel->sock_state_cb = NULL;
channel->sock_state_cb_data = NULL; channel->sock_state_cb_data = NULL;
channel->last_server = 0;
channel->last_timeout_processed = (time_t)now.tv_sec; channel->last_timeout_processed = (time_t)now.tv_sec;
/* Initialize our lists of queries */ /* Initialize our lists of queries */
@ -352,6 +354,8 @@ static int init_by_options(ares_channel channel,
channel->tries = options->tries; channel->tries = options->tries;
if ((optmask & ARES_OPT_NDOTS) && channel->ndots == -1) if ((optmask & ARES_OPT_NDOTS) && channel->ndots == -1)
channel->ndots = options->ndots; channel->ndots = options->ndots;
if ((optmask & ARES_OPT_ROTATE) && channel->rotate == -1)
channel->rotate = options->rotate;
if ((optmask & ARES_OPT_UDP_PORT) && channel->udp_port == -1) if ((optmask & ARES_OPT_UDP_PORT) && channel->udp_port == -1)
channel->udp_port = options->udp_port; channel->udp_port = options->udp_port;
if ((optmask & ARES_OPT_TCP_PORT) && channel->tcp_port == -1) if ((optmask & ARES_OPT_TCP_PORT) && channel->tcp_port == -1)
@ -932,6 +936,8 @@ static int init_by_defaults(ares_channel channel)
channel->tries = DEFAULT_TRIES; channel->tries = DEFAULT_TRIES;
if (channel->ndots == -1) if (channel->ndots == -1)
channel->ndots = 1; channel->ndots = 1;
if (channel->rotate == -1)
channel->rotate = 0;
if (channel->udp_port == -1) if (channel->udp_port == -1)
channel->udp_port = htons(NAMESERVER_PORT); channel->udp_port = htons(NAMESERVER_PORT);
if (channel->tcp_port == -1) if (channel->tcp_port == -1)
@ -1302,6 +1308,9 @@ static int set_options(ares_channel channel, const char *str)
val = try_option(p, q, "retry:"); val = try_option(p, q, "retry:");
if (val && channel->tries == -1) if (val && channel->tries == -1)
channel->tries = atoi(val); channel->tries = atoi(val);
val = try_option(p, q, "rotate");
if (val && channel->rotate == -1)
channel->rotate = 1;
p = q; p = q;
while (ISSPACE(*p)) while (ISSPACE(*p))
p++; p++;
@ -1374,7 +1383,7 @@ static char *try_config(char *s, const char *opt)
static const char *try_option(const char *p, const char *q, const char *opt) static const char *try_option(const char *p, const char *q, const char *opt)
{ {
size_t len = strlen(opt); size_t len = strlen(opt);
return ((size_t)(q - p) > len && !strncmp(p, opt, len)) ? &p[len] : NULL; return ((size_t)(q - p) >= len && !strncmp(p, opt, len)) ? &p[len] : NULL;
} }
#ifndef WIN32 #ifndef WIN32

Просмотреть файл

@ -195,8 +195,8 @@ struct query {
void *arg; void *arg;
/* Query status */ /* Query status */
int try; int try; /* Number of times we tried this query already. */
int server; int server; /* Server this query has last been sent to. */
struct query_server_info *server_info; /* per-server state */ struct query_server_info *server_info; /* per-server state */
int using_tcp; int using_tcp;
int error_status; int error_status;
@ -242,6 +242,7 @@ struct ares_channeldata {
int timeout; /* in milliseconds */ int timeout; /* in milliseconds */
int tries; int tries;
int ndots; int ndots;
int rotate; /* if true, all servers specified are used */
int udp_port; int udp_port;
int tcp_port; int tcp_port;
int socket_send_buffer_size; int socket_send_buffer_size;
@ -268,6 +269,9 @@ struct ares_channeldata {
just to draw the line somewhere. */ just to draw the line somewhere. */
time_t last_timeout_processed; time_t last_timeout_processed;
/* Last server we sent a query to. */
int last_server;
/* Circular, doubly-linked list of queries, bucketed various ways.... */ /* Circular, doubly-linked list of queries, bucketed various ways.... */
/* All active queries in a single list: */ /* All active queries in a single list: */
struct list_node all_queries; struct list_node all_queries;

Просмотреть файл

@ -670,30 +670,33 @@ static void skip_server(ares_channel channel, struct query *query,
static void next_server(ares_channel channel, struct query *query, static void next_server(ares_channel channel, struct query *query,
struct timeval *now) struct timeval *now)
{ {
/* Advance to the next server or try. */ /* We need to try each server channel->tries times. We have channel->nservers
query->server++; * servers to try. In total, we need to do channel->nservers * channel->tries
for (; query->try < channel->tries; query->try++) * attempts. Use query->try to remember how many times we already attempted
* this query. Use modular arithmetic to find the next server to try. */
while (++(query->try) < (channel->nservers * channel->tries))
{ {
for (; query->server < channel->nservers; query->server++) struct server_state *server;
/* Move on to the next server. */
query->server = (query->server + 1) % channel->nservers;
server = &channel->servers[query->server];
/* We don't want to use this server if (1) we decided this
* connection is broken, and thus about to be closed, (2)
* we've decided to skip this server because of earlier
* errors we encountered, or (3) we already sent this query
* over this exact connection.
*/
if (!server->is_broken &&
!query->server_info[query->server].skip_server &&
!(query->using_tcp &&
(query->server_info[query->server].tcp_connection_generation ==
server->tcp_connection_generation)))
{ {
struct server_state *server = &channel->servers[query->server]; ares__send_query(channel, query, now);
/* We don't want to use this server if (1) we decided this return;
* connection is broken, and thus about to be closed, (2)
* we've decided to skip this server because of earlier
* errors we encountered, or (3) we already sent this query
* over this exact connection.
*/
if (!server->is_broken &&
!query->server_info[query->server].skip_server &&
!(query->using_tcp &&
(query->server_info[query->server].tcp_connection_generation ==
server->tcp_connection_generation)))
{
ares__send_query(channel, query, now);
return;
}
} }
query->server = 0;
/* You might think that with TCP we only need one try. However, /* You might think that with TCP we only need one try. However,
* even when using TCP, servers can time-out our connection just * even when using TCP, servers can time-out our connection just
@ -702,6 +705,8 @@ static void next_server(ares_channel channel, struct query *query,
* tickle a bug that drops our request. * tickle a bug that drops our request.
*/ */
} }
/* If we are here, all attempts to perform query failed. */
end_query(channel, query, query->error_status, NULL, 0); end_query(channel, query, query->error_status, NULL, 0);
} }
@ -775,8 +780,7 @@ void ares__send_query(ares_channel channel, struct query *query,
} }
query->timeout = *now; query->timeout = *now;
ares__timeadd(&query->timeout, ares__timeadd(&query->timeout,
(query->try == 0) ? channel->timeout channel->timeout << (query->try / channel->nservers));
: channel->timeout << query->try / channel->nservers);
/* Keep track of queries bucketed by timeout, so we can process /* Keep track of queries bucketed by timeout, so we can process
* timeout events quickly. * timeout events quickly.
*/ */

Просмотреть файл

@ -95,7 +95,13 @@ void ares_send(ares_channel channel, const unsigned char *qbuf, int qlen,
/* Initialize query status. */ /* Initialize query status. */
query->try = 0; query->try = 0;
query->server = 0;
/* Choose the server to send the query to. If rotation is enabled, keep track
* of the next server we want to use. */
query->server = channel->last_server;
if (channel->rotate == 1)
channel->last_server = (channel->last_server + 1) % channel->nservers;
for (i = 0; i < channel->nservers; i++) for (i = 0; i < channel->nservers; i++)
{ {
query->server_info[i].skip_server = 0; query->server_info[i].skip_server = 0;