блоки libevent при публикации нескольких файлов - PullRequest
0 голосов
/ 16 октября 2018

Я работаю над встроенной платой arm в C с ядром linux 4.14.Я использую libevent версии 2 и создал обработчики для двух URL.Один для публикации файлов, а другой для получения статуса загрузки.Пользователь будет подключен через браузер и загрузит файл через многочастную форму POST и получит статус загрузки, используя запрос GET.

Вот некоторый псевдокод, чтобы вы могли получить представление о процессе.

void upload_cb(struct evhttp_request *req, void *arg){
    struct bufferevent *bev = evhttp_connection_get_bufferevent(req->evcon);
    if (bev) //Prio is initialized with 10 states in main
        bufferevent_priority_set(bev, 9); // set to low priority
    struct evbuffer* post_buffer = evhttp_request_get_input_buffer(req);
    size_t body_size = evbuffer_get_length(post_buffer);
    // a multipart parser takes care of writing the 
    // post_buffer content to a file
    // this takes a few seconds and after completing this 
    // the status_cb is accessible again
}

void status_cb(struct evhttp_request *req, void *arg){
    // send some json
}

evhttp_set_cb(_http, "/upload", upload_cb, NULL);
evhttp_set_cb(_http, "/status", status_cb, NULL);

Когда я загружаю файл размером около 10 Мб и в то же время опрашиваю URL-адрес статуса каждую 1 секунду, URL-адрес статуса не будет отвечать в течение 12 секунд, пока файл не будет правильно обработан и функция не будетвернулся.

upload_cb занимает некоторое время для обработки данных и поэтому удерживает status_cb от выполнения.Это нежелательно, потому что приложение не отвечает на данный момент.

Я пытаюсь постоянно получать статус из приложения и постоянно обновлять пользовательский интерфейс о происходящем.Из того, что я могу сказать, обратный вызов upload_cb вызывается после того, как все данные были буферизованы и готовы к обратному вызову для его обработки.Это означает, что он буферизировал бы все 10 Мбайт в памяти.

Я пытался снизить приоритет буферов в надежде, что планировщик событий прервет и даст некоторое время другим событиям, но мне кажется, чтоЛибвент работает неверно.

Я искал альтернативные решения и чувствовал, что мог бы создать отдельный поток, в котором я мог бы обрабатывать загрузку + сохранение в файл, а затем передавать управление обратно в основной поток.Но я бы предпочел спросить здесь, есть ли более элегантное решение, которое предполагает полное пребывание с libevent.

Могу ли я получить libevent для прерывания обработки загрузки и ответа на запрос статуса?Могу ли я заставить libevent получать данные порциями, чтобы он мог обрабатывать другие обратные вызовы?


ОБНОВЛЕНИЕ 1: в соответствии с этот вопрос Я должен использоватьнеблокирующие вызовы API.Хотя запись файлов неблокирующая, кажется, что буфер записи ядра заполнен, потому что я записываю 10+ Мбайт в файл.Я переместил разбор и запись в новый поток, который позволяет libevent вернуться из функции, и сейчас изучаю, как мне следует обрабатывать post_buffer (многопоточный доступ, освобождение его после использования и т. Д.)

UPDATE2: перемещение парсинга в новый поток не работает, потому что тогда возвращается обратный вызов upload_cb и очищается evhttp_request и его буфер, пока поток все еще хочет его проанализировать.Я склоняюсь к созданию дополнительного экземпляра event_base для бесперебойной загрузки, как указано в ссылках на этот вопрос

1 Ответ

0 голосов
/ 22 октября 2018

Я продолжал читать в Интернете и нашел решение, которое работало для меня, в основном вдохновленное этим кодом здесь: Многопоточный HTTPServer с использованием evhttp

struct eventbase *eventbase2;

void threadfunction(){
    eventbase2 = event_base_new();
    struct evhttp* http2 = evhttp_new(event_base2);
    evhttp_set_cb(http2, "/upload", upload_cb, NULL);
    struct evhttp_bound_socket* handle = 
        evhttp_bind_socket_with_handle(http2, "0.0.0.0", 8080);
    event_base_dispatch(event_base2);  // <-------- it will wait here
    evhttp_del_accept_socket(http2, handle);
    evhttp_free(http2);
    event_base_free(event_base2);
}

// initialize a thread with the threadfunction

Я создал новую тему (не показано в коде выше), который использует threadfunction в качестве своей функции потока.Функция содержит вторую секунду eventbase, поэтому URL загрузки может быть вызван и может занять столько времени, сколько нужно.Поскольку URL-адрес статуса обрабатывается первой базой событий, он будет работать параллельно, а URL-адрес загрузки не будет блокировать его, пока он делает свое дело.

Чтобы остановить работающий поток, вам следует вызвать

event_base_loopbreak(event_base2);

а потом, наверное, почистить после вашей ветки.

...