Измерение исходящих запросов в секунду? - PullRequest
5 голосов
/ 17 ноября 2011

У меня есть веб-приложение, которое использует ActiveResource для связи с другим сервером, который имеет ограничение скорости на подключение. Мне любопытно, как я могу наилучшим образом отслеживать, с какого хоста, на котором запущено мое веб-приложение, то есть из приглашения bash на linux с моего сервера, как я могу измерить количество исходящих запросов в секунду, которые моя машина делает другому?

Я ищу linux-однострочник с интерфейсом, именем хоста, и / или некоторая их комбинация, говорит мне скорость соединения делая на этом сервере. Я познакомился с такими инструментами, как tc и iftop, но они сообщают об объеме передаваемых данных, а не об установленных соединениях ... так что это не совсем то, что я ищу. Я хотел бы увидеть что-то как:


$ awesometool --host thetargethost.com - интерфейс eth0 - интервал 5

сбор статистики…

Количество запросов в секунду для thetargethost.com через интерфейс eth0

ср .: 23 требования / сек. Мин .: 12 запросов / сек. Макс. 39 требований / сек.

5 проб


Может кто-нибудь указать мне на один?

1 Ответ

4 голосов
/ 17 ноября 2011

tcpdump(8) может обеспечить что-то очень похожее;искать TCP-пакеты с установленным флагом SYN, чтобы перехватить первый пакет в трехстороннем рукопожатии , предназначенный для вашего другого партнера:

$ 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 или 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);
}
...