发布多部分文件上传时,libevent会被阻止

时间:2018-10-16 14:52:08

标签: c libevent

我正在使用Linux内核4.14开发C嵌入式嵌入式主板。 我正在使用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 Mb的文件并同时每1秒轮询一次状态URL时,状态URL将在大约12秒内没有响应,直到正确处理了文件并返回了函数。

upload_cb需要一些时间来处理数据,因此阻止了status_cb的执行。 这是不希望的,因为此时应用程序没有响应。

我试图不断从应用程序中获取状态,并使UI保持最新状态。 据我所知,一旦所有数据都已缓冲并准备好供回调处理它,就会调用upload_cb回调。这意味着它将在内存中缓冲整个10 Mb。

我试图降低缓冲区的优先级,希望事件调度程序会中断并给其他事件一些时间,但是看来我对libevent如何工作的想法是不正确的。

我一直在寻找替代解决方案,觉得可以创建一个单独的线程,在其中可以处理上载+保存到文件,然后将控制权交还给主线程。 但是我想在这里问是否有一个更优雅的解决方案,其中包括完全保留libevent。

我可以让libevent中断对上传的处理并回复状态请求吗? 我可以强制libevent分块接收数据,以便它可以处理其他回调吗?


更新1:根据this question,我必须使用非阻塞api调用。尽管写文件是非阻塞的,但似乎内核写缓冲区已满,因为我向文件写了10+ Mb。 我已经将解析和写入一个新线程转换为允许libevent从该函数返回,现在正在研究如何处理post_buffer(多线程访问,使用后释放它等)

更新2:无法将解析移至新线程,因为然后回调upload_cb返回并且evhttp_request及其缓冲区被清除,而线程仍在运行想要解析它。 我倾向于创建一个额外的event_base实例来处理无缝上传,如指向this question

的链接所述

1 个答案:

答案 0 :(得分:0)

我一直在线阅读并找到了一个对我有用的解决方案,主要受此代码启发:Multi-Threaded HTTPServer using 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,并且可以花费所需的时间。由于状态网址是由第一个事件库处理的,因此它将并行运行,并且上载网址在执行操作时不会阻止它。

要停止正在运行的线程,您应该调用

event_base_loopbreak(event_base2);

,然后可能在线程执行后清理。