⬆️ ⬇️

SignalsyMQ - simple and fast message queue in PHP + Redis (and a little Zend Framework)



Greetings to the readers. Now I am developing my own framework, based on the signal architecture (signal / slot), as opposed to the now dominant MVC model. While it is being tested in our startup, and during that time I realized that just libraries are few — real tasks and visions of the future show that different functionality is needed, but united by one theme — processing and delivering information to many clients in real time (yes, somewhat similar to Comet, he is there too). Therefore, it was decided to try to implement the main component - a message queue that would become the main one for the following projects, fast, flexible and scalable.



What happened? Alpha version of SignalsyMQ is a PHP / Redis / Zend Framework message queue.



We already wrote about message queues (the first article of the review , the second ), but once again critically reviewing all the systems and even trying some of them, I realized that the niche is small, flexible and configurable queues as close as possible to the main application platform (in this case, , PHP), remains open and free. It lacks a simple server that would operate with standard messages (in JSON format, it is just an associative array inside the application), it would provide flexible configuration of message delivery, constant storage of queues, and very fast work with them. In this case, I want a simple protocol and generally maximum simplicity.

')

Closest to this stand or products based on Ruby - Starling MQ and MemcacheQ . By the way, both systems work according to the memcache protocol, which positively affects the possibility of embedding in a heterogeneous environment. But memcacheq was embarrassed by its development policy, or rather, lack of it, restrictions on the maximum length of a message, etc., and Starling, although it has the experience of being used on Twitter, is also quite a specific product, it is damp, besides it requires absolutely another platform. Therefore, it was decided, in the best traditions, to write our own implementation of the message queue.



As a starting point, the Redis project was taken - a very, very fast and flexible NoSQL system with advanced storage and processing of structured data (it was already written about on Habré, did not have time). Rediska became the second “whale” - PHP is a library for working with Redis with convenient syntax and support for many interesting features (for example, working with several servers, embedded serialization, key distribution among servers, etc., but the library is in active development, therefore, features are added constantly) The third "whale" was the Zend Framework , which I now use in most projects, although here it plays only a supporting role (several service classes are used).



Immediately, I note that ZF already has its own implementation of message queues - Zend_Queue , which supports various backends for storing messages, starting from a regular PHP array, ending with MemcacheQ, databases and the Zend Platform. However, in practice, I have not yet succeeded in trying the most promising interface with MemcacheQ, but working with the base in the case of a dense flow of messages is simply impossible - the work slows down a lot until it crashes with the entire script. Well, the Zend_Queue interface itself is very abstract, in my case I would like some higher-level functions, so I would have to significantly extend the existing code. By the way, there is a special adapter in the Rediska library in order to work with queues over Redis, but after suffering a week and never even making a test case work, I finally decided that I’m writing my system!



To begin, we define the overall architecture and features. The basic concepts are message, queue (channel), server and storage.

Another remark about the architecture and differences from Rediska_Queue will be that, as far as I understand from the sources, for the sake of speed, they put a valuable property of distribution. For example, the getQueues command, which returns an array of all stored queues (not messages, but only queue names), works with a local copy of the list of queues. Therefore, if another client creates a new queue while the first one is running, he will not know about it. In the case of a web application, when the request is processed while the page is being formed, it may not matter, I just need a message server that can work as a daemon and process many queues and commands from many clients. Although this requires additional resources, although thanks to Redis, all operations have very good indicators of complexity.



And so, the general API consists of a number of methods that are as close as possible to the Zend_Queue interface, but with some extensions, which I will describe below.



Honestly, the source code of the project, although accessible , is in no way ready for real work - it’s rather just a prototype and the first alpha version. There are still a lot of not optimal places, different documentation, including pieces from Zend_Queue, on the interface of which I was based, not fully thought out work algorithm and data structure (for example, deleteMessage requires two parameters, the name of the queue and the message, although in the service channel messages already have the name of his queue, etc.). However, the current code already shows results, it is working and suitable for experiments.



It’s also really impossible to talk about performance now, but I conducted several tests using Amazon EC2 small instance (32 bit, Debian, PHP 5.2.11, Redis 1.1.9) - a single-threaded server in the mode of selective recording of a large number of messages to random queues (1000 messages and packs of 100) shows the speed in the range of 1500 - 3000 messages per second (recording, as the other systems are still on the server, the vibrations are very dependent on the load, and in this mode it is very sensitive how the storage interval is set in Redis itself ).



In the case when each client works with a small number of messages and one queue, but there are many parallel clients, the maximum speed is achieved, in principle, closely approaching the maximum that can be pulled out by tests on this configuration (5–7 thousand messages per second in the case when the test and the server on the same machine). Greater results can be achieved by applying multithreading, taking Redis to a separate server, as well as unloading the server from other tasks as much as possible, besides, performance rests on server CPU parameters - the Large instance (2 cores and 7 GB RAM) shows 5-6 times greater speed (of course, this is only an average figure, far from real).



These are the experiments. The topic of message queues is interesting, we should write a couple more articles, for example, almost no one knows about the components of Zend_Queue and their application, even from developers who actively use ZF in their work. Well, there are many improvements and server improvements ahead, turning it into a network daemon and building a special server on this database that works with various protocols (possibly standard for MQ - Stomp, AMQP), set up replication and take into account workload during background saving, work with several parallel servers, etc. Are you interested in reading about it?

Source: https://habr.com/ru/post/78050/



All Articles