代码之家  ›  专栏  ›  技术社区  ›  mkungla

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

  •  -6
  • mkungla  · 技术社区  · 9 年前

    我正在纠结的问题是如何确定 c++ 客户端连接最快的服务器 git 从克隆或下载 tarball 。所以基本上我想从 已知 镜像将用于从中下载内容的镜像。

    遵循我编写的代码 演示 也许我想更清楚地实现什么,但我相信这不是我们应该在生产中使用的东西:)。

    假设我有两个已知的源镜像 git-1.exmple.com git-2.example.com 我想下载 tag-x.tar.gz 从客户端具有最佳连接的服务器。

    加元.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;
    }
    

    主.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 回复  |  直到 9 年前
        1
  •  1
  •   Sam Varshavchik    9 年前

    首先,试图确定“最快的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 ()秒 /bin/bash ,或您的默认系统shell,传入一个以“wget”开头的长字符串,然后是上面看到的一系列其他参数。

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

    4) 系统外壳进程初始化。它读取 $HOME/.bashrc 文件并执行它,很可能与系统shell通常执行的任何标准shell初始化文件和脚本一起执行。它本身可以创建一堆新的进程,这些进程必须在新的系统外壳进程真正开始运行之前进行初始化和执行。。。

    5) …解析最初作为参数接收的“wget”命令,以及 执行 ()把它弄丢了。

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

    7) 在 wget公司 命令在主机上执行DNS查找,以获取要连接的web服务器的IP地址。如果本地DNS服务器没有缓存CDN镜像主机名的IP地址,则通常需要几秒钟才能查找CDN镜像的DNS区域的权威DNS服务器,然后查询它们以获取IP地址,来回跳来跳去,跨越互连网。

    现在,有一刻……我似乎忘记了我们在这里要做什么……哦,我记得:通过从每个镜像下载一个示例文件,哪个CDN镜像“最快”,对吗?是的,一定是这样!

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

    呃……从我的角度来看,这不算什么。现在,以上这些都不应该是令人震惊的消息。毕竟,所有这些都在popen()的手册页中进行了描述。如果你阅读波本的手册页,它会告诉你这就是它的作用。启动新的子进程。然后执行系统shell,以便执行请求的命令。等等。。。

    现在,我们不是在讨论测量持续数秒或数分钟的时间间隔。如果我们试图测量需要很长时间才能执行的东西 波本 ()的方法可以忽略不计,也不用担心。但是,为了计算每个内容镜像的速度,下载示例文件的预期时间——我预计实际下载时间会相对较短。但在我看来,这样做的开销,即分叉一个全新的进程,首先执行系统shell,然后执行wget命令及其大量的依赖项,在统计上是非常大的。

    正如我在开头提到的,鉴于这是在试图确定“最快的镜子”这一模糊的概念,这已经是一门不精确的科学了,在我看来,你真的希望尽可能多地去掉这里完全不相关的开销,以便得到尽可能准确的结果。

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

    我仍然认为尝试对样本下载进行计时是一个合理的建议。这里不合理的是 波本 wget公司 膨胀。所以,忘记这一切吧。把它扔出窗外。您需要测量下载示例文件所需的时间 HTTP ,来自每个候选人的镜子?好吧,你为什么不这么做 就这样 ?

    1) 新建 socket ().

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

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

    4) 设置适当的格式 HTTP GET request ,并将其发送到服务器。

    到目前为止,上面所做的与popen/wget所做的差不多。

    只有现在,我才会抓住电流开始计时 gettimeofday() ,然后等待,直到我从套接字中读取整个示例文件,然后获取当前 获取一天的时间() 再次,获取传输的结束时间,然后计算从镜像接收文件的实际时间。

    只有这样,我才会有一些合理的信心,我会真正测量从CDN镜像接收样本文件所需的时间,而完全忽略执行一堆完全不相关的进程所需要的时间;然后,通过从多个CDN镜像中获取相同的样本,尽可能多地使用合理的启发式方法来选择一个。