C++ 如何在C+中正确确定最快的CDN、镜像、下载服务器+;

C++ 如何在C+中正确确定最快的CDN、镜像、下载服务器+;,c++,c++11,download,cdn,mirror,C++,C++11,Download,Cdn,Mirror,我正在努力解决的问题是,如何在c++中确定哪个服务器是客户端连接最快的服务器,并从tarball克隆或下载git。因此,基本上我想从已知的镜像集合中选择将用于从中下载内容的镜像 我编写的以下代码说明了我试图更清楚地实现的目标,但我认为这不应该在生产中使用:) 假设我有两个已知的源镜像git-1.example.com和git-2.example.com,我想从一个客户端连接最好的镜像下载tag-x.tar.gz CDN.h #包括 #包括 #包括 #包括 #包括 #包括 #包括 使用名称空间s

我正在努力解决的问题是,如何在
c++
中确定哪个服务器是客户端连接最快的服务器,并从
tarball
克隆或下载
git
。因此,基本上我想从已知的镜像集合中选择将用于从中下载内容的镜像

我编写的以下代码说明了我试图更清楚地实现的目标,但我认为这不应该在生产中使用:)

假设我有两个已知的源镜像
git-1.example.com
git-2.example.com
,我想从一个客户端连接最好的镜像下载
tag-x.tar.gz

CDN.h

#包括
#包括
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
类CDN{
公众:
时间长;
字符串主机;
字符串原型;
字符串路径;
串分路速度;
双kbs;
双按揭证券;
双秒;
长整数毫秒;
CDN(字符串,字符串,字符串);
void get_download_speed();
布尔运算符<(常数CDN&);
};
#恩迪夫
CDN.cpp

#包括“CND.h”
CDN::CDN(字符串协议、字符串主机名、字符串下载路径)
{
proto=协议;
主机=主机名;
路径=下载路径;
dl_time=ms=sec=mbs=kbs=0;
获取下载速度();
}
void CDN::获取下载速度()
{
struct timeval dl_已启动;
gettimeofday(&dl_start,NULL);
long int download_start=((未签名的long long)dl_started.tv_sec*1000000)+dl_started.tv_usec;
字符缓冲区[256];
char cmd_输出[32];
sprintf(buffer,“wget-O/dev/null--trys=1--timeout=2--没有dns缓存--没有缓存%s://%s/%s2>&1|grep-O--color=never\“[0-9.]\+[KM]*B/s\”,proto.c_str(),host.c_str(),path.c_str());
fflush(stdout);
FILE*p=popen(缓冲区,“r”);
fgets(cmd_输出,sizeof(缓冲区),p);
命令输出[strcspn(命令输出,“\n”)]=0;
pclose(p);
dl_速度=字符串(cmd_输出);
struct timeval下载结束;
gettimeofday(下载结束(&U),NULL);
long int download_end=((未签名的long long)download_end.tv_sec*1000000)+download_end.tv_usec;
大小输出类型k=dl\u速度查找(“KB/s”);
大小输出类型m=dl速度查找(“MB/s”);
if(输出类型=字符串::npos){
字符串dl_字节=dl_速度.substr(0,输出类型\u k-1);
double dl_mb=atof(dl_bytes.c_str())/1000;
kbs=atof(dl_bytes.c_str());
mbs=dl_-mb;
}else if(输出类型=string::npos){
字符串dl_字节=dl_速度.substr(0,输出类型\u m-1);
double dl_kb=atof(dl_bytes.c_str())*1000;
kbs=dl_kb;
mbs=atof(dl_bytes.c_str());
}否则{

cout首先,试图确定“最快的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()
ed。操作系统内核创建一个新的子进程

2) 新的子进程
exec
()s
/bin/bash
,或者您的默认系统shell,传入一个以“wget”开头的长字符串,后跟一组您在上面看到的其他参数

3) 操作系统内核加载“/bin/bash”作为新的子进程。内核加载并打开系统外壳通常需要运行的所有共享库

4) 系统外壳进程初始化。它读取
$HOME/.bashrc
文件并执行它,很可能与系统外壳通常执行的任何标准外壳初始化文件和脚本一起执行。这本身可以创建一组新进程,必须在新系统外壳进程之前初始化和执行这些进程事实上,我有时间

5) …解析最初作为参数接收的“wget”命令,并执行它

6) 操作系统内核现在加载“wget”作为新的子进程。内核加载并打开
wget
进程需要的任何和所有共享库。查看我的Linux框,“wget”加载不少于25个单独的共享库,包括kerberos和ssl库。这些共享库中的每一个都被初始化

7)
wget
com
#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;
}
#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;
}
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);