1. 程式人生 > >Linux LVS高併發測試程式,核心引數設定,連線數查詢指令

Linux LVS高併發測試程式,核心引數設定,連線數查詢指令

最近在公司參與了一個LVS系統測試的專案,學習到了一些關於高併發測試相關的知識,寫到部落格裡記錄下

Linux核心引數設定

  • 在伺服器端需要調整系統最大檔案控制代碼數
    ulimit -n 1000000
    在伺服器硬體支援,以及服務較輕量的情況下,最大連線數未必只限於100000,視情況而定吧

  • 客戶端需要發起大量連線,理論上是最多可支援65535個連線,但實際上Linux的預設可用埠號並沒有這麼多,需要修改
    sudo echo 1024 65534 > /proc/sys/net/ipv4/ip_local_port_range

檢視連線個數

我們一開始用的是
netstat -nap | grep 程式名 | grep ESTABLISHED| wc -l


來查詢,這個指令當連線數上萬後就變得很慢,而且我們發現了一個略坑的問題,通過這組合指令查詢到的結果是波動的,我特意試了10000個連線下,在程式沒有報有連線關閉的情況下,通過netstat查出來的連線個數在9998到10000之間波動。最後我換了個指令
ss state ESTABLISHED dport = :埠號 and dst IP | wc -l
通過ss指令查詢,不僅速度很快,而且不會出現計數不準的情況,需要注意的是,查詢到的連線數要比實際多1

測試程式

/*client1.cpp
 *該程式需要libevent庫支援
 *測試有無LVS時連線延時區別的程式
 *程式發起指定數量的連線後,將每秒給伺服器傳送一個報文,每個連線收到     
 *伺服器返回的報文後計算耗時
 */
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <netdb.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <arpa/inet.h> #include <sys/time.h> #include <iostream>
#include <map> #include <vector> #include <string> #include "event2/bufferevent.h" #include "event2/buffer.h" #include "event2/listener.h" #include "event2/util.h" #include "event2/event.h" using namespace std; typedef struct bufferevent* BufferEvent; map<struct bufferevent*, long long> bufferevent_map; map<struct bufferevent*, long long>::iterator iter; static int index_ = 0; struct cb_arg { struct event *ev; struct timeval tv; }; void timeout_cb(int fd, short event, void *params) { if (iter == bufferevent_map.end()) { return ; } char buf[1024]; struct cb_arg *arg = (struct cb_arg*)params; struct event *timeout = arg->ev; struct timeval tv = arg->tv; struct timeval start; struct bufferevent *bufevt = iter->first; gettimeofday(&start,NULL); iter->second = 1000000 * start.tv_sec + start.tv_usec; sprintf(buf, "%lld", iter->second); bufferevent_write(bufevt, buf, strlen(buf)); printf("fd %u send %s\n", fd, buf); index_++; iter++; evtimer_add(timeout, &tv); } void read_cb(struct bufferevent *bufevt, void *arg) { char line[1024] = {0}; size_t n; evutil_socket_t fd = bufferevent_getfd(bufevt); struct timeval start; map<struct bufferevent*, long long>::iterator it_temp; it_temp = bufferevent_map.find(bufevt); if (it_temp == bufferevent_map.end()) { return ; } gettimeofday(&start,NULL); long long now = 1000000 * start.tv_sec + start.tv_usec; long long used = now - it_temp->second; while (n = bufferevent_read(bufevt, line, 1024), n > 0) { printf("fd=%u, used time: %lld us\n", fd, used); } } void error_cb(struct bufferevent *bufevt, short events, void *user_data) { evutil_socket_t fd = bufferevent_getfd(bufevt); if (events & BEV_EVENT_EOF) { printf("Connection closed.\n"); printf("fd %u close !\n", fd); bufferevent_free(bufevt); } else if (events & BEV_EVENT_ERROR) { printf("Got an error on the connection: %s\n", strerror(errno)); printf("fd %u close !\n", fd); bufferevent_free(bufevt); } } int main(int argc, char *argv[]) { if (argc != 4) { printf("引數個數不正確 %d, 需要3個引數,連線個數,IP,埠號\n", argc - 1); return 0; } vector<string> argvs; for (int i = 1; i < argc; ++i) { argvs.push_back(argv[i]); } timeval tv = {1, 0}; int times = atoi(argvs[0].c_str()); int port = atoi(argvs[2].c_str()); struct sockaddr_in sin; struct bufferevent* bufevt; event_base *evbase = event_base_new(); if (evbase == NULL) { printf("err in new event_base\n"); return 0; } struct event* timeout; struct cb_arg arg; timeout = evtimer_new(evbase, timeout_cb, &arg); if (timeout == NULL) { printf("err in new evtimer\n"); return 0; } arg.ev = timeout; arg.tv = tv; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(argvs[1].c_str()); sin.sin_port = htons(port); memset(sin.sin_zero, 0x00, 8); for (int i = 0; i < times; ++i) { int fd = socket(AF_INET, SOCK_STREAM, 0); evutil_make_socket_nonblocking(fd); bufevt = bufferevent_socket_new(evbase, fd, BEV_OPT_CLOSE_ON_FREE); if (bufevt == NULL) { printf("err in new bufferevent, fd %u connect failed !\n", fd); continue ; } bufferevent_setcb(bufevt, read_cb, NULL, error_cb, NULL); bufferevent_enable(bufevt, EV_READ | EV_WRITE | EV_PERSIST); if (bufferevent_socket_connect(bufevt, (struct sockaddr *)&sin, sizeof(sin)) < 0) { printf("err in connect with server, fd %u connect failed !\n", fd); bufferevent_free(bufevt); continue ; } bufferevent_map.insert(make_pair(bufevt, 0)); } iter = bufferevent_map.begin(); evtimer_add(timeout, &tv); event_base_dispatch(evbase); evtimer_del(timeout); event_base_free(evbase); return 0; }
/*server.cpp
 *該程式需要libevent庫支援
 *簡單的回射伺服器
 */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>

#include <event2/event.h>
#include <event2/bufferevent.h>

#define LISTEN_BACKLOG 1024

static int time_out_count = 0;
static int err_conn_count = 0;
static int err_accept_count = 0;

void do_accept(evutil_socket_t listener, short event, void *arg);
void read_cb(struct bufferevent *bev, void *arg);
void error_cb(struct bufferevent *bev, short event, void *arg);
void write_cb(struct bufferevent *bev, void *arg);

int main(int argc, char *argv[])
{
    if (argc != 2) {
        printf("引數個數不正確 %d\n", argc);
        return 0;
    }

    int port = atoi(argv[1]);
    evutil_socket_t listener;
    listener = socket(AF_INET, SOCK_STREAM, 0);
    assert(listener > 0);
    evutil_make_listen_socket_reuseable(listener);

    struct sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(port);

    if (bind(listener, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
        perror("bind");
        return 1;
    }

    if (listen(listener, LISTEN_BACKLOG) < 0) {
        perror("listen");
        return 1;
    }

    printf ("Listening...\n");

    evutil_make_socket_nonblocking(listener);

    struct event_base *base = event_base_new();
    assert(base != NULL);
    struct event *listen_event;
    listen_event = event_new(base, listener, EV_READ | EV_PERSIST, do_accept, (void*)base);
    event_add(listen_event, NULL);
    event_base_dispatch(base);

    printf("The End.");
    return 0;
}

void do_accept(evutil_socket_t listener, short event, void *arg)
{
    struct event_base *base = (struct event_base *)arg;
    evutil_socket_t fd;
    struct sockaddr_in sin;
    socklen_t slen;

    fd = accept(listener, (struct sockaddr *)&sin, &slen);
    evutil_make_socket_nonblocking(fd);
    if (fd < 0) {
        printf("err in accept, err ccode %d, 發生accept錯誤的個數 %d", errno, ++err_accept_count);
        return;
    }

    printf("ACCEPT: fd = %u\n", fd);

    struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    bufferevent_setcb(bev, read_cb, NULL, error_cb, arg);
    bufferevent_enable(bev, EV_READ | EV_WRITE | EV_PERSIST);
}

void read_cb(struct bufferevent *bev, void *arg)
{
#define MAX_LINE    256
    char line[MAX_LINE+1];
    int n;
    evutil_socket_t fd = bufferevent_getfd(bev);

    while (n = bufferevent_read(bev, line, MAX_LINE), n > 0) {
        line[n] = '\0';
        bufferevent_write(bev, line, n);
        printf("recv : %s\n", line);
    }
}

void write_cb(struct bufferevent *bev, void *arg) {}

void error_cb(struct bufferevent *bev, short event, void *arg)
{
    evutil_socket_t fd = bufferevent_getfd(bev);
    printf("fd = %u, ", fd);
    if (event & BEV_EVENT_TIMEOUT) {
        time_out_count++;
        printf("Timed out, 發生超時連線個數 %d", time_out_count);
        printf(", err code %d\n", errno);
    }
    else if (event & BEV_EVENT_EOF) {
        printf("connection closed\n");
    }
    else if (event & BEV_EVENT_ERROR) {
        err_conn_count++;
        printf("some other error, 連線被異常關閉個數 %d", err_conn_count);
        printf(", err code %d\n", errno);
    }
    bufferevent_free(bev);
}
/*client2.cpp
 *該程式需要libevent庫支援
 *測試高併發量的測試客戶端程式,該程式每隔10s就會通過所有連線向服務
 *器傳送一個報文,以免長時間沒有互動,連線被LVS斷掉,同時測試伺服器   
 *的負載
 */

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/time.h>


#include <iostream>
#include <map>
#include <vector>
#include <string>

#include "event2/bufferevent.h"
#include "event2/buffer.h"
#include "event2/listener.h"
#include "event2/util.h"
#include "event2/event.h"

using namespace std;

typedef struct bufferevent* BufferEvent;

map<struct bufferevent*, long long>    bufferevent_map;
map<struct bufferevent*, long long>::iterator  iter;
FILE*           fp = NULL;
static int      index_ = 0;
static int      counter = 0;
static int      begin_time = 0;

struct cb_arg
{
    struct event *ev;
    struct timeval tv;
};

void timeout_cb(int fd, short event, void *params)
{
    char buf[1024];
    struct cb_arg *arg = (struct cb_arg*)params;
    struct event *timeout = arg->ev;
    struct timeval tv = arg->tv;

    for (iter = bufferevent_map.begin(); iter != bufferevent_map.end(); ++iter) {
        memset(buf, 0, sizeof(buf));
        struct bufferevent *bufevt = iter->first;

        sprintf(buf, "%lld", iter->second);
        bufferevent_write(bufevt, buf, strlen(buf));
    }

    evtimer_add(timeout, &tv);
}

void read_cb(struct bufferevent *bufevt, void *arg)
{
    char    line[1024] = {0};
    size_t     n;
    evutil_socket_t fd = bufferevent_getfd(bufevt);
    struct timeval start;
    map<struct bufferevent*, long long>::iterator it_temp;

    it_temp = bufferevent_map.find(bufevt);
    if (it_temp == bufferevent_map.end()) {

        return ;
    }

    while (n = bufferevent_read(bufevt, line, 1024), n > 0)
    {
        ;
    }
}


void write_count(int count)
{
    char  buf[1024] = {0};
    int   now_time = (int)time(NULL);

    sprintf(buf, "pass %d s, now connect count %d\n", now_time - begin_time, count);
    fputs(buf, fp);
}

void error_cb(struct bufferevent *bufevt, short events, void *user_data)
{
    evutil_socket_t fd = bufferevent_getfd(bufevt);

    if (events & BEV_EVENT_EOF)
    {
        counter--;
        printf("Connection closed.\n");
        printf("fd %u close ! count %d\n", fd , counter);
        printf("now EST connect count %d\n", counter);
        bufferevent_free(bufevt);
        bufferevent_map.erase(bufevt);
        write_count(counter);
    }
    else if (events & BEV_EVENT_ERROR)
    {
        counter--;
        printf("Got an error on the connection: %d\n", errno);
        printf("fd %u close ! count %d\n", fd , counter);
        printf("now EST connect count %d\n", counter);
        bufferevent_free(bufevt);
        bufferevent_map.erase(bufevt);
        write_count(counter);
    }
}

void create_file()
{
    char  buf[1024] = {0};
    timeval tv;
    gettimeofday(&tv, NULL);

    int   time_ = (int) (1000000 * tv.tv_sec + tv.tv_usec);

    sprintf(buf, "result_%d.txt", time_);

    fp = fopen(buf, "w+");
    if (fp == NULL) {
        printf("create result file failed!\n");
        exit(-1);
    }
}

int main(int argc, char *argv[])
{
    if (argc != 4) {
        printf("引數個數不正確 %d, 需要3個引數,連線個數,IP,埠號\n", argc - 1);
        return 0;
    }

    vector<string>  argvs;
    for (int i = 1; i < argc; ++i) {
        argvs.push_back(argv[i]);
    }

    create_file();

    timeval     tv = {10, 0};
    int         times = atoi(argvs[0].c_str());
    int         port = atoi(argvs[2].c_str());
    struct sockaddr_in  sin;
    struct bufferevent* bufevt;

    event_base  *evbase = event_base_new();
    if (evbase == NULL) {
        printf("err %d in new event_base\n", errno);
        return 0;
    }

    struct event*   timeout;
    struct cb_arg   arg;
    timeout = evtimer_new(evbase, timeout_cb, &arg);
    if (timeout == NULL) {
        printf("err in new evtimer\n");
        return 0;
    }
    arg.ev = timeout;
    arg.tv = tv;

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(argvs[1].c_str());
    sin.sin_port = htons(port);
    memset(sin.sin_zero, 0x00, 8);

    for (int i = 0; i < times; ++i) {
        int fd = socket(AF_INET, SOCK_STREAM, 0);
        evutil_make_socket_nonblocking(fd);
        bufevt = bufferevent_socket_new(evbase, fd, BEV_OPT_CLOSE_ON_FREE);
        if (bufevt == NULL) {
            printf("err %d in new bufferevent, fd %u connect failed !\n", fd, errno);
            continue ;
        }

        bufferevent_setcb(bufevt, read_cb, NULL, error_cb, NULL);
        bufferevent_enable(bufevt, EV_READ | EV_WRITE | EV_PERSIST);

        if (bufferevent_socket_connect(bufevt, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
            printf("err %d in connect with server, fd %u connect failed !\n", fd, errno);
            continue ;
        }

        char buf[64] = {0};
        sprintf(buf, "%d", i);
        bufferevent_map.insert(make_pair(bufevt, 0));
        bufferevent_write(bufevt, buf, strlen(buf));
        usleep(10);
        counter++;
    }

    printf("now EST connect count %d\n", counter);
    write_count(counter);

    evtimer_add(timeout, &tv);
    event_base_dispatch(evbase);
    evtimer_del(timeout);
    event_base_free(evbase);

    return 0;
}