多进程化

优质
小牛编辑
134浏览
2023-12-01

mongols提供的所有服务器设施既可以多线程化也可以多进程化。

并且支持在多进程化的同时多线程化。

来看代码:


#include <mongols/util.hpp>
#include <mongols/web_server.hpp>
#include <sys/prctl.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <unistd.h>

#include <algorithm>
#include <functional>
#include <iostream>

int main(int, char**)
{
    //    daemon(1, 0);
    auto f = [](const mongols::request& req) {
        if (req.method == "GET" && req.uri.find("..") == std::string::npos) {
            return true;
        }
        return false;
    };
    int port = 9090;
    const char* host = "127.0.0.1";
    mongols::web_server
        server(host, port, 5000, 512000, 0);
    server.set_root_path("localhost:9090","html");
    server.set_root_path("127.0.0.1:9090","html");
    server.set_mime_type_file("html/mime.conf");
    server.set_list_directory(true);
    server.set_enable_mmap(true);
    //    if (!server.set_openssl("openssl/localhost.crt", "openssl/localhost.key")) {
    //        return -1;
    //    }

    std::function<void(pthread_mutex_t*, size_t*)> ff = [&](pthread_mutex_t* mtx, size_t* data) {
        server.set_shutdown([&]() {
            std::cout << "process " << getpid() << " exit.\n";
        });
        server.run(f);
    };

    std::function<bool(int)> g = [&](int status) {
        return false;
    };

    mongols::multi_process main_process;
    main_process.run(ff, g);
}

多进程化最合适的场景是web_server,可以显著提升性能:

nginx_4_worker.png mongols_4_worker.png nginx_vs_mongols.png

以上测试使用4个工作进程,对比于使用同样数目工作进程的nginx,更胜一筹。实际上,在同等使用单进程的情况下,mongols也比nginx更快更稳定!nginx 也是一朵浮云。

子进程重启

如需子进程重启,可向管理进程发送SIGHUP信号。当然,如果子进程非正常终止,例如被killed,子进程会自动重启。

注意

虽然mongols所包含的所有server均可以如上例所示进行多进程化从而提升性能,但是因为其中的一些server内部包含了leveldb或sqlite组件,而这两种组件只支持多线程化,并不支持多进程化,所有在对这些server进行多进程化时,ff函数应该包含一些配置数据库的代码,使得不同工作进程绑定不同的数据库。具体实现可参考example

这一点非常重要。

不过话说回来,mongols提供的服务器在单进程情况下,性能已经非常优越了。在多数情况下,无需做多进程化即可满足——还能避免无谓的复杂代码。

多进程化其他网络库

mongols配备的多进程化工具是通用的,可用于其他网络库的多进程化。这里给出一个libevent的例子,其实只需在这里中的libevent例子里稍微添加几行代码:


#include <cstring>
#include <event2/buffer.h>
#include <event2/event.h>
#include <event2/http.h>
#include <iostream>
#include <mongols/util.hpp>
#include <signal.h>

static struct event_config* server_config = 0;
static struct event_base* server_event = 0;
static struct evhttp* server = 0;
static const char* host = "127.0.0.1";
static int port = 8080;
static inline void signal_normal_cb(int sig);
static inline void generic_request_handler(struct evhttp_request* req, void* arg);

int main(int, char**)
{
    server_config = event_config_new();
    event_config_set_flag(server_config, EVENT_BASE_FLAG_NOLOCK);
    event_config_set_flag(server_config, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST);

    server_event = event_base_new_with_config(server_config);
    server = evhttp_new(server_event);

    evhttp_bind_socket(server, host, port);

    evhttp_set_gencb(server, generic_request_handler, NULL);
    evhttp_set_default_content_type(server, "text/plain;charset=UTF-8");
    evhttp_set_timeout(server, 60);

    std::function<void(pthread_mutex_t*, size_t*)> ff = [&](pthread_mutex_t* mtx, size_t* data) {
        signal(SIGHUP, signal_normal_cb);
        signal(SIGTERM, signal_normal_cb);
        signal(SIGINT, signal_normal_cb);
        signal(SIGQUIT, signal_normal_cb);

        event_base_dispatch(server_event);
        evhttp_free(server);
        event_base_free(server_event);
        event_config_free(server_config);
    };

    std::function<bool(int)> g = [&](int status) {
        return false;
    };

    mongols::multi_process main_process;
    main_process.run(ff, g);

    return 0;
}

static inline void signal_normal_cb(int sig)
{
    struct timeval delay = { 1, 0 };
    switch (sig) {
    case SIGTERM:
    case SIGHUP:
    case SIGQUIT:
    case SIGINT:
        if (server_event && !event_base_loopexit(server_event, &delay)) {
        }
        break;
    }
}
static inline void generic_request_handler(struct evhttp_request* ev_req, void* arg)
{
    struct evbuffer* ev_res = evhttp_request_get_output_buffer(ev_req);
    struct evkeyvalq *ev_output_headers = evhttp_request_get_output_headers(ev_req),
                     *ev_input_headers = evhttp_request_get_input_headers(ev_req);
    const struct evhttp_uri* ev_uri = evhttp_request_get_evhttp_uri(ev_req);

    evhttp_add_header(ev_output_headers, "Server", "libevent2");
    evbuffer_add(ev_res, "hello,world", 11);
    evhttp_send_reply(ev_req, 200, "OK", ev_res);
}

该例子像mongols一样,也支持子进程重启!

多进程化的libevent例子自然会有更好的性能表现。但是,libevent在单进程下的性能极限只有这么多——所以即使已经多进程化,该例子的吞吐率还是没有赶上单进程的mongols的http_server例子,实际上还差不少呢!