Dalam artikel ini saya akan bercakap tentang cara saya mencipta templat (cookiecutter) dan menyediakan persekitaran untuk menulis perkhidmatan API REST dalam C++ menggunakan docker/docker-compose dan pengurus pakej conan.
Semasa hackathon seterusnya, di mana saya mengambil bahagian sebagai pembangun bahagian belakang, persoalan timbul tentang apa yang perlu digunakan untuk menulis perkhidmatan mikro seterusnya. Semua yang telah ditulis selama ini ditulis oleh saya dan saya
Oleh itu, kami berhadapan dengan tugas untuk menulis perkhidmatan beban tinggi, tugas utamanya adalah untuk memproses terlebih dahulu data yang datang kepadanya dan menulisnya ke pangkalan data. Dan selepas berhenti merokok lagi, seorang rakan mencadangkan saya, sebagai pembangun C++, menulis perkhidmatan ini menggunakan pro. Berhujah ini adalah bahawa ia akan menjadi lebih pantas, lebih produktif, dan secara amnya, juri akan gembira dengan cara kami mengetahui cara menguruskan sumber pasukan. Saya menjawab bahawa saya tidak pernah melakukan perkara sedemikian dalam C++ dan boleh menumpukan baki 20+ jam dengan mudah untuk mencari, menyusun dan memautkan perpustakaan yang sesuai. Ringkasnya, saya mengecewakan. Itulah yang kami putuskan dan dengan tenang menyelesaikan segala-galanya dalam Python.
Sekarang, semasa pengasingan diri secara paksa, saya memutuskan untuk memikirkan cara menulis perkhidmatan dalam C++. Perkara pertama yang perlu dilakukan ialah memutuskan perpustakaan yang sesuai. Pilihan saya jatuh pada
conanfile.txt
[memerlukan]poco/1.9.3
libpq/11.5
dan dengan arahan mudah "conan install ." memasang perpustakaan yang diperlukan. Sememangnya, ia juga perlu untuk membuat perubahan kepada
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})
Selepas itu, saya mula mencari perpustakaan untuk bekerja dengan PostgreSQL, kerana ia adalah perpustakaan yang saya mempunyai sedikit pengalaman bekerja, dan ia juga yang berinteraksi dengan perkhidmatan Python kami. Dan adakah anda tahu apa yang saya pelajari? Ia ada dalam POCO! Tetapi conan tidak tahu bahawa ia berada dalam POCO dan tidak tahu bagaimana untuk membinanya; terdapat fail konfigurasi lapuk dalam repositori (saya telah menulis tentang ralat ini kepada pencipta POCO). Ini bermakna anda perlu mencari perpustakaan lain.
Dan kemudian pilihan saya jatuh pada perpustakaan yang kurang popular
Langkah seterusnya ialah menulis templat perkhidmatan yang boleh memproses permintaan.
Kami mesti mewarisi kelas TemplateServerApp kami daripada Poco::Util::ServerApplication dan mengatasi kaedah utama.
TemplateServerApp
#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;
}
Dalam kaedah utama kita mesti menetapkan parameter: port, bilangan benang dan saiz baris gilir. Dan yang paling penting, anda mesti menentukan pengendali untuk permintaan masuk. Ini dilakukan dengan mewujudkan kilang
TemplateRequestHandlerFactory
class TemplateRequestHandlerFactory : public HTTPRequestHandlerFactory
{
public:
virtual HTTPRequestHandler* createRequestHandler(const HTTPServerRequest & request)
{
return new TemplateServerAppHandler;
}
};
Dalam kes saya, ia hanya mencipta pengendali yang sama setiap kali - TemplateServerAppHandler. Di sinilah kita boleh meletakkan logik perniagaan kita.
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();
}
};
Saya juga mencipta templat kelas untuk berfungsi dengan PostgreSQL. Untuk melaksanakan SQL mudah, seperti mencipta jadual, terdapat kaedah ExecuteSQL(). Untuk pertanyaan yang lebih kompleks atau pengambilan data, anda perlu mendapatkan sambungan melalui GetConnection() dan gunakan API libpg. (Mungkin nanti saya akan betulkan ketidakadilan ini).
Pangkalan Data
#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;
};
Semua parameter untuk menyambung ke pangkalan data diambil daripada persekitaran, jadi anda juga perlu mencipta dan mengkonfigurasi fail .env
.env
DATABASE_NAME=template
DATABASE_USER=user
DATABASE_PASSWORD=password
DATABASE_HOST=postgres
DATABASE_PORT=5432
Anda boleh melihat semua kod di
Dan kini tibalah peringkat akhir untuk menulis fail docker dan docker-compose.yml. Sejujurnya, ini mengambil sebahagian besar masa, dan bukan sahaja kerana saya seorang noob, kerana ia perlu membina semula perpustakaan setiap kali, tetapi kerana perangkap conan. Sebagai contoh, untuk conan memuat turun, memasang dan membina kebergantungan yang diperlukan, ia tidak mencukupi untuk memuat turun βconan install .β, ia juga perlu melepasi parameter -s compiler.libcxx=libstdc++11, jika tidak anda berisiko mendapat banyak ralat pada peringkat pemautan aplikasi anda. Saya telah terperangkap dengan ralat ini selama beberapa jam dan saya harap artikel ini akan membantu orang lain menyelesaikan masalah ini dalam masa yang singkat.
Seterusnya, selepas menulis docker-compose.yml, atas nasihat rakan saya, saya menambah sokongan
Saya berharap templat ini akan membantu pemula dalam laluan sukar mereka untuk membangunkan aplikasi API REST dalam bahasa yang hebat dan berkuasa, tetapi bahasa yang kekok seperti C++.
Juga, saya sangat mengesyorkan membaca di sini
Sumber: www.habr.com