3. History of Message Queuing
Manual Telegraphy Machine Assisted Telegraphy
1920s1911 - 192019th Century 1900s
Telegrams sent using
âStore and Forwardâ
1900
1930s
4. History of Message Queuing
Electronic Telegraphy
1950s1940s1950s1940s
Electronic Telegram
Machines, eg Plan 55-A
1948
IBM
M
ne Assisted Telegraphy
1920s1920 1930s
5. History of Message Queuing
Telcos UseElectronic Telegraphy
1950s1940s 1960s
IBM System/360 with
BTAM & QTAM
Message Switching
1964
First Electronic Mail
Solutions
1965
Banking Users
1970s
IBM TCAM which is
the ïŹrst true solution
Retired 1990!
1971
6. History of Message Queuing
l
Financial Trading UsersBanking Users FMCG & Utilities
1980s 1990s
Growth of SMTP
Origins of Tibco in
Stock Price Messaging
1980s
IBM Launch MQSeries
(now WebsphereMQ)
1992
1970s
IBM TCAM which is
the ïŹrst true solution
Retired 1990!
1971
7. History of Message Queuing
l
FMCG & Utilities
1990s
MQSeries
phereMQ)
92
Corporates Large Websites YOU
Noughties Today
Sun Release Java JMS,
Reinvigorating
Enterprise Messaging
2001
AMQP Working
Group Formed by
Investment Banks
2006
Cloud Enables and
Drives StormMQ
Adoption
2009
15. Why Use it: Loose Coupling
Billing
Catalogue
Shipping
S-a-a-S Inventory
How do we connect them, without one outage or system
change taking everything down like a pack of cards?
16. Why Use it: Loose Coupling
Billing
Catalogue
Shipping
S-a-a-S Inventory
How do we connect them, without one outage or system
change taking everything down like a pack of cards?
Message Queuing lets Systems and Components exchange
data, events, commands and actions with one another
with no explicit knowledge or need for them to be online
25. However, AMQP ïŹxes this
A common wire-level binary
format and protocol
An explicit deïŹnition of a
server (aka broker)âs
semantics
Open Means
Interoperable
26. That is good âŠ
âAMQP will be to Messaging what HTTP was to theWebâ
MRG
Clients run on any Platform Vendors are Interoperable
27. That is good âŠ
âAMQP will be to Messaging what HTTP was to theWebâ
MRG
Clients run on any Platform Vendors are Interoperable
64K
33. Quick Recap
For Beer
The ïŹfth male member
of the A-Team, Frankie,
was played by
Question
Message Queuing connects systems and
components. Is it ideal for the cloud?
Does Loose-Coupling make individual
systems more likely to suffer outages?
Yes No
AMQP is Open.
This makes it suitable for programming
in C and Javascript?
34. EddieVelez
Quick Recap
â
The ïŹfth male member
of the A-Team, Frankie,
was played by
Question
Message Queuing connects systems and
components. Is it ideal for the cloud?
Does Loose-Coupling make individual
systems more likely to suffer outages?
Yes No
AMQP is Open.
This makes it suitable for programming
in C and Javascript?
!
â
36. Messaging: Which Jargon?
EMail
(SMTP, POP3, IMAP)
VoIP
(VoiceMail, XMPP)
Texting
(SMS)
Instant Messaging
(ICQ, MSN, Jabber)
Twitter
Enterprise Service Bus
(ESB)
Dynamic OO
Languages
(eg Ruby)
Message Queuing
(MQ)
Message Queuing
(MQ)
What do we
mean by
Messaging?
55. AMQP Client AMQP ServerTCP / IP Network
Connection Virtual Host
Connections and Channels
56. Connection
AMQP Client AMQP ServerTCP / IP Network
Connection
Virtual Host
Connections and Channels
TLS
âShieldingâ
Channels
Each Channel is Independent:
Effectively, aVirtual Connection
57. Basic AMQP: Connections
Basic AMQP: Connections
Open a Connection to aVirtual Host
Open a Channel
Send a Message
Receive a Message
Close Channel
Close Connection
You only need one channel!
"
#
$
%
&
'
How does
this look
in Code?
58. // The API is styled similarly to Functionally Orientated Programming (FOP)
// It makes extensive use of Constructor Injection and anonymous classes modelling 'blocks'
// Closes are handled implicitly by design with exceptions thrown, ie try-ïŹnally is internal to the API
public void demonstrateConnection()
{
ïŹnal AmqpConnectionFactory amqpConnectionFactory =
amqpConnectionFactoryWhichVeriïŹesStormMQ(âmycompanyâ, âmysystemâ, âdevelopmentâ, âuserâ, âpasswordâ);
// Creates and closes a connection
amqpConnectionFactory . useConnection
(
// Creates and closes a channel on a connection
new ChannelCreatingConnectionUser
(
// Adapts a channel to make it convenient to use
new ConvenientChannelCreatingConnectionUser
(
new ConvenientChannelUser()
(
public void use(ïŹnal @NotNull ConvenientChannel convenientChannel)
{
// Send a message, or,
// Receive a message (or both)
}
);
)
)
)
}
59. // Sending a Message
public void use(ïŹnal @NotNull ConvenientChannel convenientChannel)
{
// ConvenientChannel has static convenience methods to create messageHeaders (meta-data)
// Use them to signify a message's contentâs MIME type or an unique id
// Use meta data sparingly; think of it like email headers. More later
ïŹnal BasicProperties messageHeaders = emptyBasicProperties();
// All messages are byte arrays
ïŹnal byte[] messageBody = "HelloWorld" . getBytes( forName("UTF-8") );
// Send the message
ïŹnal String routingKey = "usually the destination message queue";
ïŹnal boolean mandatory = false;
ïŹnal boolean immediate = false;
convenientChannel . basicPublish( "", routingKey, mandatory, immediate, messageHeaders, messageBody );
}
/* Notes
* Mandatory (mandatory = true) messages are returned unless routed to a queue
* Immediate (mandatory = true) messages are returned unless there is another channel consuming
*/
60. // Receiving a Message using Polling
public void use(ïŹnal @NotNull ConvenientChannel convenientChannel)
{
ïŹnal boolean noAck = false;
convenientChannel . basicGet( "queue", noAck, DoNothingWhenMessageNotReceived, new MessageReceived()
{
public void messageReceived(ïŹnal @NotNull GetResponse message)
{
ïŹnal byte[] messageBody = message . getBody();
ïŹnal BasicProperties messageHeaders = message . getProps();
// Acknowledge successfully received messages if noAck = false
// If not acknowledged, they are eventually placed back on the queue for re-delivery
convenientChannel . basicAck( message );
}
});
}
/* Notes
* DoNothingWhenMessageNotReceived is a singleton of MesageNotReceived
* Implement DoNothing to do something else, eg for debugging
* Polling is thread-safe but inefïŹcient; using consumers (âpushâ) for more efïŹciencyâŠ
*/
61. // Consuming Messages using Push
public void use(ïŹnal @NotNull ConvenientChannel convenientChannel)
{
ïŹnal boolean noAck = true;
convenientChannel . basicConsume( "my queue", noAck, new ConvenientConsumer()
{
// Consumers run on a different thread
public void handleDelivery( ïŹnal String consumerTag, ïŹnal Envelope envelope, ïŹnal BasicProperties
messageHeaders, ïŹnal byte[] messageBody )
{
ïŹnal byte[] messageBody = message . getBody();
ïŹnal BasicProperties messageHeaders = message . getProps();
// No need to acknowledge message as noAck = true
}
});
}
/* Notes
* Consumers only exist as long as a channel is open
* Add a threading primitive (eg volatile boolean) and reference it at *
* It is possible to use multiple channels with several consumersâŠ
*/
*
62. !
â
Channels
Complex Applications
Consume on several threads
Send on several threads
Why Use More than One Channel?
Recommendation: Keep it Simple!
Simple Applications
Send on a queue
Receive on another
Transactional Applications
NeedTransactions on a queue
Donât NeedTransactions on another
â
64. Exchanges Route Messages
Exchanges route Messages to Message Queues
You send messages to an exchange, not a message queue
Exchange
MQ
A
MQ
B
65. How do Exchanges Route?
Exchange
MQ
A
The Exchange ïŹnds a Binding
matching the Routing Key
#
A Binding connects a Routing Key
to one or more Message Queues
$
Every sent message has a
Routing Key
"
The Exchange delivers the
Message to the Message Queue
%
A message queue can be
bound more than once to
one or more exchanges
(
âstringâ
66. Types of Exchange
More rarely used exchange types include amq.headers and extensions
Message Queue Name
direct
Like a âMapâ:All MQs
bound with the
routing key receive
copies of the message
ââ (blank)
amq.direct
fanout
Empty String
All MQs bound to the
Exchange receive
copies of the message
amq.fanout
topic
Dotted
Bindings use
globbing expressions
(wildcards) to route
messages to MQs
amq.topic
A message queue can be bound more than once to an exchange;
A message queue can be bound to more than one exchange
But a message queue will only receive a message sent once
Routing Key
Routing
Behaviour
Default
DeïŹnitions
Point â to â Point One â to â Many Publish â SubscribeTypical Use
67. // Declaring Exchanges
public void use(ïŹnal @NotNull ConvenientChannel convenientChannel)
{
ïŹnal boolean passivelyDeclare = false;
ïŹnal boolean durable = true;
ïŹnal boolean autoDelete = false;
// Just change the ExchangeType to one of direct, fanout or topic
convenientChannel . exchangeDeclare( "my exchange A", direct, passivelyDeclare, durable, autoDelete );
convenientChannel . exchangeDeclare( "my exchange B", fanout, passivelyDeclare, durable, autoDelete );
convenientChannel . exchangeDeclare( "my exchange C", topic, passivelyDeclare, durable, autoDelete );
// A passive declaration asserts that an exchange exists, causing a channel exception if it does not
convenientChannel . exchangeDeclare( "my exchange A", direct, true, durable, autoDelete );
}
/* Notes
* Always use durable = true with StormMQ as we are a hosted service
* Nearly always use autoDelete = false as exchanges should be long-lived
* Declaring an exchange twice is not an error; if parameters differ, new values are used if possible
* Passive declaration is interesting but typical deployment practices mitigate its usefulness
*/
68. // Declaring and Binding Message Queues
public void use(ïŹnal @NotNull ConvenientChannel convenientChannel)
{
ïŹnal boolean passivelyDeclare = false;
ïŹnal boolean durable = true;
ïŹnal boolean autoDelete = false;
ïŹnal boolean exclusive = false;
// A declared queue is bound to the default ("") exchange with the routing key the queue name ("my queue")
convenientChannel . queueDeclare( "my queue", passivelyDeclare, durable, exclusive, autoDelete );
// A message queue can be bound to another exchange with whatever key you choose
convenientChannel . queueBind( "my queue", "another exchange", "some routing key" );
// When binding to a fanout exchange the routing key is the empty string ("")
convenientChannel . queueBind( "my queue", "a fanout exchange, eg amq.fanout", "" );
// When binding to a topic exchange, set the routing key as a wildcard
// Publish messages with speciïŹc routing keys eg "BONDS.USD.FTSE", "BONDS.USD.NYSE", "BONDS.GBP.FTSE"
convenientChannel . queueBind( "my queue", "a fanout exchange, eg amq.topic", "BONDS.USD.*" );
}
/* Notes
* Always use durable = true with StormMQ as we are a hosted service
* Nearly always use autoDelete = false as message queues should be long-lived
* Declaring a message queue twice is not an error; if parameters differ, new values are used if possible
* Exclusive message queues are for advanced usages and often arenât needed
*/
69. AMQP: Introducing Users
but
StormMQ enforces that the same
Users and Permissions are in
all of a Systemâs Environments
Passwords are different
Passwords are securely generated
by StormMQ and are 512 bit keys
ConïŹguration Managers can prevent
revelation of passwords to different
system users, eg developers vs sysadmins
Aim: Separation of Environments
A Connection can use several
authentication mechanisms
which are perVirtual Host
but
Commonest is User - Password
andand
Aim: Separation of Responsibility
Users are intended to be system
accounts (robots), not sysadmins
Users have read, write and create
permissions using a wildcard syntax:
important to name queues, etc well
70. // Example of create-system.json used with API call stormmq-create-system
{
"companyName" : "usuallyYourUserName",
"systemName" : "anythingAlphabetic",
"environments" :
[{
"environmentName" : "development",
// Different StormMQ clusters have different characteristics (and costs)
"clusterName" : "free-1",
// These are the StormMQ user accounts to which passwords for AMQP users (below) can be revealed for
this environment (development)
"permittedStormMQUserNames" : [ "developerPeter", "developerJames", "developerJohn" ]
}],
// User permissions operate on Message Queues and Exchanges
"amqpUserPermissions" :
{
"queueReader" : { "create" : "^$", "read" : ".*", "write" : "^$" },
"queueWriter" : { "create" : "^$", "read" : "^$", "write" : ".*" },
"queueCreator" : { "create" : ".*", "read" : "^$", "write" : "^$" },
"onlyProcessingObjects" : { "create" : "^processing.*", "read" : "^processing.*", "write" : "^processing.*" }
}
}
71. ConïŹguring AMQP
â*Our REST API has Java, Ruby, PHP and .NET bindingsâ
AMQP StormMQ
â ActiveMessage Queues
Exchanges
Bindings
Users
User Permissions
Entire Systems
! Extra
! Extra
! Extra
â Active
â Active
â REST
â REST
â REST*
Programmatic ConïŹg
â Same
â Same
â Same
72. AMQP: Getting Statistics
â*Password revelation is restricted to speciïŹc individualsâ
AMQP StormMQ
Message Queues
Exchanges
Bindings
Generated Passwords
Usage Patterns
Entire Systems
! Extra
! Extra
! Extra
â REST
â REST
â REST
Programmatic Info
! Extra â REST
! Extra â REST*
! Extra â REST
73. // Getting Statistics from StormMQ using the REST API in Java
// If you need to use proxies or experience the pain of Java SSL choose a different method in ApiConïŹguration
ïŹnal ApiConïŹguration apiConïŹguration = directConnectionIfHavingProblemsWithSslVeriïŹcation();
// If your Magic Secret Key is in an unusual place choose a different method in Api
ïŹnal Api api = apiByGuessingUsingOsConïŹgurationFiles( apiConïŹguration, stormMqUserName );
ïŹnal List<AmqpQueue> amqpQueues = api . listQueues( companyName, systemName, environmentName );
ïŹnal List<AmqpExchange> amqpExchanges = api . listExchanges( companyName, systemName, environmentName );
ïŹnal List<AmqpBinding> amqpBindings = api . listBindings( companyName, systemName, environmentName );
ïŹnal Map<AmqpUserName, AmqpUserPassword> passwords =
api . listAmqpUserNamesAndPasswords( companyName, systemName, environmentName );
ïŹnal CompanyDetails companyDetails = api . describeCompany( companyName );
// Finding âwhat weâve gotâ
ïŹnal List<CompanyName> companyNames = api . listCompanies();
ïŹnal List<ClusterName> clusterNames = api . listClusters( companyName );
ïŹnal List<SystemName> systemNames = api . listSystems( companyName );
ïŹnal SystemDetails systemDetails = api . describeSystem( companyName, systemName );
ïŹnal Set<Environment> environments = systemDetails . getEnvironments();
/* Notes
* Also consider the command-line tools stormmq-list-queues, stormmq-list-exchanges and stormmq-list-bindings
* Further examples are in com.stormmq.api.Example (part of the url signer jar)
*/
74. Virtual Hosts
Virtual Hosts: Think Apache!
Virtual Hosts isolate Message Queues
Virtual Hosts isolate User Groups*
Virtual Hosts isolate Exchanges*
Virtual Hosts isolate Bindings*
Virtual Hosts isolate
Virtual Hosts: StormMQ
Virtual Hosts isolateYour Company
Virtual Hosts isolateYour Systems
Virtual Hosts isolateYour Environments
Enable ConïŹguration Management
Virtual Hosts stop Data Accidents
Widgets Ltd /
Invoicing System /
/
Testing
Development
Production
75. Message Properties
Server Understood Good Practice
âThis is just the tip of messaging best practice with AMQPâ
Persistent = 1
Priority = 0
Timestamp = 4568965345
MessageId = 567A-GH-20100709
Priority Queues are per-Message
Control life of a Message
Persistence is per-Message
Timestamps expire Messages
Completely Optional
MIME Content-Type
Unique MessageId
Schema &Version
76. // Message Properties: Examples of Good Practice
// Being explicit is good practice
ïŹnal BasicProperties messageHeaders = new BasicProperties();
{{
DeliveryMode . NonPersistent . setOn( this );
Priority . Zero . setOn( this );
setMessageId( "My unique ID, eg a GUID or ascending value" );
// Explicit MIME type including charset; use application/octect-stream for trully binary data
setContentType("application/json; charset=utf-8");
// Use compression for larger textual data; use the same values as HTTP headers; use "identity" for none
setContentEncoding( "gzip" );
// Timestamp each message
setTimestamp( new Date( System . currentTimeMillis() ) );
// Schema and version, perhaps using a XML namespace deïŹnition
setType( "syslog-message_1.0.4" );
// Optional: Use an expiration to discard old messages that are stale (milliseconds, example is 5 minutes)
setExpiration( Integer . toString( 5 * 60 * 1000) );
// Optional: Identify the sender for later debugging
setAppId( "sending application name" );
setUserId( "role of sender, or, user of machine" );
// Optional: Identify domain or environment (consumer could error if working in different one)
setClusterId( "production" );
}};
77. And there is more!
Transactions
More Message
Properties
Custom Message
Properties
Immediate Delivery
Additional Exchange
Types
Auto Deletion
Fine-Grained User
Permissions
Queue
Purging
QoS