ไมโครเซอร์วิสใน C++ นิยายหรือความจริง?

ไมโครเซอร์วิสใน C++ นิยายหรือความจริง?

ในบทความนี้ ฉันจะพูดถึงวิธีที่ฉันสร้างเทมเพลต (cookiecutter) และตั้งค่าสภาพแวดล้อมสำหรับการเขียนบริการ REST API ใน C++ โดยใช้ docker/docker-compose และ conan package manager

ในช่วงแฮ็กกาธอนครั้งต่อไป ซึ่งฉันเข้าร่วมในฐานะนักพัฒนาแบ็กเอนด์ คำถามก็เกิดขึ้นเกี่ยวกับสิ่งที่จะใช้ในการเขียนไมโครเซอร์วิสถัดไป ทุกสิ่งที่เขียนจนถึงตอนนี้ถูกเขียนโดยฉันและฉัน สหาย ใน Python เนื่องจากเพื่อนร่วมงานของฉันเป็นผู้เชี่ยวชาญในสาขานี้และพัฒนาแบ็กเอนด์อย่างมืออาชีพ ในขณะที่โดยทั่วไปฉันเป็นนักพัฒนาระบบฝังตัวและเขียนด้วย C++ ที่ยอดเยี่ยมและแย่มาก และฉันเพิ่งเรียน Python ที่มหาวิทยาลัย

ดังนั้นเราจึงต้องเผชิญกับงานเขียนบริการที่มีภาระงานสูง งานหลักคือการประมวลผลข้อมูลที่มาถึงล่วงหน้าและเขียนลงในฐานข้อมูล และหลังจากพักควันอีกครั้ง เพื่อนคนหนึ่งแนะนำให้ฉันในฐานะนักพัฒนา C++ เขียนบริการนี้โดยใช้ผู้เชี่ยวชาญ การโต้แย้งคือมันจะเร็วขึ้น มีประสิทธิผลมากขึ้น และโดยทั่วไป คณะกรรมการจะยินดีกับวิธีที่เรารู้วิธีจัดการทรัพยากรของทีม ซึ่งฉันตอบว่าฉันไม่เคยทำสิ่งเหล่านี้ในภาษา C++ และสามารถอุทิศเวลาที่เหลือมากกว่า 20 ชั่วโมงในการค้นหา คอมไพล์ และลิงก์ไลบรารีที่เหมาะสมได้อย่างง่ายดาย พูดง่ายๆ ก็คือ ฉันอกหัก นั่นคือสิ่งที่เราตัดสินใจและทำทุกอย่างใน Python อย่างใจเย็น

ตอนนี้ ระหว่างการบังคับแยกตัวเอง ฉันตัดสินใจหาวิธีเขียนบริการในภาษา C++ สิ่งแรกที่ต้องทำคือตัดสินใจเลือกห้องสมุดที่เหมาะสม ทางเลือกของฉันล้มลง POCOเนื่องจากมันถูกเขียนในรูปแบบเชิงวัตถุและยังมีเอกสารประกอบตามปกติอีกด้วย นอกจากนี้ ยังเกิดคำถามเกี่ยวกับการเลือกระบบการประกอบอีกด้วย จนถึงจุดนี้ ฉันทำงานกับ Visual Studio, IAR และ makefiles เปล่าเท่านั้น และไม่มีระบบใดที่ดึงดูดใจฉันเลย เนื่องจากฉันวางแผนที่จะใช้บริการทั้งหมดในคอนเทนเนอร์นักเทียบท่า จากนั้นฉันก็ตัดสินใจลองหา cmake และตัวจัดการแพ็คเกจที่น่าสนใจ conan. ตัวจัดการแพ็คเกจนี้อนุญาตให้คุณลงทะเบียนการขึ้นต่อกันทั้งหมดในไฟล์เดียว

conanfile.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 แล้ว) ซึ่งหมายความว่าคุณจะต้องมองหาห้องสมุดอื่น

จากนั้นตัวเลือกของฉันก็ตกอยู่ที่ห้องสมุดที่ได้รับความนิยมน้อยกว่า libpg. และฉันก็โชคดีมาก ที่มันอยู่ในโคนันแล้ว และกำลังประกอบอยู่ด้วยซ้ำ

ขั้นตอนต่อไปคือการเขียนเทมเพลตบริการที่สามารถประมวลผลคำขอได้
เราต้องสืบทอดคลาส TemplateServerApp ของเราจาก Poco::Util::ServerApplication และแทนที่เมธอดหลัก

เทมเพลตเซิร์ฟเวอร์แอป

#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;
}

ในวิธีการหลัก เราต้องตั้งค่าพารามิเตอร์: พอร์ต จำนวนเธรด และขนาดคิว และที่สำคัญที่สุด คุณต้องระบุตัวจัดการสำหรับคำขอที่เข้ามา ทำได้โดยการสร้างโรงงาน

TemplateRequestHandlerFactory

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

ในกรณีของฉัน มันเพียงแค่สร้างตัวจัดการเดียวกันทุกครั้ง - 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 อย่างง่าย เช่น การสร้างตาราง ก็มีวิธีการหนึ่ง ExecuteSQL(). สำหรับการสืบค้นหรือการเรียกข้อมูลที่ซับซ้อนมากขึ้น คุณจะต้องได้รับการเชื่อมต่อผ่านทาง รับการเชื่อมต่อ() และใช้ 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 install ” ยังไม่เพียงพอ แต่ยังต้องผ่านพารามิเตอร์ -sคอมไพเลอร์.libcxx=libstdc++11 ด้วย ไม่เช่นนั้น คุณเสี่ยงที่จะได้รับข้อผิดพลาดมากมายในขั้นตอนการเชื่อมโยงแอปพลิเคชันของคุณ ฉันติดอยู่กับข้อผิดพลาดนี้มาหลายชั่วโมงแล้ว และหวังว่าบทความนี้จะช่วยให้ผู้อื่นแก้ไขปัญหานี้ได้โดยใช้เวลาน้อยลง

ต่อไป หลังจากเขียน docker-compose.yml ตามคำแนะนำของเพื่อน ฉันได้เพิ่มการสนับสนุน เครื่องตัดคุกกี้ และตอนนี้ คุณสามารถรับเทมเพลตเต็มรูปแบบสำหรับบริการ REST API ใน C++ พร้อมสภาพแวดล้อมที่ปรับแต่งเอง และติดตั้ง PostgreSQL เพียงป้อน “cookiecutter” ลงในคอนโซล https://github.com/KovalevVasiliy/cpp_rest_api_template.git" จากนั้น "นักเทียบท่าเขียน - สร้าง"

ฉันหวังว่าเทมเพลตนี้จะช่วยให้ผู้เริ่มต้นบนเส้นทางที่ยากลำบากในการพัฒนาแอปพลิเคชัน REST API ในภาษาที่ยอดเยี่ยมและทรงพลัง แต่เป็นภาษาที่เงอะงะเช่น C ++
นอกจากนี้ฉันขอแนะนำให้อ่านที่นี่มาก นี้ บทความ. โดยจะอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการทำงานกับ POCO และเขียนบริการ REST API ของคุณเอง

ที่มา: will.com

เพิ่มความคิดเห็น