سأتحدث في هذه المقالة عن كيفية إنشاء قالب (مقطع ملفات تعريف الارتباط) وإعداد بيئة لكتابة خدمة REST API في لغة C++ باستخدام docker/docker-compose ومدير حزم conan.
خلال الهاكاثون التالي، الذي شاركت فيه كمطور للواجهة الخلفية، نشأ سؤال حول ما يجب استخدامه لكتابة الخدمة المصغرة التالية. كل ما كتب حتى الآن كتبه أنا وصديقي
لذلك، واجهنا مهمة كتابة خدمة عالية التحميل، وكانت المهمة الرئيسية لها هي المعالجة المسبقة للبيانات الواردة إليها وكتابتها في قاعدة البيانات. وبعد استراحة أخرى، اقترح أحد الأصدقاء أن أقوم، كمطور C++، بكتابة هذه الخدمة باستخدام المحترفين. ويجادل هذا بأن الأمر سيكون أسرع وأكثر إنتاجية، وبشكل عام، ستكون لجنة التحكيم سعيدة بكيفية معرفتنا لكيفية إدارة موارد الفريق. وأجبت عليه أنني لم أفعل مثل هذه الأشياء مطلقًا في لغة C++ ويمكنني بسهولة تخصيص ما يزيد عن 20 ساعة المتبقية للبحث عن المكتبات المناسبة وتجميعها وربطها. بكل بساطة، لقد شعرت بالخوف. هذا ما قررناه وأكملنا كل شيء في بايثون بهدوء.
الآن، أثناء العزلة الذاتية القسرية، قررت معرفة كيفية كتابة الخدمات بلغة C++. أول شيء يجب فعله هو تحديد المكتبة المناسبة. وقع خياري
conanfile.txt
[يتطلب] بوكو/1.9.3
ليببك/11.5
وبأمر بسيط "conan install". تثبيت المكتبات اللازمة. وبطبيعة الحال، كان من الضروري أيضا إجراء تغييرات على
CMakeLists.txt
include(build/conanbuildinfo.cmake)
conan_basic_setup()
target_link_libraries(<target_name> ${CONAN_LIBS})
بعد ذلك، بدأت البحث عن مكتبة للعمل مع PostgreSQL، نظرًا لأنها كانت المكتبة التي لم تكن لدي خبرة كبيرة في العمل معها، وكانت أيضًا المكتبة التي تفاعلت معها خدمات Python الخاصة بنا. وهل تعرف ماذا تعلمت؟ إنه في POCO! لكن كونان لا يعرف أنه موجود في POCO ولا يعرف كيفية بنائه، يوجد ملف تكوين قديم في المستودع (لقد كتبت بالفعل عن هذا الخطأ إلى منشئي POCO). هذا يعني أنه سيتعين عليك البحث عن مكتبة أخرى.
ثم وقع اختياري على مكتبة أقل شعبية
وكانت الخطوة التالية هي كتابة قالب خدمة يمكنه معالجة الطلبات.
يجب أن نرث فئة TemplateServerApp الخاصة بنا من Poco::Util::ServerApplication وتجاوز الطريقة الرئيسية.
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;
}
في الطريقة الرئيسية يجب علينا تعيين المعلمات: المنفذ وعدد المواضيع وحجم قائمة الانتظار. والأهم من ذلك، يجب عليك تحديد معالج للطلبات الواردة. ويتم ذلك عن طريق إنشاء مصنع
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 بسيط، مثل إنشاء جدول، هناك طريقة تنفيذSQL(). بالنسبة للاستعلامات أو استرجاع البيانات الأكثر تعقيدًا، سيتعين عليك الحصول على اتصال عبر جيتكونيكشن () واستخدم واجهة برمجة تطبيقات libpg. (ربما سأصحح هذا الظلم لاحقًا).
قاعدة البيانات
#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
يمكنك رؤية كل الكود على
والآن تأتي المرحلة الأخيرة من كتابة ملف dockerfile وdocker-compose.yml. لأكون صادقًا، استغرق هذا معظم الوقت، ليس فقط لأنني مستجد، لأنه كان من الضروري إعادة بناء المكتبات في كل مرة، ولكن بسبب مخاطر كونان. على سبيل المثال، لكي يتمكن conan من تنزيل التبعيات الضرورية وتثبيتها وبناءها، لا يكفي تنزيل "conan install ." أنت تخاطر بالحصول على مجموعة من الأخطاء في مرحلة ربط طلبك. لقد واجهت هذا الخطأ لعدة ساعات وآمل أن تساعد هذه المقالة الأشخاص الآخرين على حل هذه المشكلة في وقت أقل.
بعد ذلك، بعد كتابة docker-compose.yml، قمت بإضافة الدعم بناءً على نصيحة صديقي
آمل أن يساعد هذا القالب المبتدئين في طريقهم الصعب لتطوير تطبيقات REST API بلغة رائعة وقوية، ولكن خرقاء مثل C++.
كما أنصح بشدة بالقراءة هنا
المصدر: www.habr.com