我正在努力解决的问题是如何在c++
中确定哪个服务器连接速度最快的服务器确实从git
克隆或下载tarball
。所以基本上我想从已知镜像的集合中进行选择,这些镜像将用于从中下载内容。
以下代码我写了演示我想要更清楚地实现的目标,但我相信这不是人们应该在制作中使用的东西:)。
我们假设我有两个已知的源镜像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 ++中实现相同的清洁方式
修改 正如@molbdnilo正确指出
ping测量延迟,但您对吞吐量感兴趣。
因此,我编辑了演示代码以反映这一点,但问题仍然相同
答案 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命令,以及其庞大的依赖列表,将具有统计意义。
正如我在开始时所提到的那样,鉴于这是试图确定“快速镜像”这个模糊不清的概念,这已经是一门不精确的科学了 - 在我看来,你和我一样。我真的想在这里尽可能多地摆脱无关紧要的开销 - 以便尽可能准确地得到结果。
所以,在我看来,除了你想要测量的东西之外,你真的不想在这里测量任何东西:网络带宽。而且你肯定不想在任何网络活动发生之前测量任何发生的事情。
我仍然认为尝试下载示例是一个合理的主张。这里不合理的是所有popen
和wget
膨胀。所以,忘掉所有这一切。把它扔出窗外。您想测量从每个候选镜像下HTTP
上的样本文件所需的时间吗?那么,为什么你不做那个?
1)创建一个新的socket()。
2)使用getaddrinfo()执行DNS查找,并获取候选镜像的IP地址。
3)connect()到镜像的HTTP端口。
4)格式化相应的HTTP GET request,并将其发送到服务器。
上面几乎完成了popen / wget所做的事情,到目前为止。
现在我只能通过抓取当前gettimeofday()
启动时钟,然后等到我从套接字读取整个示例文件,然后再次获取当前gettimeofday()
,以获得传输的结束时间,然后计算从镜像接收文件所花费的实际时间。
只有这样,我才能确信我实际上是在测量从CDN镜像接收样本文件所需的时间,并完全忽略执行一堆完全不相关的进程所需的时间;然后从多个CDN镜像中获取相同的样本,有希望选择一个,尽可能使用合理的启发式方法。