>> 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