If you remember the initial use case, CCM wants to interact with the authentication service from the Ruby-on-Rails back office application. Therefore, you will implement the client in Ruby. The code you came up with is reproduced as follows:
channel = AMQP::Channel.new(connection) channel.on_error do |ch, channel_close| connection.close { EventMachine.stop } raise "Channel error: #{channel_close.inspect()}" end channel.headers( 'internal-services', :durable => true, :auto_delete => false, :passive => true) do |exchange| channel.queue('', :exclusive => true, :durable => false, :auto_delete => true) do |response_queue| response_queue.subscribe do |metadata, payload| handle_response(metadata.content_type, payload) end puts "Response queue created: #{response_queue.name}" message_id = SecureRandom.uuid message_json = JSON.generate({ :username => user_name, :password => password}) exchange.publish( message_json, :content_type => 'application/vnd.ccm.login.req.v1+json', :content_encoding => 'UTF-8', :message_id => message_id, :correlation_id => message_id, :reply_to => response_queue.name, :headers => { :request_type => 'login', :request_version => 'v1' }) end EventMachine.add_timer(3) do puts 'No response after 3 seconds' connection.close { EventMachine.stop } end end
There are several interesting bits in this code. Observe that first, a passive declaration of the internal-services
headers exchange is performed. What does this mean? Basically, it is a declaration attempt that just checks the existence of the exchange, the expected routing type, and the durability configuration. If you remember our discussion about check then act strategies in Chapter 2, Creating an Application Inbox, you're probably thinking "Ah hah! You're doing the opposite of what you preached." Actually, in this case, it's okay if the exchange gets deleted after the check and the subsequent publish operation fails; the failure will be dealt with by the channel error handler (at the top of the code snippet). This passive declaration saves you the effort of creating a temporary queue for no reason if it's clear that the publish operation will fail anyway.
With this in place, the next step consists of creating the exclusive autodelete response queue. Did you spot that an empty string is used for its name? This means that it will be up to RabbitMQ to generate a unique name for the queue, which is what you want since you're using short-lived response queues. Then, the response handler gets subscribed to the queue, which makes sense because we want to do this before sending the request. Otherwise, we may not be ready to receive the response if it comes back very quickly.
After this, the login message is created and published to the exchange with the necessary reply_to
property and the request_type
and request_version
headers. For good measure, a correlation_id
property is also provided, though it's not used since a temporary queue is used for the response (instead of a permanent one).
You can learn more about EventMachine and how it enables Ruby to execute a code in a nonblocking fashion at http://rubyeventmachine.com.
Finally, a time-out is set in order to deal with a case where no service response comes back. This is crucial if you do not want to block the client application threads forever in case there is an issue with the service, a blocking that could eventually make the whole application unusable.
You're done with the authentication service client. If you glance at the management console while a service request is underway, you will see the temporary reply queue right above the authentication-service
queue, as illustrated in the following screenshot. The name of the management console starts with amq.gen-
to make it clear that it's a name that has been generated by RabbitMQ. Observe that the queue is exclusively accessible to its owner:
At this point, you've built a solid message-oriented SOA foundation that you build upon to roll out new services or new versions of existing services as the need arises.
3.144.222.185