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

时间:2015-12-29 20:13:22

标签: c++ c++11 download cdn mirror

我正在努力解决的问题是如何在c++中确定哪个服务器连接速度最快的服务器确实从git克隆或下载tarball 。所以基本上我想从已知镜像的集合中进行选择,这些镜像将用于从中下载内容。

  

以下代码我写了演示我想要更清楚地实现的目标,但我相信这不是人们应该在制作中使用的东西:)。

我们假设我有两个已知的源镜像git-1.exmple.comgit-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 ++中实现相同的清洁方式

修改 正如@molbdnilo正确指出

  

ping测量延迟,但您对吞吐量感兴趣。

因此,我编辑了演示代码以反映这一点,但问题仍然相同

1 个答案:

答案 0 :(得分:1)

对于初学者来说,试图确定最快的CDN镜像&#34;是一门不精确的科学。关于什么&#34;最快&#34;没有普遍接受的定义。手段。在这里,大多数人都希望选择一种合理的启发式方法来实现最快的&#34;意味着,然后在这种情况下精确地测量这种启发式。

在此处的代码示例中,所选的启发式似乎是通过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或您的默认系统shell,传入一个以&#34; wget&#34;开头的长字符串,然后是一堆您在上面看到的其他参数。

3)操作系统内核加载&#34; / bin / bash&#34;作为新的子进程。内核加载并打开系统shell通常需要运行的所有共享库。

4)系统shell进程初始化。它读取$HOME/.bashrc文件并执行它,最有可能与系统shell通常执行的任何标准shell初始化文件和脚本一起执行。在新的系统shell进程实际到达之前,它本身可以创建一堆必须初始化和执行的新进程...

5)...解析&#34; wget&#34;命令它最初作为参数接收,并exec()使用它。

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

7)wget命令在主机上执行DNS查找,以获取要连接的Web服务器的IP地址。如果本地DNS服务器没有缓存CDN镜像的主机名的IP地址,则通常需要几秒钟来查找CDN镜像的DNS区域的权威DNS服务器,然后查询它们的IP地址,以这种方式和那种方式,跨越intertubes。

现在,有一刻......我似乎已经忘记了我们在这里要做的事情......哦,我记得:通过从每个镜像下载样本文件,哪个CDN镜像是最快的#34; , 对?是的,那一定是它!

现在,上面所做的所有工作,所有这些工作,都与确定哪个内容镜像最快有关?

呃......从它看起来对我的方式来说并不多。现在,以上都不应该是如此令人震惊的消息。毕竟,所有这些都在popen()的手册页中描述。如果您阅读了popen的手册页,它会告诉您它的作用。开始一个新的子进程。然后执行系统shell,以执行请求的命令。等等等......

现在,我们不是在谈论测量持续数秒或分钟的时间间隔。如果我们尝试测量需要花费很长时间才能执行的内容,popen()方法的相对开销可以忽略不计,并且不用担心。但是下载示例文件的预期时间,目的是确定每个内容镜像的速度 - 我希望实际下载时间相对较短。但在我看来,这样做的开销,分配一个全新的进程,首先执行系统shell,然后wget命令,以及其庞大的依赖列表,将具有统计意义。

正如我在开始时所提到的那样,鉴于这是试图确定“快速镜像”这个模糊不清的概念,这已经是一门不精确的科学了 - 在我看来,你和我一样。我真的想在这里尽可能多地摆脱无关紧要的开销 - 以便尽可能准确地得到结果。

所以,在我看来,除了你想要测量的东西之外,你真的不想在这里测量任何东西:网络带宽。而且你肯定不想在任何网络活动发生之前测量任何发生的事情。

我仍然认为尝试下载示例是一个合理的主张。这里不合理的是所有popenwget膨胀。所以,忘掉所有这一切。把它扔出窗外。您想测量从每个候选镜像下HTTP上的样本文件所需的时间吗?那么,为什么你不做那个

1)创建一个新的socket()。

2)使用getaddrinfo()执行DNS查找,并获取候选镜像的IP地址。

3)connect()到镜像的HTTP端口。

4)格式化相应的HTTP GET request,并将其发送到服务器。

上面几乎完成了popen / wget所做的事情,到目前为止。

现在我只能通过抓取当前gettimeofday()启动时钟,然后等到我从套接字读取整个示例文件,然后再次获取当前gettimeofday(),以获得传输的结束时间,然后计算从镜像接收文件所花费的实际时间。

只有这样,我才能确信我实际上是在测量从CDN镜像接收样本文件所需的时间,并完全忽略执行一堆完全不相关的进程所需的时间;然后从多个CDN镜像中获取相同的样本,有希望选择一个,尽可能使用合理的启发式方法。