-ѕоиск по дневнику

ѕоиск сообщений в predefglas

 -ѕодписка по e-mail

 

 -—татистика

—татистика LiveInternet.ru: показано количество хитов и посетителей
—оздан: 10.12.2014
«аписей:
 омментариев:
Ќаписано: 94

»спользование механизма SRR в приложени€х разработанных на Qt дл€ QNX

ƒневник

¬торник, 19 »юл€ 2016 г. 20:31 + в цитатник
‘реймворк Qt один из самых попул€рных и примен€емых при разработке кроссплатформенных настольных и мобильных приложений. Ёта попул€рность не могла рано или поздно не привести к использованию Qt в системах специального и ответственного назначени€. ƒостаточно давно существует возможность разработки на Qt дл€ QNX Neutrino. Ѕиблиотека Qt поддерживает платформу QNX, а среда разработки Qt Creator обеспечивает взаимодействие с системами на QNX. ќднако QNX, как система в том числе и дл€ встраиваемых решений, имеет в своЄм составе технологии, которые не требуютс€, а потому и отсутствуют в системах общего назначени€.  лючева€ дл€ ќ—–¬ QNX функциональность, на которой построена сама система и на которую нередко опираютс€ пользовательские задачи это передача сообщений. ќб особенност€х применени€ механизма SRR (Send/Receive/Replay), как ещЄ называют передачу сообщений в QNX, и о разработке двух примеров Qt-приложений Ч клиента и сервера Ч € и хотел бы рассказать сегодн€. ¬ заметке не делаетс€ каких-то открытий, предлагаетс€ в общем-то известна€ информаци€. “ем не менее Qt это относительно новый фреймворк дл€ разработчиков систем специального назначени€, где исторически наблюдаетс€ некотора€ инертность при внедрении новых технологий. –азработчики систем на QNX могут быть недостаточно знакомы с тонкост€ми Qt, а разработчики Qt-приложений зачастую могут не знать специфику QNX. –ешение задачи использовани€ в одном проекте и графических возможностей библиотеки Qt, и специфичных дл€ QNX технологий может потребовать затраты усилий, особенно на первых этапах. »менно поэтому и по€вилась данна€ заметка, цель которой в одном месте собрать информацию, котора€ может понадобитьс€ разработчикам при использовании Qt в QNX. “иповой пример использовани€ SRR в QNX ѕоскольку о сообщени€х QNX € уже писал раньше на ’абре, в том числе и о составных сообщени€х, то будем считать, что теори€ в каком-то виде уже известна и можно переходить к практике. ѕоэтому привожу ниже исходный код приложени€ клиента: qnx_client.c//////////////////////////////////////////////////////////////////////////////// // qnx_client.c // // demonstrates using input/output vector (IOV) messaging // //////////////////////////////////////////////////////////////////////////////// #include <string.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include "../iov_server.h" int main(int argc, char* argv[]) { int coid; // Connection ID to server cksum_header_t hdr; // msg header will specify how many bytes of data will follow int incoming_checksum; // space for server's reply int status; // status return value iov_t siov[2]; // create a 2 part iov if ( 2 d\n"); exit(EXIT_FAILURE); } // locate the server coid = name_open(CKSUM_SERVER_NAME, 0); if ( -1 > ѕрограмма довольно тривиальна€, € просто вз€л пример из курсов по QNX и немного его Ђпричесалї. Ёто консольное приложение, которое принимает на вход строку, пересылает еЄ на сервер и выводит на экран ответ сервера Ч контрольную сумму переданной ранее строки. ќбратите внимание, что в примере используютс€ составные сообщени€ Ч макрос SETIOV() и функци€ MsgSendvs() вместо MsgSend() Ч что позвол€ет избежать лишнего копировани€. —амое интересное здесь это использование функции name_open() дл€ поиска сервера и установлени€ соединени€ с ним. “еперь самое врем€ посмотреть исходный код сервера: qnx_server.c//////////////////////////////////////////////////////////////////////////////// // qnx_server.c // // demonstrates using input/output vector (IOV) messaging // //////////////////////////////////////////////////////////////////////////////// #include <stdio.h> #include <stdlib.h> #include "../iov_server.h" typedef union { uint16_t msg_type; struct _pulse pulse; cksum_header_t cksum_hdr; } msg_buf_t; int calculate_checksum(char *text) { char *c; int cksum = 0; for ( c = text; *c; c++ ) cksum >  од сервера немного поинтереснее. —ервер принимает и обрабатывает сообщени€ от клиента. Ќа самом деле в этом примере реализовано только одно сообщение Ч CKSUM_MSG_TYPE Ч подсчЄт контрольной суммы переданных данных. ƒругое сообщение Ч _IO_CONNECT Ч посылаетс€ серверу, когда клиент вызывает функцию name_open(). ѕомимо сообщений сервер умеет обрабатывать служебные импульсы _PULSE_CODE_DISCONNECT и _PULSE_CODE_UNBLOCK. ¬ этом простом примере обработка служебных сообщений в принципе не требуетс€. јлгоритм работы сервера достаточно прост. —начала выполн€етс€ инициализаци€, в данном случае это объ€вление имени при помощи функции name_attach(), после чего клиенты могут найти сервер. ƒальнейша€ работа сервера представл€ет собой Ђвечный циклї. ¬ самом начале цикла сервер блокируетс€ на вызове MsgReceive() ожида€ сообщений от клиента. ѕо приходу сообщени€ или пульса €дро QNX разблокирует сервер, который начнЄт обработку прин€того сообщени€. ¬ примере используетс€ объединение (union) msg_buf_t дл€ получени€ сообщени€. Ёто обычна€ практика дл€ QNX, когда возможные типы сообщений (а сообщение обычно описываютс€ структурой €зыка —и) объедин€ютс€ в union. Ќаше полезное сообщение CKSUM_MSG_TYPE мы получаем при помощи MsgReceive() не целиком, принимаетс€ только заголовок, в котором указан размер данных. ƒанные дочитываютс€ при помощи функции MsgRead(). ќтвет клиенту отправл€етс€ при помощи функции MsgReply(), а в случае ошибки Ч MsgError(). »мпульсы не требуют ответа. ƒл€ полноты картины привожу текст заголовочного файла. Ётот заголовочный файл используетс€ и сервером, и клиентом, и, как мы увидим дальше, Qt версии нашего сервера и клиента тоже используют этот файл. ќн предназначен дл€ подключени€ необходимых заголовочных файлов и объ€влени€ структуры заголовка сообщени€ CKSUM_MSG_TYPE. iov_server.h#ifndef _IOV_SERVER_H_ #define _IOV_SERVER_H_ #include <sys/dispatch.h> #include <sys/neutrino.h> #include <sys/iomsg.h> #include <errno.h> #define CKSUM_SERVER_NAME "cksum" #define CKSUM_MSG_TYPE (_IO_MAX + 2) typedef struct { uint16_t msg_type; unsigned data_size; } cksum_header_t; // checksum reply is an int #endif //_IOV_SERVER_H_ Ќа скриншоте ниже представлен пример работы консольных версий сервера и клиента: —начала запускаетс€ сервер, который ожидает сообщений от клиента. ѕотом запускаетс€ клиент, в качестве аргумента ему указываетс€ строка ЂHello, QNX!ї ¬о врем€ работы клиент и сервер вывод€т диагностические сообщени€ в консоль, по которым можно судить о работе программ. ѕрограммы работают как ожидалось, можно приступать к написанию графических вариантов на Qt. —начала адаптируем клиентское приложение. ѕример клиента на Qt –азрабатывать Qt приложени€ будем в Qt Creator. ¬ этом случае сам процесс разработки приложений дл€ QNX в общем не отличаетс€ от разработки приложений дл€ других ќ—. ¬едь Qt это кроссплатформенный фреймворк. “ребуетс€ только создать комплект (Kit) дл€ QNX в Qt Creator. —оздаЄм новый проект приложени€ типа Qt Widgets Application. ѕри этом Qt Creator подготовит все необходимые файлы, в том числе и форму дл€ окна. ƒл€ клиента форму окна приводим к следующему виду: Ќа форме размещены поле дл€ ввода текста (text), который передаЄтс€ серверу, кнопки подключени€ (connect) и отключени€ (disconnect) от сервера, кнопка (calc) отправки сообщени€ серверу, поле ввода (cksum), которое используетс€ дл€ вывода контрольной суммы полученной от сервера, и область вывода диагностических сообщений (status). ќсталось только написать код дл€ работы с сервером и обработки логики графической формы. ¬ результате получаем следующий класс MainWindow: mainwindow.h#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "../iov_server.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); public slots: void log(QString msg); void showCrc(QString crc); private slots: void qnxConnect(); void qnxDisconnect(); void calculate(); private: Ui::MainWindow *ui; int coid; // Connection ID to server }; #endif // MAINWINDOW_H mainwindow.cpp#include "mainwindow.h" #include "ui_mainwindow.h" #include <QDateTime> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); coid = -1; connect(ui->connect, SIGNAL(clicked()), this, SLOT(qnxConnect())); connect(ui->disconnect, SIGNAL(clicked()), this, SLOT(qnxDisconnect())); connect(ui->calc, SIGNAL(clicked()), this, SLOT(calculate())); } MainWindow::~MainWindow() { delete ui; } void MainWindow::qnxConnect() { // check if we already connected if ( coid t connect to server: ")).append(strerror(errno))); return; } log(tr("Connected to server")); ui->connect->setEnabled(false); ui->disconnect->setEnabled(true); ui->calc->setEnabled(true); } void MainWindow::qnxDisconnect() { // check if we already disconnected if ( coid < 0 ) return; // disconnect from the server int status = name_close(coid); if ( status < 0 ) { log(QString(tr("Can't disconnect from server: ")).append(strerror(errno))); return; } log(tr("Disconnected from server")); coid = -1; ui->calc->setEnabled(false); ui->disconnect->setEnabled(false); ui->connect->setEnabled(true); } void MainWindow::calculate() { ui->disconnect->setEnabled(false); ui->calc->setEnabled(false); // get the data QString data = ui->text->toPlainText(); log(QString(tr("Sending the following text to checksum server: %1")).arg(data)); // build the header cksum_header_t hdr; // msg header will specify how many bytes of data will follow hdr.msg_type = CKSUM_MSG_TYPE; hdr.data_size = data.length() + 1; // setup the message as a two part iov, first the header then the data iov_t siov[2]; // create a 2 part iov SETIOV(&siov[0], &hdr, sizeof(hdr)); SETIOV(&siov[1], data.toAscii().data(), hdr.data_size); // and send the message off to the server int incoming_checksum; // space for server's reply int status = MsgSendvs(coid, siov, 2, &incoming_checksum, sizeof(incoming_checksum)); if ( status < 0 ) { log(QString(tr("Can't send message to server: ")).append(strerror(errno))); return; } log(QString(tr("MsgSend return status: %1")).arg(status)); showCrc(QString::number(incoming_checksum)); } void MainWindow::showCrc(QString crc) { ui->cksum->setText(crc); ui->disconnect->setEnabled(true); ui->calc->setEnabled(true); } void MainWindow::log(QString msg) { ui->status->append(msg.prepend(QDateTime::currentDateTime().toString("hh:mm:ss "))); } ‘айл main.cpp осталс€ таким, каким его создал Qt Creator, поэтому приводить его содержимое не стану. »так, посмотрим, что же мы тут понаделали. —начала, как и в прошлом примере, запускаем сервер. «атем запускаем Qt версию клиента. Ќажимаем кнопку Connect, обращаем внимание, что сервер получает уведомление о подключении клиента в виде сообщени€ _IO_CONNECT. «атем пишем текст ЂHello, QNX!ї и нажимаем кнопку Calc, что приводит к отправке сообщени€ на на сервер. —обытие отправки также отображаетс€ на экране. ѕолученна€ от сервера контрольна€ сумма отображаетс€ в окне клиента. ѕример работает, сообщени€ отправл€ютс€ и принимаютс€, проблем не замечено. ЌоЕ Ќо € то знаю, что всЄ не должно так замечательно работать. ƒело в том, что после вызова MsgSendvs() клиент блокируетс€ как минимум до вызова сервером функции MsgReceive() (моет быть больше, если в системе есть более высокоприоритетные процессы). ƒл€ иллюстрации этой особенности в коде функции calculate_checksum() сервера добавлена задержка в виде вызова sleep(10). — такой задержкой в сервере клиент блокируетс€ на 10 секунд, что приводит к заметному Ђзамерзаниюї графического окна сервера. ¬ некоторых случа€х, особенно когда сервер сразу отвечает клиенту (т.е. информаци€ всегда доступна серверу, а не приходит извне), блокировка не €вл€етс€ проблемой. ¬ остальных случа€х, пользователь может начать нервничать, когда у него Ђзамерзаетї графический интерфейс. я бы не стал рисковать и выпускать программы, которые могут нервировать заказчиков. — ЂзамЄрзшимї интерфейсом клиент не сможет продолжить работу с приложением после отправки сообщени€ до получени€ ответа от сервера, а ведь в реальной жизни приложение может взаимодействовать с несколькими серверами и предоставл€ть другие функции управлени€. Ќет, текущий вариант клиентского приложени€ нас не может устроить. ѕоэтому давайте посмотрим на правильную реализацию клиента. ѕравильный пример клиента на Qt  ак же можно решить проблему с блокировкой клиента?  лиент не может не блокироватьс€ на MsgSendvs(). ќднако вполне допустимо выделить работу с сообщени€ми в отдельный поток. ¬ этом случае один поток обслуживает графический интерфейс, другой Ч реализует механизм SRR. ƒл€ работы с потоками в Qt будем использовать класс QThread. –еализацию SRR вынесем в отдельный класс Sender. —в€зь между классами Sender (работа с сообщени€ми) и MainWindow (графический интерфейс) организуем через сигналы и слоты Qt. ѕосмотрим, как изменилс€ класс MainWindow с учЄтом вышесказанного. ƒл€ нагл€дности старый код также оставлен, и добавлен макрос SENDER_THREAD, при объ€влении которого работа с сообщени€ми выполн€етс€ в отдельном потоке Qt. mainwindow.h#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include "../iov_server.h" #define SENDER_THREAD #ifdef SENDER_THREAD #include <QThread> #endif namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); #ifdef SENDER_THREAD signals: void calcCrc(int coid, QString data); #endif public slots: void log(QString msg); void showCrc(QString crc); private slots: void qnxConnect(); void qnxDisconnect(); void calculate(); private: Ui::MainWindow *ui; int coid; // Connection ID to server #ifdef SENDER_THREAD QThread senderThread; #endif }; #endif // MAINWINDOW_H mainwindow.cpp#include "mainwindow.h" #include "ui_mainwindow.h" #ifdef SENDER_THREAD #include "sender.h" #endif #include <QDateTime> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); coid = -1; connect(ui->connect, SIGNAL(clicked()), this, SLOT(qnxConnect())); connect(ui->disconnect, SIGNAL(clicked()), this, SLOT(qnxDisconnect())); connect(ui->calc, SIGNAL(clicked()), this, SLOT(calculate())); #ifdef SENDER_THREAD Sender *sender = new Sender; sender->moveToThread(&senderThread); connect(&senderThread, SIGNAL(finished()), sender, SLOT(deleteLater())); connect(this, SIGNAL(calcCrc(int, QString)), sender, SLOT(send(int, QString))); connect(sender, SIGNAL(result(QString)), this, SLOT(showCrc(QString))); connect(sender, SIGNAL(log(QString)), this, SLOT(log(QString))); senderThread.start(); #endif } MainWindow::~MainWindow() { #ifdef SENDER_THREAD senderThread.quit(); senderThread.wait(); #endif delete ui; } void MainWindow::qnxConnect() { // check if we already connected if ( coid t connect to server: ")).append(strerror(errno))); return; } log(tr("Connected to server")); ui->connect->setEnabled(false); ui->disconnect->setEnabled(true); ui->calc->setEnabled(true); } void MainWindow::qnxDisconnect() { // check if we already disconnected if ( coid < 0 ) return; // disconnect from the server int status = name_close(coid); if ( status < 0 ) { log(QString(tr("Can't disconnect from server: ")).append(strerror(errno))); return; } log(tr("Disconnected from server")); coid = -1; ui->calc->setEnabled(false); ui->disconnect->setEnabled(false); ui->connect->setEnabled(true); } void MainWindow::calculate() { ui->disconnect->setEnabled(false); ui->calc->setEnabled(false); // get the data QString data = ui->text->toPlainText(); #ifdef SENDER_THREAD emit calcCrc(coid, data); #else log(QString(tr("Sending the following text to checksum server: %1")).arg(data)); // build the header cksum_header_t hdr; // msg header will specify how many bytes of data will follow hdr.msg_type = CKSUM_MSG_TYPE; hdr.data_size = data.length() + 1; // setup the message as a two part iov, first the header then the data iov_t siov[2]; // create a 2 part iov SETIOV(&siov[0], &hdr, sizeof(hdr)); SETIOV(&siov[1], data.toAscii().data(), hdr.data_size); // and send the message off to the server int incoming_checksum; // space for server's reply int status = MsgSendvs(coid, siov, 2, &incoming_checksum, sizeof(incoming_checksum)); if ( status < 0 ) { log(QString(tr("Can't send message to server: ")).append(strerror(errno))); return; } log(QString(tr("MsgSend return status: %1")).arg(status)); showCrc(QString::number(incoming_checksum)); #endif } void MainWindow::showCrc(QString crc) { ui->cksum->setText(crc); ui->disconnect->setEnabled(true); ui->calc->setEnabled(true); } void MainWindow::log(QString msg) { ui->status->append(msg.prepend(QDateTime::currentDateTime().toString("hh:mm:ss "))); } ¬ объ€влении класса MainWindow по€вилс€ сигнал calcCrc(), при помощи которого сообщаемс€ экземпл€ру класса Sender кому и какое сообщение требуетс€ отправить. Ѕольшие изменени€ претерпела реализаци€ класса MainWindow. ¬ конструкторе по€вилс€ блок кода, в котором создаЄтс€ экземпл€р класса Sender и при помощи метода moveToThread() выдел€етс€ в отдельный поток. ¬ деструкторе ожидаем завершение потока (методы quit() и wait() класса QThread). ¬есь код метода calculate() перенесЄн в класс Sender и заменЄн на генерацию сигнала calcCrc(). ѕосле доработки MainWindow, можно перейти к классу Sender. sender.h#ifndef SENDER_H #define SENDER_H #include <QObject> #include "../iov_server.h" class Sender : public QObject { Q_OBJECT public: Sender() {} virtual ~Sender() {} signals: void result(QString data); void log(QString err); public slots: void send(int coid, QString data); }; #endif // SENDER_H sender.cpp#include "sender.h" void Sender::send(int coid, QString data) { emit log(QString(tr("Sending the following text to checksum server: %1")).arg(data)); // build the header cksum_header_t hdr; // msg header will specify how many bytes of data will follow hdr.msg_type = CKSUM_MSG_TYPE; hdr.data_size = data.length() + 1; // setup the message as a two part iov, first the header then the data iov_t siov[2]; // create a 2 part iov SETIOV(&siov[0], &hdr, sizeof(hdr)); SETIOV(&siov[1], data.toAscii().data(), hdr.data_size); // and send the message off to the server int incoming_checksum; // space for server's reply int status = MsgSendvs(coid, siov, 2, &incoming_checksum, sizeof(incoming_checksum)); if ( status < 0 ) { emit log(QString(tr("Can't send message to server: ")).append(strerror(errno))); return; } emit log(QString(tr("MsgSend return status: %1")).arg(status)); emit result(QString::number(incoming_checksum)); } ѕо сути это код, который был раньше в методе calculate() класса MainWindow. ¬ывод ошибок и результата в графическое окно приложени€ клиента реализован при помощи сигналов log() и result(). — такими доработками графический интерфейс клиента не Ђзамерзаетї, т.е. пока экземпл€р класса Sender блокируетс€ на 10 секунд в отдельном потоке, мы можем управл€ть графическим окном. ѕравда в представленном примере управл€ть особо нечем, но возможность то есть. ѕример сервера на Qt ѕоэксперементировав с клиентом будем сразу разрабатывать сервер правильно. ѕоскольку вызов MsgReceive() приводит к блокировке, то вынесем функциональность сервера в класс Server, который будет работать в отдельном потоке. ѕринципы те же, что и в клиенте. ‘орму главного окна по-честному Ђскомуниздимї у клиента Ч скопируем mainwindow.ui, откроем в редакторе, удалим ненужные кнопки и преобразуем класс QPlainTextEdit (объект text) в QTextBrowser (редактор это позвол€ет). ќбъ€вление и реализаци€ класса MainWindow сервера приведены ниже: mainwindow.h#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QThread> #include "../iov_server.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); signals: void startServer(name_attach_t* attach); public slots: void log(QString msg); void showCrc(QString crc); void showText(QString txt); void stopServer(void); private: Ui::MainWindow *ui; name_attach_t* attach; QThread serverThread; }; #endif // MAINWINDOW_H mainwindow.cpp#include "mainwindow.h" #include "ui_mainwindow.h" #include "server.h" #include <QDateTime> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); Server *server = new Server; server->moveToThread(&serverThread); connect(&serverThread, SIGNAL(finished()), server, SLOT(deleteLater())); connect(this, SIGNAL(startServer(name_attach_t*)), server, SLOT(process(name_attach_t*))); connect(server, SIGNAL(result(QString)), this, SLOT(showCrc(QString))); connect(server, SIGNAL(text(QString)), this, SLOT(showText(QString))); connect(server, SIGNAL(log(QString)), this, SLOT(log(QString))); attach = name_attach(NULL, CKSUM_SERVER_NAME, 0); if ( NULL t attach name: %1")).arg(strerror(errno))); } else { serverThread.start(); emit startServer(attach); } } MainWindow::~MainWindow() { stopServer(); serverThread.quit(); serverThread.wait(); delete ui; } void MainWindow::showText(QString txt) { ui->text->setText(txt); } void MainWindow::showCrc(QString crc) { ui->cksum->setText(crc); } void MainWindow::log(QString msg) { ui->status->append(msg.prepend(QDateTime::currentDateTime().toString("hh:mm:ss "))); } void MainWindow::stopServer() { if ( NULL > ƒл€ работы сервера создаЄм им€ в MainWindow использу€ функцию name_attach(). ѕри помощи сигнала передаЄм структуру attach в поток сервера, тем самым запуска€ его. ƒл€ остановки сервера удал€ем им€ Ч функци€ name_detach(). ¬ остальном очень похоже на то, что было сделано в клиенте. ѕосмотрим на код: server.h#ifndef SERVER_H #define SERVER_H #include <QObject> #include "../iov_server.h" typedef union { uint16_t msg_type; struct _pulse pulse; cksum_header_t cksum_hdr; } msg_buf_t; class Server : public QObject { Q_OBJECT public: Server() {} virtual ~Server() {} signals: void result(QString data); void text(QString text); void log(QString err); public slots: void process(name_attach_t* attach); private: int calculate_checksum(char *text); }; #endif // SERVER_H server.cpp#include "server.h" int Server::calculate_checksum(char *text) { int cksum = 0; for ( char *c = text; *c; c++ ) cksum >  ласс Server реализует две функции консольного сервера (qnx_server), изменилс€ только вывод сообщений (при помощи сигналов/слотов Qt) и регистраци€ имени выполн€етс€ в классе MainWindow. –абота графических вариантов клиента и сервера представлена на следующем скриншоте: —ервер получилс€ без элементов управлени€. Ќет ни кнопок, ни полей ввода. √рафическое окно сервера служит только дл€ контрол€ за его работой. «аключение ¬от и подошла к концу эта заметка. Ѕыл рассмотрен код нескольких примеров, стало пон€тно, как правильно использовать механизм сообщений QNX в приложени€х на Qt. ƒл€ тех же, кто захочет воспроизвести примеры € опубликовал их на Bitbucket. ѕредвид€ возможные замечани€ по коду, прошу учесть, что это только примеры, которые иллюстрируют работу SRR в Qt.  ое-что в рабочей системе € бы сделал иначе, но чтобы не перегружать примеры, их код был упрощЄн, и на некоторые моменты € закрыл глаза. “ем не менее, если у кого-то из читателей по€в€тс€ конкретные предложени€ по улучшению кода примеров или исправлению ошибок, то € их по возможности учту. ѕрошу по этим вопросам обращатьс€ в личные сообщени€.
—етевые технологии »нтернета вещей

ћетки:  

[»з песочницы] ѕравильное использование require в node.js

ƒневник

—реда, 15 »юн€ 2016 г. 19:24 + в цитатник
ѕредисловие Ќе так давно проект, на котором € работаю в данный момент, начал использовать модульную систему ES2015. я не буду заостр€ть внимание на этой технологии JavaScript, т.к стать€ совсем не об этом, а о том как технологи€ сподвигла мен€ к одной мысли.  ак многие знают, ES2015 Modules представл€ют собой импортирование/экспортирование скриптов крайне схожее по синтаксису с python и многими другими €зыками программировани€. ѕример: // Helper.js export function includes(array, variable) { return array.indexOf(variable) Helper'; assets(includes([1,2,3], 2), true); ¬се, кто интересовалс€ модул€ми JavaScript знают, что импортирование и экспортирование возможно только на верхнем уровне модул€ (файла с кодом). —ледующий грубый пример кода вызовет ошибки: // sendEmail.js export default function sendEmails(emails_list) { import sender from 'sender'; export sender; // сделать что-то } Exception: SyntaxError: import/export declarations may only appear at top level of a module ¬ отличие от ES2015 Modules Ч в модульной системе node.js импортирование и экспортирование возможны на любом уровне вложенности. јналогичный код на node.js не вызовет ошибку: // sendEmail.js module.exports = function sendEmails(emails_list) { const sender = require('sender'); exports.sender = sender; // сделать что-то } ѕреимущество такого способа в том, что модули необходимые в обработчике €вно импортированы внутри и не засор€ют пространство имен модул€ (особенно актуально, если импортируемый модуль нужен только в одном обработчике). “ак же по€вл€етс€ возможность отложенного экспортировани€ данных модул€. ќсновные минусы: ќб отсутствии модул€ вы узнаете только во врем€ вызова соответствующего обработчика ѕуть к импортироемому модулю может изменитьс€, что приведет к изменению в каждом месте импортировани€ (например, в вашем модуле, в различных обработчиках используетс€ lodash/object/defaults и вы решили обновитьс€ до 4.x версии, где подключать нужно lodash/defaults). –азбор полетов ¬ большинстве задач дл€ которых используетс€ node.js Ч front-end или основной веб-сервер, и высока€ нагрузка на node.js частое €вление. ѕропуска€ способность вашего сервера должны быть максимально возможна€. »змерение пропускной способности ƒл€ измерени€ пропускной способности веб-сервера используетс€ великолепна€ утилита от Apache Ч ab. ≈сли вы еще с ней не знакомы, то насто€тельно рекомендую это сделать.  од веб-сервера одинаков за исключением обработчиков. “ест запускалс€ на node.js 6.0 с использованием модул€ ifnode, сделанного на базе express »мпортирование модулей непосредственно в обработчик  од: const app = require('ifnode')(); const RequireTestingController = app.Controller({ root: '/', map: { 'GET /not_imported': 'notImportedAction' } }); RequireTestingController.notImportedAction = function(request, response, next) { const data = { message: 'test internal and external require' }; const _defaults = require('lodash/object/defaults'); const _assign = require('lodash/object/assign'); const _clone = require('lodash/lang/clone'); response.ok({ _defaults: _defaults(data, { lodash: 'defaults' }), _assign: _assign(data, { lodash: 'assign' }), _clone: _clone(data) }); }; –езультат: $ ab -n 15000 -c 30 -q "http://localhost:8080/not_imported&quot; Server Hostname: localhost Server Port: 8080 Document Path: /not_imported Document Length: 233 bytes Concurrency Level: 30 Time taken for tests: 4.006 seconds Complete requests: 15000 Failed requests: 0 Total transferred: 6195000 bytes HTML transferred: 3495000 bytes Requests per second: 3744.32 [#/sec] (mean) Time per request: 8.012 [ms] (mean) Time per request: 0.267 [ms] (mean, across all concurrent requests) Transfer rate: 1510.16 [Kbytes/sec] received Percentage of the requests served within a certain time (ms) 50% 6 66% 7 75% 8 80% 8 90% 10 95% 15 98% 17 99% 20 100% 289 (longest request) »мпортирование модулей в начале файла  од: const app = require('ifnode')(); const _defaults = require('lodash/object/defaults'); const _assign = require('lodash/object/assign'); const _clone = require('lodash/lang/clone'); const RequireTestingController = app.Controller({ root: '/', map: { 'GET /already_imported': 'alreadyImportedAction' } }); RequireTestingController.alreadyImportedAction = function(request, response, next) { const data = { message: 'test internal and external require' }; response.ok({ _defaults: _defaults(data, { lodash: 'defaults' }), _assign: _assign(data, { lodash: 'assign' }), _clone: _clone(data) }); }; –езультат: $ ab -n 15000 -c 30 -q "http://localhost:8080/already_imported&quot; Server Hostname: localhost Server Port: 8080 Document Path: /already_imported Document Length: 233 bytes Concurrency Level: 30 Time taken for tests: 3.241 seconds Complete requests: 15000 Failed requests: 0 Total transferred: 6195000 bytes HTML transferred: 3495000 bytes Requests per second: 4628.64 [#/sec] (mean) Time per request: 6.481 [ms] (mean) Time per request: 0.216 [ms] (mean, across all concurrent requests) Transfer rate: 1866.83 [Kbytes/sec] received Percentage of the requests served within a certain time (ms) 50% 5 66% 6 75% 6 80% 7 90% 8 95% 14 98% 17 99% 20 100% 38 (longest request) јнализ результатов »мпортирование модулей в начале файла уменьшило врем€ одного запроса на ~23%(!) (в сравнение с импортированием непосредственно в обработчик), что весьма существенно. “ака€ больша€ разница в результатах кроетс€ в работе функции require. ѕеред импортированием, require обращаетс€ к алгоритму поиска абсолютного пути к запрашиваемому компоненту (алгоритм описан в документации node.js).  огда путь был найден, то require провер€ет был ли закеширован модуль, и если нет Ч не делает ничего сверхестественного, кроме вызова обычного fs.readFileSync дл€ .js и .json форматов, и недокументированного process.dlopen дл€ загрузки C++ модулей. Note: пробовал "прогревать" кеш дл€ случа€ с непосредственным импортированием модулей в обработчик (перед запуском утилиты ab, модули были уже закешированы) Ч производительность улучшалась на 1-2%. ¬ыводы ≈сли вы используете node.js, как сервер (нет разницы какой Ч TCP/UDP или HTTP(S)), то: »мпортирование всех модулей необходимо делать в начале файла, чтобы избегать лишних синхронных операций св€занных с загрузкой модулей (один из главных анти-паттернов использовани€ node.js как асинхронного сервера). ¬ы можете не тратить ресурсы на вычисление абсолютного пути запрашиваемого модул€ (это и есть основное место дл€ потери производительности).

ћетки:  

ќпыт использовани€ Liferay Portal в e—ommerce

ƒневник

ѕонедельник, 06 »юн€ 2016 г. 19:17 + в цитатник
ћногие из нас уже не раз писали разного рода магазины. Ќо большие e-commerce проекты в быстро растущей и развивающейс€ компании разрабатывать приходитс€ нечасто.   таким решени€м предъ€вл€ютс€ дополнительные требовани€, такие как конфигурируемость, адаптивность к изменени€м, возможности встраивани€ в другие системы и прочее. ƒл€ написани€ такого решени€ компани€ Netcracker использовала Liferay Portal фреймворк. ¬ итоге получили достаточно преимуществ, но и без проблем не обошлось.
ѕодробнее под катом

ћетки:  

 —траницы: [1]