During the
development of several online services, we noticed that a lot of their
functionality is shared, and the principle of DRY (Do not Repeat Yourself - Do not Repeat Yourself), decided to make common functionality in a separate module.
The module has been presented to the following requirements:
I'll note that our services are written in PHP and Apache server running Linux. The main questions were: "on what to write-in?" And "how to handle to the module of PHP-scripts? '. An analysis of the implementation of the module was conducted with the software you are using, as well as our personal preferences, knowledge and experience.
It was proposed that three main options:
So, we proceed to implementation. Let's start with the "client» PHP-code. Suppose that there are some applications (our module written in Qt), which registered D-Bus-service with a unique name «test.service». Service is a hierarchical structure of registered items in it. If you are not in the hierarchy, you can use the object path "/" (like the root directory in Linux). In our case, there is an object in the service "/ test". Objects provide interfaces, containing an assortment of methods. Object "/ test" has an interface «test.iface» the method «sum».
client.php:
The code is calling «sum» of the interface «test.iface» at the facility, located on the path "/ test", the service «test.service» System bus D-Bus. The method is called with two integer arguments. The output of this script on the service «test.service» should do the math for 42 and 13 years, and the result is displayed using var_dump.
When designing the architecture of the module, we decided to exploit the terminology ZendFramework (which may seem odd for a program napisaanoy for C + +). This was caused by the fact that terms such as "service", "interface", "object" is used by us in relation to D-Bus. And to avoid confusion, we have taken the concept of "action» (Action) and "controller» (Controller) of ZendFramework.
The term "action," we decided to understand inherited from QThread class that represents a thread in which to implement any required functionality.
A "controller" named class that encapsulates calls to action in their methods. In this case, the controller should inherit from QObject and QDBusContext.
The code for the parent function of the module (file main.cpp). It is registered on the system bus controller D-Bus.
It should be noted that the controller methods that are open to inter-processor calls D-Bus, working consistently. That is, if the first client makes a call to sum, the second must wait until the implementation of the method is not over. Therefore, we have decided to reduce the method's code to a minimum, in order to avoid long waiting times. Thus, for each client call is started work line (action) and the output from the method.
Consider the controller class (file TestController.h). Implementation of a method for the sake of brevity we write in the header.
The steps we will be posting a functional module. Each controller method will match the class action. Therefore, it is advisable to write a class Action, the base for all activities.
Inheriting this class, we will focus on the implementation of the required functionality without worrying about the details of the interaction with D-Bus. All you need to do is to save the settings in the properties of the class, add a and b, and write the result to the link reply ().
SumAction.h:
Compiled the module described above, we get an application registers a D-Bus-service «test.service». Let's try to run it. Most likely, the result will be:
To solve this problem, make changes in the D-Bus. D-Bus allows flexible configuration of safety and functionality. For example, our work is enough to create a file: / etc/dbus-1/system.d/dbus-test.conf follows:
Restart the daemon D-Bus - is not necessary. The changes will take effect when you save the file.
Launch the module and, if it is successfully launched, try to refer to it from PHP-script.
That's the expected result: 42 + 13 = 55. Sources can be taken here .
The above described method of interprocess communication allowed us to interact modules written in C + +, with several Web services, in need of its functionality. Thus, we have the high performance and flexibility in building complex information systems, which gives us C + + (and Qt in particular), and ease of development and maintenance of Web services in PHP.
The module has been presented to the following requirements:
- independence from the use of its services;
- simplicity "client" code;
- threading and high speed.
I'll note that our services are written in PHP and Apache server running Linux. The main questions were: "on what to write-in?" And "how to handle to the module of PHP-scripts? '. An analysis of the implementation of the module was conducted with the software you are using, as well as our personal preferences, knowledge and experience.
It was proposed that three main options:
- Implement the module in the form of another web service in PHP and make calls from the client services using CUrl or using SOAP. Disadvantages of this approach - slow work interpitiruemogo language, the cost of network requests (unnecessary at the moment, as is supposed, that the module and the services will be running on the same server), the complexity of shared objects in concurrent queries.
- Implement the module as FastCGI-applications using multi-threading. On the positive side of this option include: the ability to write a module for C / C + +, that would increase the speed and allowed to implement shared obety in a multithreaded environment. Appeals to the module of PHP-scripts, can be implemented with the help of domain sockets Unix, thereby avoiding the costs of implementing unnecessary calls to the network (at the location of services and modules on the same server).
- In addition to these approaches, our attention was drawn to the system inter-process communication (IPC) D-Bus, is widely used in Linux today. Features and Specifications D-Bus, such as speed, reliability, flexibility, availability of high-level libraries, wrappers, seemed attractive to us and to satisfy our requirements. In fact, the use of the second option would have to write your own equivalent D-Bus. Further, there was a question about calls from the client module PHP-code. On the Internet, we came across two libraries implement D-Bus for PHP: from Pecl and Japanese from GREE Labs . But, as we have already managed to write a working test case for Pecl D-Bus, Japanese realization we do not draw much attention. On the part of C + +, we have decided to use QtDBus, due to dating programmers with a library Qt.
"Client" code
So, we proceed to implementation. Let's start with the "client» PHP-code. Suppose that there are some applications (our module written in Qt), which registered D-Bus-service with a unique name «test.service». Service is a hierarchical structure of registered items in it. If you are not in the hierarchy, you can use the object path "/" (like the root directory in Linux). In our case, there is an object in the service "/ test". Objects provide interfaces, containing an assortment of methods. Object "/ test" has an interface «test.iface» the method «sum».
client.php:
// Создание объекта, соединяющегося с системной шиной D-Bus. $dbus = new DBus(DBus::BUS_SYSTEM); // Создание объекта, для осуществления межпроцессных вызовов. $proxy = $dbus->createProxy("test.service", "/test", "test.iface"); try { // Осуществляем вызов метода $result = $proxy->sum(42, 13); var_dump($result); } catch (Exception $e) { print $e->getMessage()."\n"; }
The code is calling «sum» of the interface «test.iface» at the facility, located on the path "/ test", the service «test.service» System bus D-Bus. The method is called with two integer arguments. The output of this script on the service «test.service» should do the math for 42 and 13 years, and the result is displayed using var_dump.
The implementation of D-Bus-module
When designing the architecture of the module, we decided to exploit the terminology ZendFramework (which may seem odd for a program napisaanoy for C + +). This was caused by the fact that terms such as "service", "interface", "object" is used by us in relation to D-Bus. And to avoid confusion, we have taken the concept of "action» (Action) and "controller» (Controller) of ZendFramework.
The term "action," we decided to understand inherited from QThread class that represents a thread in which to implement any required functionality.
A "controller" named class that encapsulates calls to action in their methods. In this case, the controller should inherit from QObject and QDBusContext.
Parent function
The code for the parent function of the module (file main.cpp). It is registered on the system bus controller D-Bus.
#include <QCoreApplication> #include <QDebug> #include <QDBusConnection> #include "TestController.h" #define SERVICE_NAME "test.service" #define OBJECT_PATH "/test" int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); // Создаём соединение с системной шиной D-Bus QDBusConnection conn = QDBusConnection::systemBus(); // Регистрируем сервис if (! conn.registerService(SERVICE_NAME)) { qDebug() << "Error:" << conn.lastError().message(); exit(EXIT_FAILURE); } TestController controller; // Регистрируем контроллер conn.registerObject(OBJECT_PATH, &controller, QDBusConnection::ExportAllContents); return app.exec(); }
"Controller"
It should be noted that the controller methods that are open to inter-processor calls D-Bus, working consistently. That is, if the first client makes a call to sum, the second must wait until the implementation of the method is not over. Therefore, we have decided to reduce the method's code to a minimum, in order to avoid long waiting times. Thus, for each client call is started work line (action) and the output from the method.
Consider the controller class (file TestController.h). Implementation of a method for the sake of brevity we write in the header.
#ifndef TEST_CONTROLLER_H #define TEST_CONTROLLER_H #include <QObject> #include <QDBusContext> #include <QDBusConnection> #include <QDebug> #include "SumAction.h" class TestController: public QObject, protected QDBusContext { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "test.iface") // Имя интерфейса public: Q_INVOKABLE int sum(int a, int b) { // Сообщаем D-Bus, что ответ прийдёт позже. setDelayedReply(true); // Запускаем нить (new SumAction(a, b, this))->start(); // Формальный возврат значения для компилятора. Реальный результат будет возвращён из нити. return 0; }; }; #endif // TEST_CONTROLLER_H
"Actions"
The steps we will be posting a functional module. Each controller method will match the class action. Therefore, it is advisable to write a class Action, the base for all activities.
Action.h
Action.cpp
Inheriting this class, we will focus on the implementation of the required functionality without worrying about the details of the interaction with D-Bus. All you need to do is to save the settings in the properties of the class, add a and b, and write the result to the link reply ().
SumAction.h:
#ifndef SUMACTION_H #define SUMACTION_H #include "Action.h" class SumAction: public Action { Q_OBJECT public: SumAction(int a, int b, const QDBusContext* context): Action(context), _a(a), _b(b) {} virtual ~SumAction() {}; protected: void run() { reply() << _a + _b; } private: int _a; int _b; }; #endif // SUMACTION_H
Configuration D-Bus
Compiled the module described above, we get an application registers a D-Bus-service «test.service». Let's try to run it. Most likely, the result will be:
$ ./dbus-test Error: "Connection ":1.66" is not allowed to own the service "test.service" due to security policies in the configuration file"
To solve this problem, make changes in the D-Bus. D-Bus allows flexible configuration of safety and functionality. For example, our work is enough to create a file: / etc/dbus-1/system.d/dbus-test.conf follows:
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> <policy context="default"> <allow own="test.service"/> <allow send_destination="test.service"/> <allow receive_sender="test.service"/> </policy> </busconfig>
Restart the daemon D-Bus - is not necessary. The changes will take effect when you save the file.
Launch the module and, if it is successfully launched, try to refer to it from PHP-script.
$ php client.php int(55)
That's the expected result: 42 + 13 = 55. Sources can be taken here .
Conclusion
The above described method of interprocess communication allowed us to interact modules written in C + +, with several Web services, in need of its functionality. Thus, we have the high performance and flexibility in building complex information systems, which gives us C + + (and Qt in particular), and ease of development and maintenance of Web services in PHP.
0 commentaires:
Enregistrer un commentaire