ruby - 测量每秒的出站请求?

标签 ruby linux networking activeresource

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

我正在寻找一个给定接口(interface)、主机名的 linux 单行程序, 和/或它们的某种组合,告诉我我的连接率 制作到该服务器。我已经接近使用 tc 和 iftop 等工具,但这些工具报告的是传输的数据量,而不是建立的连接……所以它不是我想要的。我想看点什么 喜欢:


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

收集统计数据……

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

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

采集了 5 个样本


谁能告诉我一个?

最佳答案

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))已满,然后再发送 任何内容 输出,导致极度紧张的结果。

但如果您的连接率不是那么高,这可能会满足您的需要。如果您的连接率更高,那么最好忽略这个小技巧——让我印象深刻的是 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);
}

关于ruby - 测量每秒的出站请求?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8161281/

相关文章:

ruby - 如何使用 class_eval 获取多属性值

mysql - 如何强制 rsyslog ommysql 使用 utf8?

python - 如何在没有网络的情况下通过 wifi 发送原始数据包?

ruby-on-rails - Rails API 设计 : best way to include other attributes with json_api relationships

ruby-on-rails - 原子 rails 4 个窗口 64 位

android - Appium ERROR 转储失败,因为无法加载 Assets : Invalid file Support

c++ - 安装? linux屏幕后面有什么?

unix - sendto的最大缓冲区长度?

node.js - 无法从 Node.js 连接到 Telnet 服务器

ruby-on-rails - Rails 4 测试用户初始化总是空白