babelouest / ulfius

Web Framework to build REST APIs, Webservices or any HTTP endpoint in C language. Can stream large amount of data, integrate JSON data with Jansson, and create websocket services
https://babelouest.github.io/ulfius
GNU Lesser General Public License v2.1
1.07k stars 183 forks source link

Connection failure error occurs in high concurrency scenario #208

Open zhangsen1860 opened 2 years ago

zhangsen1860 commented 2 years ago

Hello @babelouest , I tested ulfius in multithreading scenario, I created some threads and each thread connected the server 1000 times, when the number of threads increased to 1000, the connection failure error occured, error code is 5. Below is my test code:

/////////////////////////////////////////////////////////////////////////////// //client.c ///////////////////////////////////////////////////////////////////////////////

include

include

include "ulfius.h"

include "pthread.h"

static void get_msg_from_response(struct _u_response* response, char* res){ if(NULL != response){ res = (char)calloc(response->binary_body_length + 1, 1); memcpy(res, response->binary_body, response->binary_body_length); } }

void fun(void args){ int i = 0, res = 0; int times = (int)args; char url[256] = { 0 }; char res_msg = NULL; struct _u_request request; struct _u_response response; char *req_str = "123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100";

sprintf(url, "http://127.0.0.1:20000");
while(i < times){
    ulfius_init_request(&request);
    ulfius_init_response(&response);

    ulfius_set_request_properties(&request,
               U_OPT_HTTP_VERB, "POST",
               U_OPT_HTTP_URL, url,
               U_OPT_HTTP_URL_APPEND, "/test/thread",
               U_OPT_TIMEOUT, 20,
               U_OPT_BINARY_BODY, req_str, o_strlen(req_str),
               U_OPT_HEADER_PARAMETER, "Content-Type", "application/json",
               U_OPT_NONE);

    res = ulfius_send_http_request(&request, &response);
    if(res != U_OK){
        printf("Communication error! error code = %d.\n", res);
    }

    get_msg_from_response(&response, &res_msg);
    printf("Server response:%s.\n", res_msg);

    ulfius_clean_response(&response);
    ulfius_clean_request(&request);
    if(res_msg){
        free(res_msg);
        res_msg = NULL;
    }
    i++;
}

}

int mult_thread_testing(int thread_num, int times){ int num = 0; int t = 0; t = times; while(num < thread_num){ //usleep(200000); pthread_t thread_id; pthread_create(&thread_id, NULL, (void*)fun, &t); pthread_detach(thread_id); num++; } getchar(); return 0; }
int main(int argc, char** argv){ if (argc < 2) { getchar(); return 0; }

if (!strcmp(argv[1], "thread_test")) {
    printf("-------------Thread Testing-------------\n");
    if (argc < 4) {
        printf("-------------cThread Testing args error, need threads num, times--------------\n");
        return 0;
    }
    mult_thread_testing(atoi(argv[2]), atoi(argv[3]));
    return 0;
}

}

/////////////////////////////////////////////////////////////////////////////// //server.c ///////////////////////////////////////////////////////////////////////////////

include

include

include "ulfius.h"

void get_msg_from_request(const struct _u_request * request, char *res) { if (NULL != request) { res = (char)calloc(request->binary_body_length + 1, 1); memcpy(res, request->binary_body, request->binary_body_length); } }

int callback_test_thread(const struct _u_request request, struct _u_response response, void user_data) { char req_str = NULL;

get_msg_from_request(request, &req_str); printf("Request data from client:\n %s.\n", req_str);

ulfius_set_string_body_response(response, 200, "Receive data success"); if(req_str){ free(req_str); req_str = NULL; } return U_CALLBACK_CONTINUE; }

int main(){ int res = 0; struct _u_instance instance; res = ulfius_init_instance(&instance, 20000, NULL, NULL); if(res != U_OK){ printf("Init instance failed, error code = %d.\n", res); } ulfius_add_endpoint_by_val(&instance, "POST", "/test/thread", NULL, 0, &callback_test_thread, NULL); res = ulfius_start_framework(&instance); if(res == U_OK){ printf("Start framwork success!\n"); while(1){ sleep(1); } }else{ printf("Start Rest Server: Error starting framework, error code = %d.\n", res); } ulfius_stop_framework(&instance); ulfius_clean_instance(&instance); return res; } ///////////////////////////////////////////////////////////////////////////////

Does ulfius have a limit on the number of threads, or there are some problem in my code?

babelouest commented 2 years ago

Hello @zhangsen1860 ,

Ulfius is based on libmicrohttpd for the low-level http management, including network connections.

More specifically, it uses the mhd flags MHD_USE_THREAD_PER_CONNECTION and MHD_USE_INTERNAL_POLLING_THREAD for the multi thread parameters.

I don't know specifically what is the thread limit for an ulfius instance, but of course every ulfius instance has a limit on the number of threads it can open, based on libmicrohttpd and the system beneath.

Here are some quick searches about libmicrohttpd threads management:

Here is your example programs patched to work, and if make a quick test, I come to the limit with the following options:

$ ./client thread_test 340 10

This seems to be the limit on my side.

If you need to calculate the thread limit for a program, I suggest you run the client and the server on different machines, otherwise the client would use resources that could be used by the server, and the tests would lack precision.

zhangsen1860.tar.gz

zhangsen1860 commented 2 years ago

Sorry to reply so late, I will study the libmicrohttpd and test again.

babelouest commented 2 years ago

Hello @zhangsen1860 , any update on your tests?