2015-12-29 5 views
-6

Вопрос, с которым я борюсь, заключается в том, как определить в c++ то, что является сервером с самым быстрым соединением для клиента, сделать git клон от или загрузить tarball. Поэтому в основном я хочу выбрать из коллекции известных зеркал, из которых один будет использоваться для загрузки контента.Как правильно определить самый быстрый CDN, зеркало, сервер загрузки в C++

После кода я написал демонстрирует то, что я пытаюсь добиться более ясно, может быть, но я считаю, что это не то, что нужно использовать в производстве :).

Так позволяет сказать, что у меня есть два известных источника зеркала git-1.exmple.com и git-2.example.com и я хочу, чтобы загрузить tag-x.tar.gz от одного, который клиент имеет лучшее подключение к.

CDN.h

#include <iostream> 
#include <cstdio> 
#include <cstring> 
#include <cstdlib> 
#include <netdb.h> 
#include <arpa/inet.h> 
#include <sys/time.h> 
using namespace std; 

class CDN { 
public: 
    long int dl_time; 
    string host; 
    string proto; 
    string path; 
    string dl_speed; 
    double kbs; 
    double mbs; 
    double sec; 
    long int ms; 
    CDN(string, string, string); 
    void get_download_speed(); 
    bool operator < (const CDN&); 
}; 
#endif 

CDN.cpp

#include "CND.h" 
CDN::CDN(string protocol, string hostname, string downloadpath) 
{ 
    proto = protocol; 
    host = hostname; 
    path = downloadpath; 
    dl_time = ms = sec = mbs = kbs = 0; 
    get_download_speed(); 
} 
void CDN::get_download_speed() 
{ 
    struct timeval dl_started; 
    gettimeofday(&dl_started, NULL); 
    long int download_start = ((unsigned long long) dl_started.tv_sec * 1000000) + dl_started.tv_usec; 
    char buffer[256]; 
    char cmd_output[32]; 
    sprintf(buffer,"wget -O /dev/null --tries=1 --timeout=2 --no-dns-cache --no-cache %s://%s/%s 2>&1 | grep -o --color=never \"[0-9.]\\+ [KM]*B/s\"",proto.c_str(),host.c_str(),path.c_str()); 
    fflush(stdout); 
    FILE *p = popen(buffer,"r"); 

    fgets(cmd_output, sizeof(buffer), p); 
    cmd_output[strcspn(cmd_output, "\n")] = 0; 
    pclose(p); 

    dl_speed = string(cmd_output); 
    struct timeval download_ended; 
    gettimeofday(&download_ended, NULL); 
    long int download_end = ((unsigned long long)download_ended.tv_sec * 1000000) + download_ended.tv_usec; 

    size_t output_type_k = dl_speed.find("KB/s"); 
    size_t output_type_m = dl_speed.find("MB/s"); 

    if(output_type_k!=string::npos) { 
     string dl_bytes = dl_speed.substr(0,output_type_k-1); 
     double dl_mb = atof(dl_bytes.c_str())/1000; 
     kbs = atof(dl_bytes.c_str()); 
     mbs = dl_mb; 
    } else if(output_type_m!=string::npos) { 
     string dl_bytes = dl_speed.substr(0,output_type_m-1); 
     double dl_kb = atof(dl_bytes.c_str()) * 1000; 
     kbs = dl_kb; 
     mbs = atof(dl_bytes.c_str()); 
    } else { 
     cout << "Should catch the errors..." << endl; 
    } 
    ms = download_end-download_start; 
    sec = ((float)ms)/CLOCKS_PER_SEC; 
} 
bool CDN::operator < (const CDN& other) 
{ 
    if (dl_time < other.dl_time) 
     return true; 
    else 
     return false; 
} 

main.cpp

#include "CDN.h" 
int main() 
{ 
    cout << "Checking CDN's" << endl; 
    char msg[128]; 
    CDN cdn_1 = CDN("http","git-1.example.com","test.txt"); 
    CDN cdn_2 = CDN("http","git-2.example.com","test.txt"); 
    if(cdn_2 > cdn_1) 
    { 
     sprintf(msg,"Downloading tag-x.tar.gz from %s %s since it's faster than %s %s", 
     cdn_1.host.c_str(),cdn_1.dl_speed.c_str(),cdn_2.host.c_str(),cdn_2.dl_speed.c_str()); 
     cout << msg << endl; 

    } 
    else 
    { 
     sprintf(msg,"Downloading tag-x.tar.gz from %s %s since it's faster than %s %s", 
     cdn_2.host.c_str(),cdn_2.dl_speed.c_str(),cdn_1.host.c_str(),cdn_1.dl_speed.c_str()); 
     cout << msg << endl; 
    } 
    return 0; 
} 

Итак, каковы ваши мысли и как вы подходите к этому. Каковы альтернативы для замены этого wget и достичь такой же чистой, как в C++

EDIT: Как правильно @molbdnilo

меры пинг задержки заостренные, но вы заинтересованы в пропускной способности.

И поэтому я редактировал код, демонстрирующий, чтобы отразить, что, однако вопрос остается тем же

+1

'ping' измеряет задержку, но вы заинтересованы в пропускной способности (соединение 128 kb/s с пингом 5 мс, вероятно, хуже, чем 100 Мбит/с с 50-секундным пингом). Я не думаю, что есть надежный способ предсказать пропускную способность, кроме как загрузить что-то существенное. – molbdnilo

+0

@molbdnilo вы совершенно правы. Меня интересует пропускная способность и код, который я поставил с помощью ping, был просто иллюстрацией того, что я пытаюсь сделать. Вот что этот вопрос означает, что «загружает что-то существенное» только решение. –

+2

@Marko этот вопрос как написано [не в формате, подходящем для переполнения стека] (http://stackoverflow.com/help), пожалуйста, сушите вопрос до конкретной проблемы с кодом или проблемы. – Mgetz

ответ

1

Для начала, пытаясь определить «самый быстрое CDN зеркало» является неточной наукой. Не существует общепринятого определения того, что означает «самый быстрый». Самое большее, на что можно надеяться, - это выбрать разумную эвристику для того, что означает «самый быстрый», а затем измерить эту эвристику точно так, как это может быть при данных обстоятельствах.

В примере кода, по-видимому, выбранная эвристика занимает столько времени, сколько требуется, чтобы загрузить образец файла из каждого зеркала через HTTP.

Это не такой уж плохой выбор, на самом деле. Вы могли бы обоснованно аргументировать, что некоторые другие эвристики могут быть немного лучше, но основной тест того, сколько времени потребуется, чтобы перенести образец файла из каждого потенциального зеркала, я бы подумал, что это очень разумная эвристика.

Большая, большая проблема здесь, я вижу здесь фактическую реализацию этой эвристики. Способ, которым эта попытка - время загрузки образца, - здесь, кажется, не очень надежным, и в итоге он будет измерять целую кучу несвязанных факторов, которые не имеют ничего общего с пропускной способностью сети.

Я вижу, по крайней мере, несколько возможностей здесь, когда внешние факторы, полностью не связанные с пропускной способностью сети, будут затухать измеренные тайминги и сделать их менее надежными, чем они должны быть.

Итак, давайте посмотрим на код и посмотрим, как он пытается измерить латентность сети. Вот мясо его:

sprintf(buffer,"wget -O /dev/null --tries=1 --timeout=2 --no-dns-cache --no-cache %s://%s/%s 2>&1 | grep -o --color=never \"[0-9.]\\+ [KM]*B/s\"",proto.c_str(),host.c_str(),path.c_str()); 
fflush(stdout); 
FILE *p = popen(buffer,"r"); 

fgets(cmd_output, sizeof(buffer), p); 
cmd_output[strcspn(cmd_output, "\n")] = 0; 
pclose(p); 

... и gettimeofday() привыкает к образцу системных часов до и после, чтобы выяснить, как долго это ушло. Хорошо, это здорово. Но что бы это значило?

Это помогает много здесь, чтобы взять чистый лист бумаги и просто записать все, что происходит здесь, как часть() вызова popen, шаг за шагом:

1) Новый дочерний процесс является fork() ред. Ядро операционной системы создает новый дочерний процесс.

2) Новый дочерний процесс exec() s /bin/bash или ваша системная оболочка по умолчанию, проходящая в длинной строке, которая начинается с «wget», а затем куча других параметров, которые вы видите выше.

3) Ядро операционной системы загружает «/ bin/bash» в качестве нового дочернего процесса. Ядро загружает и открывает все и все разделяемые библиотеки, которые обычно необходимо запустить системной оболочке.

4) Процесс инициализации оболочки системы. Он читает файл $HOME/.bashrc и выполняет его, скорее всего, вместе с любыми стандартными файлами инициализации оболочки и сценариями, которые обычно выполняет ваша системная оболочка. Это само по себе может создать множество новых процессов, которые должны быть инициализированы и выполнены до того, как новый процесс системной оболочки фактически обойдется до ...

5) ... разбор команды «wget», которую она первоначально получила как аргумент и exec().

6) Ядро операционной системы теперь загружает «wget» в качестве нового дочернего процесса. Ядро загружает и открывает все и все разделяемые библиотеки, необходимые для процесса wget. Глядя на мой Linux-блок, «wget» загружает не менее 25 отдельных разделяемых библиотек, включая kerberos и ssl-библиотеки. Каждая из этих разделяемых библиотек инициализируется.

7) Команда wget выполняет поиск DNS на хосте, чтобы получить IP-адрес веб-сервера для подключения. Если на локальном DNS-сервере нет кэшированного IP-адреса хоста CDN-зеркала, часто требуется несколько секунд для поиска авторитетных DNS-серверов зеркал DNS CDN, а затем запрашивать их для IP-адреса, прыгать таким образом и таким образом, через межтрубные.

Теперь, один момент ... Кажется, я забыл, что мы пытались сделать здесь ... О, я помню: какое зеркало CDN было «самым быстрым», загрузив образец файла из каждого зеркала, не так ли? Да, это должно быть!

Теперь, что делает вся работа выше, вся эта работа, связана с определением, какое зеркало содержимого является самым быстрым ???

Err ... Не так много, как мне кажется. Теперь ни одно из вышеперечисленных событий не должно быть такими шокирующими новостями. В конце концов, все это описано на странице руководства пользователя popen(). Если вы читаете страницу руководства по папке, это говорит вам, что это ... что он делает. Запускает новый дочерний процесс. Затем выполняется системная оболочка, чтобы выполнить запрошенную команду. Etc и т. Д. И т. Д. ...

Теперь мы не говорим об измерении временных интервалов, которые длились много секунд или минут. Если мы пытаемся измерить что-то, что занимает много времени для выполнения, относительные накладные расходы popen() будут незначительными, и не о чем беспокоиться. Но ожидаемое время для загрузки образца файла, с целью выяснить, насколько быстро каждое зеркало содержимого - я бы ожидал, что фактическое время загрузки будет относительно коротким. Но мне кажется, что накладные расходы делают это таким образом, разворачивая совершенно новый процесс и выполняя сначала системную оболочку, тогда команда wget с ее массивным списком зависимостей будет статистически значимой.

И как я уже говорил в начале, учитывая, что это пытается определить неопределенно туманную концепцию «самого быстрого зеркала», которая уже является неточной наукой - мне кажется, что вы действительно хотите избавиться здесь совершенно неактуальны накладные расходы - насколько это возможно, чтобы получить точный результат.

Итак, мне кажется, что вы действительно не хотите измерять здесь ничего, кроме того, что вы пытаетесь измерить: пропускная способность сети. И вы, конечно, не хотите измерять какие-либо из того, что происходит до того, как произойдет какая-либо сетевая активность.

Я до сих пор считаю, что попытка попробовать пробную версию - разумное предложение. Что здесь не разумно, так это все popen и wget раздувание. Итак, забудьте все это. Выбросьте это окно. Вы хотите измерить, сколько времени требуется, чтобы загрузить образец файла по HTTP, из каждого зеркало-кандидата? Ну, почему бы вам не сделать именно так?

1) Создать новый socket().

2) Используйте команду getaddrinfo() для выполнения поиска DNS и получения IP-адреса кандидата-зеркала.

3) connect() к порту HTTP зеркала.

4) Отформатируйте соответствующий HTTP GET request и отправьте его на сервер.

Вышеупомянутое делает в значительной степени то, что делает popen/wget, вплоть до этого момента.

И только теперь я начну работать, хватая текущий gettimeofday(), а затем дождитесь, пока я прочитаю весь файл образца из сокета, а затем снова возьмем текущий gettimeofday(), чтобы получить время окончания передачи, и затем вычислить фактическое время, необходимое для получения файла из зеркала.

Только тогда у меня будет некоторая разумная уверенность в том, что я буду фактически измерять время, необходимое для получения образца файла из зеркала CDN, и полностью игнорировать время, необходимое для выполнения кучки совершенно несвязанных процессов; а затем, взяв один и тот же образец из нескольких зеркал CDN, есть какая-то надежда на выбор, используя как можно больше разумной эвристики.

+0

отличный ответ. 2 очка вы сделали то, что я получил от вашего ответа. Во-первых, нужно действительно избавиться от столь же неуместных накладных расходов, а во-вторых, эта задача - неточная наука, и нет общепринятого определения того, что означает «самый быстрый». И поскольку «HTTP» находится в контексте в этом вопросе, ваш ответ будет более полным, если вы будете отражать ваш простой «сокет/HTTP GET» с небольшим фрагментом кода. –

+1

Вы можете найти примеры кода для использования сокетов по адресу http://www.linuxhowtos.org/C_C++/socket.htm и бесконечный запас дополнительных примеров, которые можно найти в простом поиске Google. Точно так же учебник с примерами HTTP-запросов можно найти на https://www.jmarshall.com/easy/http/, и, опять же, бесконечное предложение дополнительных примеров - это просто поиск Google. Я нашел обе эти ссылки менее чем за 10 секунд, с Google. Я не вижу оснований явно ссылаться на Google. –