【问题标题】:Measuring outbound requests per second?每秒测量出站请求?
【发布时间】:2014-05-09 23:43:21
【问题描述】:

我有一个正在使用的网络应用程序 ActiveResource 与另一个有速率限制的服务器通信 联系。我很好奇如何从运行我的 Web 应用程序的主机上最好地监控它 - 也就是说,从我的服务器上的 linux 上的 bash 提示符,我如何测量我的机器每秒向另一台机器发出的出站请求?

我正在寻找一个提供接口、主机名、 和/或它们的某种组合,告诉我我的连接速率 制作到该服务器。我已经接近使用 tc 和 iftop 等工具,但它们报告的是传输的数据量,而不是建立的连接......所以它不是我想要的。我很想看点东西 喜欢:


$awesometool --host thetargethost.com --interface eth0 --interval 5

收集统计数据……

通过接口 eth0 报告 thetargethost.com 的每秒请求数

平均:23 个请求/秒,最小值:12 个请求/秒,最大 39 个请求/秒

采集了 5 个样本


谁能给我指一个?

【问题讨论】:

    标签: ruby linux networking activeresource


    【解决方案1】:

    tcpdump(8) 可以提供非常相似的东西;搜索设置了SYN 标志的TCP 数据包,以捕获three-way handshake 中发往其他对等方的第一个数据包:

    $ sudo tcpdump -c 10 -i eth0 "tcp[tcpflags] & (tcp-syn) != 0 and dst 192.168.0.1"
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
    18:26:24.800308 IP haig.59419 > 192.168.0.1.telnet: Flags [S], seq 3197302320, win 14600, options [mss 1460,sackOK,TS val 19460844 ecr 0,nop,wscale 7], length 0
    ...
    18:26:27.420132 IP haig.59428 > 192.168.0.1.telnet: Flags [S], seq 1238498237, win 14600, options [mss 1460,sackOK,TS val 19461106 ecr 0,nop,wscale 7], length 0
    10 packets captured
    10 packets received by filter
    0 packets dropped by kernel
    

    您可以使用/usr/bin/time 或您的shell 的time 内置函数,或者对输出中的时间戳进行一些算术运算,以获得每秒的平均速率。 (使用十个以上的数据包——这只是为了演示。)

    更新

    我写了一个小程序来运行tcpdump(8),统计数据包,并报告在指定的时间间隔内发送了多少数据包:

    # ./counter --host 192.168.0.1 --interface eth0 --interval 3
    2 requests in 3 seconds; average 0.67 req/seq
    20 requests in 3 seconds; average 6.67 req/seq
    19 requests in 3 seconds; average 6.33 req/seq
    19 requests in 3 seconds; average 6.33 req/seq
    ^C
    # ./counter --host 192.168.0.1 --interface eth0 --interval 5
    30 requests in 5 seconds; average 6.00 req/seq
    20 requests in 5 seconds; average 4.00 req/seq
    1176 requests in 5 seconds; average 235.20 req/seq
    1414 requests in 5 seconds; average 282.80 req/seq
    0 requests in 5 seconds; average 0.00 req/seq
    ^C
    

    因为它要求tcpdump(8) 使用行缓冲输出,所以我有点担心它的扩展速度可能不会超过每秒 200-300 个请求,至少在我的硬件上是这样。但如果没有行缓冲输出,tcpdump(8) 将等到其输出缓冲区(有关详细信息,请参阅 setvbuf(3))已满后再发送 any 输出,从而导致极其不稳定的结果。

    但是,如果您的连接率不是很高,这可能会满足您的需求。如果你的连接率更高,那么这个小技巧可能最好忽略 - 我觉得iptables(8) 可能可以计算流。

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <getopt.h>
    #include <signal.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <sys/types.h>
    
    #define CMDLEN 1024
    #define TCPDUMPLEN 4096
    
    int show_stats;
    long counter;
    
    void alarm_handler(int signum)
    {
        show_stats = 1;
    }
    
    void install_handler(void)
    {
        struct sigaction sa;
    
        memset(&sa, 0, sizeof(sa));
    
        sigemptyset(&sa.sa_mask);
        sa.sa_handler = &alarm_handler;
        if (sigaction(SIGALRM, &sa, NULL) == -1) {
            perror("Can't install alarm handler");
            exit(1);
        }
    }
    
    int count_lines(char *p, int bytes)
    {
        int counter = 0;
        char *i;
        for (i=p; i < p+bytes ; i++) {
            if (*i == '\n')
                counter++;
        }
        return counter;
    }
    
    int spawn_tcpdump(char *host, char *interface)
    {
        int fd[2];
        pid_t child;
    
        if (pipe(fd) == -1) {
            perror("Can't create pipes");
            exit(1);
        }
    
        child = fork();
    
        if (child == -1) {
            perror("Can't fork(2) for tcpdump");
            exit(1);
        }
    
        if (child == 0) {
            int null;
            int len;
            char syn_and_dst[CMDLEN];
    
            len = snprintf(syn_and_dst, CMDLEN, "tcp[tcpflags] & (tcp-syn) != 0 and dst %s", host);
    
            if (len > CMDLEN) {
                perror("host argument too long");
                exit(1);
            }
    
            /* child writes into pipe */
            close(fd[0]);
            dup2(fd[1], STDOUT_FILENO);
    
            /* throw away first two lines of tcpdump output */
            null = open("/dev/null", O_WRONLY);
    
            if (null == -1) {
                perror("Can't open /dev/null");
                exit(1);
            }
    
            dup2(null, STDERR_FILENO);
    
            execl("/usr/sbin/tcpdump", "tcpdump", "-l", "-n", "-s 96", "-i", interface, syn_and_dst, (char *) NULL);
            /* can't reach */
            perror("Cannot execute tcpdump");
            exit(1); 
        } else {
            /* parent reads from pipe */
            close(fd[1]);
            return fd[0];
        }
    }
    
    int main(int argc, char *argv[])
    {
        int tcpdump;
        char *host;
        char *interface;
        long interval;
    
    
        while (1) {
            int option_index;
            int c;
    
            static struct option opts[] = {
                {"host", required_argument, NULL, 'h'},
                {"interface", required_argument, NULL, 'i'},
                {"interval", required_argument, NULL, 'n'},
                {0, 0, 0, 0},
            };
    
            c = getopt_long(argc, argv, "", opts, &option_index);
    
            if (c == -1)
                break;
    
            switch (c) {
                case 'h':
                    host = strdup(optarg);
                    break;
                case 'i':
                    interface = strdup(optarg);
                    break;
                case 'n': {
                        char *endptr;
                        interval = strtol(optarg, &endptr, 10);
                        if (!(optarg[0] != '\0' && endptr[0] == '\0')) {
                            fprintf(stderr, "Expected integer; invalid"
                                    " input '%s'\n", optarg);
                            exit(1);
                        }
                    }
                    break;
                default:
                    fprintf(stderr, "Option parsing error\n");
                    exit(1);
            }
    
        }
    
        if (optind < argc) {
            fprintf(stderr, "unexpected arguments: ");
            while (optind < argc) {
                fprintf(stderr, "%s ", argv[optind++]);
            }
            fprintf(stderr, "\n");
        }
    
        tcpdump = spawn_tcpdump(host, interface);
    
        install_handler();
        alarm(interval);
    
        while(1) {
            if (show_stats) {
                printf("%ld requests in %ld seconds; average %2.2f req/seq\n",
                        counter, interval, (double)counter / (double)interval);
                counter = 0;
                show_stats = 0;
                alarm(interval);
            } else {
                char buffer[TCPDUMPLEN];
                int ret;
    
                memset(buffer, 0, TCPDUMPLEN);
                ret = read(tcpdump, buffer, TCPDUMPLEN);
                if (ret == -1 && errno == EINTR) {
                    /* nop */
                } else if (ret == -1) {
                    perror("read");
                    exit(1);
                } else {
                    counter += count_lines(buffer, ret);
                }
            }
        }
    
        exit(0);
    }
    

    【讨论】:

    • 这是一个很好的开始,但是 -c 的任何值对于一天中不会发生连接的时间都是有问题的。我需要它能够每秒报告“0”个连接,这可能发生在非工作时间,但这会在那段时间挂起(等待)。有没有办法将 tcpdump 转储到 tmp 文件几秒钟,然后用 grep 计数?例如,将这个解决方案与 Munin 一起使用,我想每 5 分钟获取一次快照,并在 5 秒或更短的时间内获取。
    • 太棒了! (我原来的评论是在你更新之前)。我正在用 perl 破解一些东西,定义一个 shell 函数和字数,但你的解决方案要好得多。谢谢你。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-13
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    • 2012-06-23
    • 1970-01-01
    • 2019-10-23
    相关资源
    最近更新 更多