>> DNS load balancing is quite common so this should work quite easily.
> It isn't easy. getaddrinfo() returns the addresses in a prio order and we
go
> through the addresses in that order. How are you suggesting we change this
> without overriding the users' /etc/gai.conf, RFC 3484 and whatever more
that
> need to be taken into account? Of course, involving c-ares complicates
matters
> slightly more too... I'm open for suggestions and patches on how to
improve this.

True, does not look that easy. Thanks for the explanation! My example had
also one bug with share handles. The code should have sharehandles[i] and
not sharehandles[0]. Thus assigning separate share handles seems to still
work also at least with c-ares.

Probably I keep my current solution, which is to resolve the hostname at
the beginning and assign IPs manually using CURLOPT_RESOLVE. So basically
having my own DNS cache layer in the end. An updated multi-app.c
demonstrating this approach is attached for those who might be interested.

Cheers,
Seppo
<http://daniel.haxx.se>
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2011, Daniel Stenberg, <dan...@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at http://curl.haxx.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ***************************************************************************/
/* This is an example application source code using the multi interface
 * and testing access to host that is using DNS load balancing.
 *
 * Compile:
 * g++ -lcurl -lcares multi-app.c
 *
 * Usage:
 * ./a.out | sort | uniq -c
 */

#include <stdio.h>
#include <string.h>

/* somewhat unix-specific */
#include <sys/time.h>
#include <unistd.h>

/* curl stuff */
#include <curl/curl.h>
#include <ares_version.h>
#include <ares.h>

#define HANDLECOUNT 100   /* Number of simultaneous transfers */

int main(void) {
  CURL   *handles[HANDLECOUNT];
  CURLSH *sharehandles[HANDLECOUNT];
  CURLM  *multi_handle;

  int still_running; /* keep number of running handles */
  int i;

  curl_global_init(CURL_GLOBAL_ALL);

  // Print version info
  curl_version_info_data *d = curl_version_info(CURLVERSION_NOW);
  printf("Curl version: %s\n", d->version);
  if (d->features & CURL_VERSION_ASYNCHDNS) {
    printf("ares enabled\n");
  } else {
    printf("ares NOT enabled\n");
  }
  printf("C-ares: %s\n", ares_version(NULL));

  CURLMsg *msg;   /* for picking up messages with the transfer status */
  int msgs_left;  /* how many messages are left */

  /* init a multi stack */
  multi_handle = curl_multi_init();

  /* Allocate one CURL handle per transfer */
  for (i = 0; i < HANDLECOUNT; i++) {
    handles[i] = curl_easy_init();
  }

//#define USE_SEPARATE_SHARE_HANDLES
#ifdef USE_SEPARATE_SHARE_HANDLES
  for (i = 0; i < HANDLECOUNT; i++) {
    sharehandles[i] = curl_share_init();
    curl_share_setopt(sharehandles[i], CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
    curl_easy_setopt(handles[i], CURLOPT_SHARE, sharehandles[i]);
  }
#endif

  // Force some example IPs (name resolve would be here)
  struct curl_slist *host1 = NULL;
  host1 = curl_slist_append(NULL, "google.com:80:173.194.40.74");
  CURLSH *host1sh = curl_share_init();
  curl_share_setopt(host1sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
  struct curl_slist *host2 = NULL;
  host2 = curl_slist_append(NULL,  "google.com:80:173.194.40.75");
  CURLSH *host2sh = curl_share_init();
  curl_share_setopt(host2sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);

  for (i = 0; i < HANDLECOUNT; i++) {
//#define USE_PROXY
#ifdef USE_PROXY
    curl_easy_setopt(handles[i], CURLOPT_PROXYPORT, 80L);
    if (i % 2 == 1) {
      curl_easy_setopt(handles[i], CURLOPT_PROXY, "173.194.40.72");
    } else {
      curl_easy_setopt(handles[i], CURLOPT_PROXY, "173.194.40.73");
    }
#endif
#define USE_RESOLVE
#ifdef USE_RESOLVE
    if (i % 2 == 1) {
      curl_easy_setopt(handles[i], CURLOPT_SHARE, host1sh);
      curl_easy_setopt(handles[i], CURLOPT_RESOLVE, host1);
    } else {
      curl_easy_setopt(handles[i], CURLOPT_SHARE, host2sh);
      curl_easy_setopt(handles[i], CURLOPT_RESOLVE, host2);
    }
#endif
    curl_easy_setopt(handles[i], CURLOPT_UPLOAD, 0);
    curl_easy_setopt(handles[i], CURLOPT_NOBODY, 1);
    curl_easy_setopt(handles[i], CURLOPT_NOSIGNAL, 1L);
    //curl_easy_setopt(handles[i], CURLOPT_DNS_CACHE_TIMEOUT, 0);
    //curl_easy_setopt(handles[i], CURLOPT_DNS_USE_GLOBAL_CACHE, 0);
    //curl_easy_setopt(handles[i], CURLOPT_FORBID_REUSE, 1L);
    //curl_easy_setopt(handles[i], CURLOPT_VERBOSE, 1L);
    curl_easy_setopt(handles[i], CURLOPT_URL,
                     "http://google.com/";);
  }

  /* add the individual transfers */
  for (i = 0; i < HANDLECOUNT; i++) {
    if (i == 1) {
      // With libcurl 7.37.0 this was needed for DNS balancing not to work
      curl_multi_perform(multi_handle, &still_running);
      sleep(1);
    }
    curl_multi_add_handle(multi_handle, handles[i]);
  }

  //curl_multi_perform(multi_handle, &still_running);

  do {
    struct timeval timeout;
    int rc; /* select() return code */

    fd_set fdread;
    fd_set fdwrite;
    fd_set fdexcep;
    int maxfd = -1;

    long curl_timeo = -1;

    FD_ZERO(&fdread);
    FD_ZERO(&fdwrite);
    FD_ZERO(&fdexcep);

    /* set a suitable timeout to play around with */
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;

    curl_multi_timeout(multi_handle, &curl_timeo);
    if (curl_timeo >= 0) {
      timeout.tv_sec = curl_timeo / 1000;
      if (timeout.tv_sec > 1)
        timeout.tv_sec = 1;
      else
        timeout.tv_usec = (curl_timeo % 1000) * 1000;
    }

    /* get file descriptors from the transfers */
    curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);

    /* In a real-world program you OF COURSE check the return code of the
       function calls.  On success, the value of maxfd is guaranteed to be
       greater or equal than -1.  We call select(maxfd + 1, ...), specially in
       case of (maxfd == -1), we call select(0, ...), which is basically equal
       to sleep. */
    rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);

    switch (rc) {
    case -1:
      /* select error */
      break;
    case 0: /* timeout */
    default: /* action */
      curl_multi_perform(multi_handle, &still_running);
      break;
    }
  } while (still_running);

  /* See how the transfers went */
  while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
    if (msg->msg == CURLMSG_DONE) {
      int idx, found = 0;

      /* Find out which handle this message is about */
      for (idx = 0; idx < HANDLECOUNT; idx++) {
        found = (msg->easy_handle == handles[idx]);
        if (found)
          break;
      }

      if (found) {
        char *server_ip;
        curl_easy_getinfo(handles[idx], CURLINFO_PRIMARY_IP, &server_ip);
        printf("Transfer done to IP: %s\n", server_ip);
      }
    }
  }

  curl_multi_cleanup(multi_handle);

  /* Free the CURL handles */
  for (i = 0; i < HANDLECOUNT; i++) {
    curl_easy_cleanup(handles[i]);
  }

#ifdef USE_SEPARATE_SHARE_HANDLES
  /* Free share handles */
  for (i = 0; i < HANDLECOUNT; i++) {
    curl_share_cleanup(sharehandles[i]);
  }
#endif

  curl_slist_free_all(host1);
  curl_slist_free_all(host2);

  return 0;
}
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html

Reply via email to