README.md
# ManageIQ Messaging Client
[![Gem Version](https://badge.fury.io/rb/manageiq-messaging.svg)](http://badge.fury.io/rb/manageiq-messaging)
[![CI](https://github.com/ManageIQ/manageiq-messaging/actions/workflows/ci.yaml/badge.svg)](https://github.com/ManageIQ/manageiq-messaging/actions/workflows/ci.yaml)
[![Code Climate](https://codeclimate.com/github/ManageIQ/manageiq-messaging.svg)](https://codeclimate.com/github/ManageIQ/manageiq-messaging)
[![Test Coverage](https://codeclimate.com/github/ManageIQ/manageiq-messaging/badges/coverage.svg)](https://codeclimate.com/github/ManageIQ/manageiq-messaging/coverage)
Client library for ManageIQ components to exchange messages through its internal message bus.
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'manageiq-messaging'
```
And then execute:
$ bundle
Or install it yourself as:
$ gem install manageiq-messaging
## Usage
### Initialize a client
It is not recommended to directly create an actual client through `new` operation. Follow the example by specifying a protocol. This allows to easily switch the underlying messaging system from one type to another. Currently `:Stomp` and `:Kafka` are implemented.
```
ManageIQ::Messaging.logger = Logger.new(STDOUT)
client = ManageIQ::Messaging::Client.open(
:protocol => 'Stomp',
:host => 'localhost',
:port => 61616,
:password => 'smartvm',
:username => 'admin',
:client_ref => 'generic_1',
:encoding => 'json' # default 'yaml'
)
# publish or consume messages using the client
client.close
```
Alternatively, you can pass a block to `.open` without the need to explicitly close the client.
```
ManageIQ::Messaging::Client.open(
:protocol => 'Stomp',
:host => 'localhost',
:port => 61616,
:password => 'smartvm',
:username => 'admin',
:client_ref => 'generic_1'
) do |client|
# do stuff with the client
end
end
```
### Publish and subscribe messages
This is the one-to-one publish/subscribe pattern. Multiple subscribers can subscribe to the same queue but only one will consume the message. Subscribers are load balanced.
```
client.publish_message(
:service => 'ems_operation',
:affinity => 'ems_amazon1',
:message => 'power_on',
:payload => {
:ems_ref => 'u987',
:id => '123'
}
)
client.subscribe_messages(:service => 'ems_operation', :affinity => 'ems_amazon1', :auto_ack => false) do |messages|
messages.each do |msg|
# do stuff with msg.message and msg.payload
msg.ack
end
end
# You can create a second client instance and call subscribe_messages with
# the same options. Then both clients will take turns to consume the messages.
```
For better sending performance, you can publish a collection of messages together
```
msg1 = {:service => 'ems_inventory', :affinity => 'ems1', :message => 'refresh', :payload => 'vm1'}
msg2 = {:service => 'ems_inventory', :affinity => 'ems1', :massage => 'refresh', :payload => 'vm2')
client.publish_messages([msg1, msg2])
```
Provide a block if you want `#publish_message` to wait on a response from the subscriber. This feature may not be supported by every underlying messaging system.
```
client.publish_message(
:service => 'ems_operation',
:affinity => 'ems_amazon1',
:message => 'power_on',
:payload => {
:ems_ref => 'u987',
:id => '123'
}
) do |result|
ansible_install_pkg(vm1) if result == 'running'
end
```
### Publish and subscribe background jobs
Background Job is a special type of message with a known `class_name` and the subscriber knows how to process the message without a user block
```
client.publish_message(
:service => 'generic',
:class_name => 'MiqTask',
:message => 'update_attributes', # method name
:payload => {
:instance_id => 2,
:args => [{:status => 'Timeout'}]
}
)
client.subscribe_background_job(:service => 'generic')
```
Provide a call block if you want `#publish_message` to wait on a response from the subscriber.
### Publish and subscribe topics (events)
This is the one-to-many publish/subscribe pattern. Multiple subscribers can subscribe to the same queue and each one will receive the same message.
```
client.publish_topic(
:service => 'provider_events',
:event => 'powered_on',
:sender => 'ems_amazon1', # optional
:payload => {
:ems_ref => 'uid987',
:timestamp => '1501091391'
})
client.subscribe_topic(:service => 'provider_events', :persist_ref => 'automate_1') do |msg|
# do stuff with msg.sender, msg.message, and msg.payload. sender may be nil if not set by the publisher
end
```
By default, events are delivered to live subscribers only. Some messaging systems support persistence with options.
### Publish bulk messages to a topic
Often it is more efficient to publish messages in bulk rather than one-at-a-time. To do this you can pass an array of messages to the `publish_topic` API:
```ruby
client.publish_topic(
[
{:service => 'provider_events', :event => 'powered_off', :payload => {:ems_ref => 'uid987', :timestamp => '1501091391'}},
{:service => 'provider_events', :event => 'powered_on', :payload => {:ems_ref => 'uid987', :timestamp => '1501091429'}},
]
)
```
### Add your own headers to a message (Queue or Topic)
If you want you can add in your own headers to the send message
```
client.publish_topic(
:service => 'provider_events',
:event => 'powered_on',
:headers => {:request_id => "12345"},
:payload => {:ems_ref => 'uid987'}
)
client.subscribe_topic(:service => 'provider_events', :persist_ref => 'automate_1') do |msg|
puts "Received event #{msg.message} with request-id: #{msg.headers['request_id']}"
end
```
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/ManageIQ/manageiq-messaging. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
## License
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).