📜 ⬆️ ⬇️

Improving the performance of Ruby on rails applications using ActiveMQ

In my post I want to talk about the possibility of using ActiveMQ in a project written in the Ruby on rails framework.

What is a Message Queue?


MQ is the messaging architecture between application components in asynchronous mode. That is, the sender and receiver can interact at different times. Such systems consist of a producer'a (sender) and a consumer'a (recipient) that interact with each other through a broker.

Using such systems, you can significantly increase application performance by executing code in asynchronous mode. Suppose you have a code that slows down the execution of some part on your site so that the user does not wait for the completion of such code, it is better to execute it in asynchronous mode. A few simple examples:
- thumbnails generation;
- collection of statistics;
- distribution of letters / messages;
- deletion of data from the tables;
- data indexing;
- import data into the database.
')
There are a lot of such examples, I think everyone can find part of the code in their project that can be submitted for execution in asynchronous mode.

What is ActiveMQ?


ActiveMQ is an open source implementation of the message broker system. The advantage of this system is high performance, openness and the ability to implement customers in any languages. ActiveMQ currently supports the following protocols:
- OpenWire (own binary protocol);
- Stomp;
- REST;
- WS Notification;
- XMPP.

Install ActiveMQ


The description will go for Linux, although I think there should be no problems installing on Windows. We execute the following commands:

wget apache.multihomed.net/activemq/apache-activemq/5.2.0/apache-activemq-5.2.0-bin.tar.gz
tar xvf apache-activemq-5.2.0-bin.tar.gz
sudo cp -R ./apache-activemq-5.2.0 /usr/local/apache-activemq


Installation completed. Run ActiveMQ:

sudo /usr/local/apache-activemq/bin/activemq &

Everything is ready to go.

Working with ActiveMQ on Ruby on rails


To understand how convenient it is to execute code in asynchronous mode, let's first create a code that will be executed for a long time.

rails test
cd test
script/generate controller test
script/server


Convert the Test controller code to the following form:

class TestController < ApplicationController
def index
self.class.benchmark('Write to file') do
path = File.join(RAILS_ROOT, 'tmp', 'test')
file_test = File.new(path, 'w')

10000000.times do
file_test.write("Some test message\n")
end
end

render :text => 'Data saved'
end
end


We open http://0.0.0.0ل000 / test / index and see that it takes some time to wait for the completion of the code. My benchmark showed that this code runs for 10 seconds, a lot, not every user is willing to wait for such a time.

To interact with ActiveMQ, I suggest using the ActiveMessaging plugin, I want to note that it is possible to use the Stomp client directly. But in this case we will lose the ability to easily and easily switch to another MQ system.

ActiveMessaging is a plugin for Ruby on rails which is very easy to use in a project for executing code in asynchronous mode. It supports a large number of protocols, including Stomp, i.e. we can use it to work with ActiveMQ.

Install it into the project:

script/plugin install activemessaging.googlecode.com/svn/trunk/plugins/activemessaging

we also need to install a client to work with the stomp protocol:

gem install stomp

Installation completed.

ActiveMessaging has two configuration files:
- config / broker.yml - contains settings for connection;
- config / messaging.rb - contains queue settings.

Executable code in asynchronous mode is written in the so called processor. To generate our first processor, execute the following command:

script/generate processor Test

Open our processor (/app/processors/test_proccesor.rb) and transfer the test code here:

class TestProcessor < ApplicationProcessor
subscribes_to :test

def on_message(message)
path = File.join(RAILS_ROOT, 'tmp', 'test')
file_test = File.new(path, 'w')

10000000.times do
file_test.write("#{message}\n")
end

logger.debug 'Data saved'
end
end


We convert the index action code to the following:

def index
publish :test, 'Some test message'
render :text => 'Message sent'
end


Here we simply pass the message 'Some test message' to the destination under the name test, i.e., we perform the function of the producer. If you open the configuration file config / messaging.rb then you will see the queue / queue / Test as the destination destination test queue, to which our message will be sent.

We start the demon that is our consumer, i.e. it will read all messages from the queue / queue / Test and transfer them to the TestProcessor processor.

script/poller start

For the purity of the test, delete our file in which we wrote the data:

rm /tmp/test

We update the page http://0.0.0.0ل000 /test/ index and immediately see that the code runs almost instantly, but now we go to tmp and see the newly created test file with the given one. You can also go to the log and see the message 'Data saved' there.

In most cases, we need to execute the code with some parameters. We can translate the hash with parameters into XML, JSON or Yaml and pass as a message. I prefer JSON.

Let's try to transfer the number of written lines and the message itself, which is written to the file. Change the index action a bit:

def index
publish :test, {:count => 200000, :message => 'Some new test message'}.to_json
render :text => 'Message sent'
end


Change the on_message method:

def on_message(message)
options = JSON.parse(message).symbolize_keys
path = File.join(RAILS_ROOT, 'tmp', 'test')
file_test = File.new(path, 'w')

options[:count].times do
file_test.write("#{options[:message]}\n")
end

logger.debug 'Data saved'
end


Since ruby ​​does not have standard functions for working with JSON, you need to install gem:

sudo gem install json

and connect it to TestProcessor:

require 'json'

Now we interrupt the work of our demon and restart it:

script/poller stop
script/poller start


Again we are updating the page http://0.0.0.0ل000/test/index . Opening the tmp / test file, we will see the overwritten file already in the lines 'Some new test message'.

In the processor it is very easy to use models, just like in ordinary action games. A small example for clarity:

def on_message(message)
options = JSON.parse(message).symbolize_keys

Product.find(:all).each do |product|
product.price = options[:price]
product.save
end
end


In ActiveMQ there is a utility in which you can view statistics of sent messages, a list of queues, and even send a message, it is very convenient to use for debugging. It is located at http://0.0.0.0:8161/admin/ .

I hope this post has demonstrated the simplicity and advantage of using ActiveMQ in a project. Just a little refactoring in your project and the application will breathe differently.

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


All Articles