发布多部分文件上传时 libevent 阻塞
libevent blocks when posting multipart file upload
我正在使用 linux 内核 4.14 使用 C 编写嵌入式 arm 开发板。
我正在使用 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 秒内没有响应,直到文件正确处理和函数 returned。
upload_cb 需要一段时间来处理数据,因此会阻止 status_cb 执行。
这是不可取的,因为应用程序此时没有响应。
我正在尝试不断从应用程序中获取状态并使 UI 保持最新状态。
据我所知,一旦所有数据都已缓冲并准备好让回调处理它,就会调用 upload_cb 回调。这意味着它将在内存中缓冲整个 10 Mb。
我曾尝试降低缓冲区的优先级,希望事件调度程序会中断并为其他事件留出一些时间,但我对 libevent 工作方式的看法似乎是不正确的。
我正在寻找替代解决方案,觉得我可以创建一个单独的线程,我可以在其中处理上传 + 保存到文件,然后将控制权交还给主线程。
但我更愿意在这里询问是否有更优雅的解决方案,涉及完全使用 libevent。
我可以让 libevent 中断上传处理并回复状态请求吗?
我可以强制 libevent 以块的形式接收数据,以便它可以处理其他回调吗?
更新 1: 根据 this question 我必须使用非阻塞 api 调用。虽然写入文件是非阻塞的,但似乎内核写入缓冲区已满,因为我将 10+ Mb 写入文件。
我已经将解析和写入移动到一个新线程,它允许 libevent 从函数 return 并且现在正在研究我应该如何处理 post_buffer (多线程访问,使用后释放它等)
更新 2: 将解析移动到新线程不起作用,因为回调 upload_cb
returns 和 evhttp_request
并且它的缓冲区在线程仍然想要解析它时被清理。
我倾向于创建一个额外的 event_base 实例来处理无缝上传,如 this question
的链接中所述
我一直在网上阅读并找到了一个对我有用的解决方案,主要受此代码启发: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,并且可以根据需要花费任意多的时间。由于状态 url 由第一个事件库处理,它将并行工作并且上传 url 在执行其操作时不会阻止它。
要停止 运行 线程,您应该调用
event_base_loopbreak(event_base2);
然后可能会在您的线程后清理。
我正在使用 linux 内核 4.14 使用 C 编写嵌入式 arm 开发板。 我正在使用 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 秒内没有响应,直到文件正确处理和函数 returned。
upload_cb 需要一段时间来处理数据,因此会阻止 status_cb 执行。 这是不可取的,因为应用程序此时没有响应。
我正在尝试不断从应用程序中获取状态并使 UI 保持最新状态。 据我所知,一旦所有数据都已缓冲并准备好让回调处理它,就会调用 upload_cb 回调。这意味着它将在内存中缓冲整个 10 Mb。
我曾尝试降低缓冲区的优先级,希望事件调度程序会中断并为其他事件留出一些时间,但我对 libevent 工作方式的看法似乎是不正确的。
我正在寻找替代解决方案,觉得我可以创建一个单独的线程,我可以在其中处理上传 + 保存到文件,然后将控制权交还给主线程。 但我更愿意在这里询问是否有更优雅的解决方案,涉及完全使用 libevent。
我可以让 libevent 中断上传处理并回复状态请求吗? 我可以强制 libevent 以块的形式接收数据,以便它可以处理其他回调吗?
更新 1: 根据 this question 我必须使用非阻塞 api 调用。虽然写入文件是非阻塞的,但似乎内核写入缓冲区已满,因为我将 10+ Mb 写入文件。 我已经将解析和写入移动到一个新线程,它允许 libevent 从函数 return 并且现在正在研究我应该如何处理 post_buffer (多线程访问,使用后释放它等)
更新 2: 将解析移动到新线程不起作用,因为回调 upload_cb
returns 和 evhttp_request
并且它的缓冲区在线程仍然想要解析它时被清理。
我倾向于创建一个额外的 event_base 实例来处理无缝上传,如 this question
我一直在网上阅读并找到了一个对我有用的解决方案,主要受此代码启发: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,并且可以根据需要花费任意多的时间。由于状态 url 由第一个事件库处理,它将并行工作并且上传 url 在执行其操作时不会阻止它。
要停止 运行 线程,您应该调用
event_base_loopbreak(event_base2);
然后可能会在您的线程后清理。