C++ 中的微服务。 小说还是现实?

C++ 中的微服务。 小说还是现实?

在本文中,我将讨论如何创建模板 (cookiecutter) 并设置环境,以便使用 docker/docker-compose 和 conan 包管理器在 C++ 中编写 REST API 服务。

在我作为后端开发人员参加的下一次黑客马拉松中,出现了关于使用什么来编写下一个微服务的问题。 到目前为止所写的所有内容都是我和我的朋友写的 朋友 我的同事是这个领域的专家,专业开发后端,而我一般是嵌入式系统开发人员,用伟大而可怕的C++编写,而我刚刚在大学学习了Python。

因此,我们面临着编写高负载服务的任务,其主要任务是预处理传入的数据并将其写入数据库。 在又一次冒烟之后,一位朋友建议我,作为一名 C++ 开发人员,使用专业人士来编写此服务。 我认为这样会更快、更有效率,而且总的来说,评审团会对我们如何管理团队资源感到高兴。 我回答说我从来没有在 C++ 中做过这样的事情,并且可以轻松地将剩余的 20 多个小时用于搜索、编译和链接合适的库。 简单地说,我退缩了。 我们就这样决定了,平静地用Python完成了一切。

现在,在强制自我隔离期间,我决定弄清楚如何用 C++ 编写服务。 要做的第一件事是决定一个合适的库。 我的选择落到了 POCO,因为它是用面向对象的风格编写的,并且还拥有正常的文档。 此外,还出现了有关选择装配系统的问题。 到目前为止,我只使用过 Visual Studio、IAR 和裸 makefile。 这些系统都不吸引我,因为我计划在 Docker 容器中运行整个服务。 然后我决定尝试找出 cmake 和一个有趣的包管理器 柯南。 该包管理器允许您在一个文件中注册所有依赖项

柯南文件.txt
[需要]poco/1.9.3
libpq/11.5

[生成器] cmake

并使用一个简单的命令“conan install”。 安装必要的库。 当然,也需要做出改变

CMakeLists.txt

include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})

之后,我开始寻找一个与 PostgreSQL 一起使用的库,因为我几乎没有使用它的经验,而且它也是我们的 Python 服务与之交互的库。 你知道我学到了什么吗? 就在POCO! 但柯南不知道它在 POCO 中,也不知道如何构建它;存储库中有一个过时的配置文件(我已经向 POCO 的创建者写了有关此错误的信息)。 这意味着您将不得不寻找另一个图书馆。

然后我选择了一个不太受欢迎的图书馆 库文件库。 我非常幸运,它已经在柯南中了,甚至正在组装和组装。

下一步是编写一个可以处理请求的服务模板。
我们必须从 Poco::Util::ServerApplication 继承 TemplateServerApp 类并重写 main 方法。

模板服务器应用程序

#pragma once

#include <string>
#include <vector>
#include <Poco/Util/ServerApplication.h>

class TemplateServerApp : public Poco::Util::ServerApplication
{
    protected:
        int main(const std::vector<std::string> &);
};

int TemplateServerApp::main(const vector<string> &)
{
    HTTPServerParams* pParams = new HTTPServerParams;

    pParams->setMaxQueued(100);
    pParams->setMaxThreads(16);

    HTTPServer s(new TemplateRequestHandlerFactory, ServerSocket(8000), pParams);

    s.start();
    cerr << "Server started" << endl;

    waitForTerminationRequest();  // wait for CTRL-C or kill

    cerr << "Shutting down..." << endl;
    s.stop();

    return Application::EXIT_OK;
}

在main方法中我们必须设置参数:端口、线程数和队列大小。 最重要的是,您必须为传入请求指定一个处理程序。 这是通过创建工厂来完成的

模板请求处理工厂

class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
    virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
    {
        return new TemplateServerAppHandler;
    }
};

就我而言,它只是每次创建相同的处理程序 - TemplateServerAppHandler。 这是我们可以放置业务逻辑的地方。

模板服务器应用程序处理程序

class TemplateServerAppHandler : public HTTPRequestHandler
{
public:
    void handleRequest(HTTPServerRequest &req, HTTPServerResponse &resp)
    {
        URI uri(req.getURI());
        string method = req.getMethod();

        cerr << "URI: " << uri.toString() << endl;
        cerr << "Method: " << req.getMethod() << endl;

        StringTokenizer tokenizer(uri.getPath(), "/", StringTokenizer::TOK_TRIM);
        HTMLForm form(req,req.stream());

        if(!method.compare("POST"))
        {
            cerr << "POST" << endl;
        }
        else if(!method.compare("PUT"))
        {
            cerr << "PUT" << endl;
        }
        else if(!method.compare("DELETE"))
        {
            cerr << "DELETE" << endl;
        }

        resp.setStatus(HTTPResponse::HTTP_OK);
        resp.setContentType("application/json");
        ostream& out = resp.send();

        out << "{"hello":"heh"}" << endl;
        out.flush();
    }
};

我还创建了一个类模板来使用 PostgreSQL。 要执行简单的SQL,例如创建表,有一个方法 执行SQL()。 对于更复杂的查询或数据检索,您必须通过以下方式获得连接 获取连接() 并使用 libpg API。 (也许以后我会纠正这个不公正的现象)。

数据库

#pragma once

#include <memory>
#include <mutex>
#include <libpq-fe.h>

class Database
{
public:
    Database();
    std::shared_ptr<PGconn> GetConnection() const;
    bool ExecuteSQL(const std::string& sql);

private:
    void establish_connection();
    void LoadEnvVariables();

    std::string m_dbhost;
    int         m_dbport;
    std::string m_dbname;
    std::string m_dbuser;
    std::string m_dbpass;

    std::shared_ptr<PGconn>  m_connection;
};

连接数据库的所有参数均取自环境,因此还需要创建并配置.env文件

.ENV

DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432

您可以在以下位置查看所有代码 github。

C++ 中的微服务。 小说还是现实?

现在是编写 dockerfile 和 docker-compose.yml 的最后阶段。 说实话,这花了大部分时间,不仅因为我是菜鸟,因为每次都需要重建库,还因为柯南的陷阱。 例如,为了让conan下载、安装并构建必要的依赖项,仅仅下载“conan install.”是不够的,还需要传递-s compiler.libcxx=libstdc++11参数,否则您可能会在应用程序的链接阶段遇到一堆错误。 我已经被这个错误困扰了几个小时,我希望这篇文章能够帮助其他人在更短的时间内解决这个问题。

接下来,写完docker-compose.yml后,根据朋友的建议,我添加了支持 小甜饼 现在,您只需在控制台中输入“cookiecutter”,即可获得 C++ 中的 REST API 服务的成熟模板,其中包含自定义环境并安装了 PostgreSQL https://github.com/KovalevVasiliy/cpp_rest_api_template.git” 然后“docker-compose up —build”。

我希望这个模板能够帮助初学者走上使用 C++ 这样强大但笨拙的语言开发 REST API 应用程序的艰难道路。
另外,我强烈推荐在这里阅读 文章。 它更详细地解释了如何使用 POCO 并编写您自己的 REST API 服务。

来源: habr.com

添加评论