Author: gsim
Date: Thu Jul 8 09:31:56 2010
New Revision: 961673
URL: http://svn.apache.org/viewvc?rev=961673&view=rev
Log:
Restructuring of chapter on Qpid Messaging API
Modified:
qpid/trunk/qpid/doc/book/src/Programming-In-Apache-Qpid.xml
Modified: qpid/trunk/qpid/doc/book/src/Programming-In-Apache-Qpid.xml
URL:
http://svn.apache.org/viewvc/qpid/trunk/qpid/doc/book/src/Programming-In-Apache-Qpid.xml?rev=961673&r1=961672&r2=961673&view=diff
==============================================================================
--- qpid/trunk/qpid/doc/book/src/Programming-In-Apache-Qpid.xml (original)
+++ qpid/trunk/qpid/doc/book/src/Programming-In-Apache-Qpid.xml Thu Jul 8
09:31:56 2010
@@ -1245,7 +1245,7 @@ spout - -content "$(cat rdu.xml | sed -e
<entry>
one of: unreliable, at-least-once, at-most-once, exactly-once
</entry>
- <entry><!-- from https://issues.apache.org/jira/browse/QPID-2380
-->
+ <entry>
Reliability indicates the level of reliability that
the sender or receiver. <literal>unreliable</literal>
and <literal>at-most-once</literal> are currently
@@ -1255,7 +1255,14 @@ spout - -content "$(cat rdu.xml | sed -e
a message is not lost, but duplicates may be
received. <literal>exactly-once</literal> guarantees
that a message is not lost, and is delivered precisely
- once.
+ once. Currently only <literal>unreliable</literal>
+ and <literal>at-least-once</literal> are supported.
+ <footnote><para>If at-most-once is requested,
+ unreliable will be used and for durable messages on
+ durable queues there is the possibility that messages
+ will be redelivered; if exactly-once is requested,
+ at-most-once will be used and the application needs to
+ be able to deal with duplicates.</para></footnote>
</entry>
</row>
<row>
@@ -1416,53 +1423,84 @@ options := map
</section>
-<section>
- <title>Logging</title>
+<section id="replay">
+ <title>Sender Capacity & Replay</title>
- <para>To simplify debugging, Qpid provides a logging facility
- that prints out messaging events.</para>
+ <para>The send method of a sender has an optional second parameter
+ that controls whether the send call is synchronous or not. A
+ synchronous send call will block until the broker has confirmed
+ receipt of the message. An asynchronous send call will return
+ before the broker confirms receipt of the message, allowing for
+ example further send calls to be made without waiting for a
+ roundtrip to the broker for each message. This is desirable where
+ increased throughput is important.</para>
+
+ <para>The sender maintains a list of sent messages whose receipt
+ has yet to be confirmed by the broker. The maximum number of such
+ messages that it will hold is defined by the capacity of the
+ sender, which can be set by the application. If an application
+ tries to send with a sender whose capacity is already fully used
+ up, the send call will block waiting for capacity regardless of
+ the value of the sync flag.</para>
+
+ <para>The sender can be queried for the available space (i.e. the
+ unused capacity), and for the current count of unsettled messages
+ (i.e. those held in the queue pending confirmation by the
+ server). When the unsettled count is zero, all messages on that
+ sender have been successfully sent.</para>
+
+ <para>If the connection fails and is transparently reconnected
+ (see <xref linkend="connection-options"/> for details on how to control
+ this feature), the unsettled messages for each sender over that
+ connection will be re-transmitted. This provides a transparent
+ level of reliability. This feature can be controlled through the
+ link's reliability as defined in the address (see
+ <xref linkend="table-link-properties"/>). At present only
+ at-least-once guarantees are offered. </para>
+</section>
- <section>
- <title>Logging in C++</title>
- <para>The Qpidd broker and C++ clients can both use environment
- variables to enable logging. Use QPID_LOG_ENABLE to set the
- level of logging you are interested in (trace, debug, info,
- notice, warning, error, or critical):</para>
+<section id="prefetch">
+ <title>Receiver Capacity i.e. Prefetch</title>
-<screen>
-$ export QPID_LOG_ENABLE="warning+"
-</screen>
+ <para>By default, a receiver requests the next message from the
+ server in response to each fetch call, resulting in messages being
+ sent to the receiver one at a time. As in the case of sending, it
+ is often desirable to avoid this roundtrip for each message. This
+ can be achieved by allowing the receiver
+ to <firstterm>prefetch</firstterm> messages in anticipation of
+ fetch calls being made. The receiver needs to be able to store
+ these prefetched messages, the number it can hold is controlled by
+ the receivers capacity.</para>
- <para>The Qpidd broker and C++ clients use QPID_LOG_OUTPUT to
- determine where logging output should be sent. This is either a
- file name or the special values stderr, stdout, or syslog:</para>
+</section>
-<screen>
-export QPID_LOG_TO_FILE="/tmp/myclient.out"
-</screen>
+<section id="acknowledgements">
+ <title>Acknowledging Received Messages</title>
+
+ <para>Applications that receive messages should acknowledge their
+ receipt by calling the session's acknowledge method. As in the
+ case of sending messages, acknowledged transfer of messages to
+ receivers provides at-least-once reliability meaning that the loss
+ of the connection, a client crash or even in the case of durable
+ messages a broker restart does not result in lost messages. Some
+ cases may not require this however and the reliability can be
+ controlled through a link property in the address options (see
+ <xref linkend="table-link-properties"/>).</para>
+
+ <para>The acknowledge call acknowledges all messages received on
+ the session (i.e. all message that have been returned from a fetch
+ call on a receiver created on that session).</para>
+
+ <para>The acknowledge call also support an optional parameter
+ controlling whether the call is synchronous or not. A synchronous
+ acknowledge will block until the server has confirmed that it has
+ received the acknowledgement. In the asynchronous case, when the
+ call returns there is not yet any guarantee that the server has
+ received and processed the acknowledgement. The session may be
+ queried for the number of unsettled acknowledgements; when that
+ count is zero all acknowledgements made for received messages have
+ been successful.</para>
- </section>
-
- <section>
- <title>Logging in Python</title>
- <para>
- The Python client library supports logging using the
standard Python logging module. The easiest way to do logging is to use the
<command>basicConfig()</command>, which reports all warnings and errors:
- </para>
-
-<programlisting>from logging import basicConfig
-basicConfig()
-</programlisting>
- <para>
- Qpidd also provides a convenience method that makes it
easy to specify the level of logging desired. For instance, the following code
enables logging at the <command>DEBUG</command> level:
- </para>
-
-<programlisting>from qpid.log import enable, DEBUG
-enable("qpid.messaging.io", DEBUG)
-</programlisting>
- <para>
- For more information on Python logging, see <ulink
url="http://docs.python.org/lib/node425.html">http://docs.python.org/lib/node425.html</ulink>.
For more information on Qpid logging, use <command>$ pydoc qpid.log</command>.
- </para>
- </section>
</section>
@@ -1470,12 +1508,11 @@ enable("qpid.messaging.io", DEBUG)
<title>Receiving Messages from Multiple Sources</title>
<para>A receiver can only read from one source, but many
- programs need to be able to read messages from many sources,
- preserving the original sequence of the messages. In the Qpid
- Messaging API, a program can ask a session for the <quote>next
- receiver</quote>; that is, the receiver that is responsible for
- the next available message. The following example shows how this
- is done in C++, Python, and .NET C#.
+ programs need to be able to read messages from many sources. In
+ the Qpid Messaging API, a program can ask a session for
+ the <quote>next receiver</quote>; that is, the receiver that is
+ responsible for the next available message. The following
+ example shows how this is done in C++, Python, and .NET C#.
</para>
<para>Note that to use this pattern you must enable prefetching
@@ -1527,413 +1564,102 @@ session.Acknowledge();
</section>
<section>
- <title>Request / Response</title>
- <para>Request / Response applications use the reply-to property,
- described in <xref
- linkend="table-amqp0-10-message-properties"/>, to allow a server
- to respond to the client that sent a message. A server sets up a
- service queue, with a name known to clients. A client creates a
- private queue for the server's response, creates a message for a
- request, sets the request's reply-to property to the address of
- the client's response queue, and sends the request to the
- service queue. The server sends the response to the address
- specified in the request's reply-to property.
- </para>
- <example>
- <title>Request / Response Applications in C++</title>
-
- <para>This example shows the C++ code for a client and server
- that use the request / response pattern.</para>
-
- <para>The server creates a service queue and waits for a
- message to arrive. If it receives a message, it sends a
- message back to the sender.</para>
+ <title>Transactions</title>
- <programlisting><![CDATA[Receiver receiver =
session.createReceiver("service_queue; {create: always}");
+ <para>Sometimes it is useful to be able to group messages
+ transfers - sent and/or received - on a session into atomic
+ grouping. This can be done be creating the session as
+ transactional. On a transactional session sent messages only
+ become available at the target address on commit. Likewise any
+ received and acknowledged messages are only discarded at their
+ source on commit
-Message request = receiver.fetch();
-const Address& address = request.getReplyTo(); // Get "reply-to" from
request ...
-if (address) {
- Sender sender = session.createSender(address); // ... send response to
"reply-to"
- Message response("pong!");
- sender.send(response);
- session.acknowledge();
-}
- ]]></programlisting>
+ <footnote><para>Note that this currently is only true for
+ messages received using a reliable mode
+ e.g. at-least-once. Messages sent by a broker to a receiver in
+ unreliable receiver will be discarded immediately regardless of
+ transctionality.</para></footnote>
- <para>The client creates a sender for the service queue, and
- also creates a response queue that is deleted when the
- client closes the receiver for the response queue. In the C++
- client, if the address starts with the character
- <literal>#</literal>, it is given a unique name.</para>
+ .</para>
+ <example>
+ <title>Transactions</title>
+ <para>C++:</para>
<programlisting><![CDATA[
-Sender sender = session.createSender("service_queue");
-
-Address responseQueue("#response-queue; {create:always, delete:always}");
-Receiver receiver = session.createReceiver(responseQueue);
-
-Message request;
-request.setReplyTo(responseQueue);
-request.setContent("ping");
-sender.send(request);
-Message response = receiver.fetch();
-std::cout << request.getContent() << " -> " << response.getContent() <<
std::endl;
-]]> </programlisting>
-
- <para>The client sends the string <literal>ping</literal> to
- the server. The server sends the response
- <literal>pong</literal> back to the same client, using the
- <varname>replyTo</varname> property.</para>
-
- </example>
+Connection connection(broker);
+Session session = connection.createTransactionalSession();
+...
+if (smellsOk())
+ session.commit();
+else
+ session.rollback();
+ ]]></programlisting>
<!--
- <example>
- <title>Request / Response Applications in Python</title>
- <programlisting>### TODO</programlisting>
- </example>
+ <para>Python</para>
+ <programlisting><![CDATA[
+### TODO
+ ]]></programlisting>
-->
- </section>
- <section id="section-Maps">
- <title>Maps in Message Content</title>
-
- <para>Many messaging applications need to exchange data across
- languages and platforms, using the native datatypes of each
- programming language.</para>
-
- <para>The Qpid Messaging API supports maps in message
- content.
+ </example>
- <footnote><para>Unlike JMS, there is not a specific message type for
- map messages.</para></footnote>
+ </section>
- These maps are supported in each language using
- the conventions of the language. In Java, we implement the
- <classname>MapMessage</classname> interface
-
- <footnote><para>Note that the Qpid JMS client supports
- MapMessages whose values can be nested maps or lists. This is
- not standard JMS behaviour.</para></footnote>
+ <section id="connection-options">
+ <title>Connection Options</title>
- ; in Python, we
- support <classname>dict</classname> and
- <classname>list</classname> in message content; in C++, we
- provide the <classname>Variant::Map</classname> and
- <classname>Variant::List</classname> classes to represent maps
- and lists. In all languages, messages are encoded using AMQP's
- portable datatypes.
+ <para>
+ Aspects of the connections behaviour can be controlled through
+ specifying connection options. For example, connections can be
+ configured to automatically reconnect if the connection to a
+ broker is lost.
</para>
- <tip>
- <para>Because of the differences in type systems among
- languages, the simplest way to provide portable messages is to
- rely on maps, lists, strings, 64 bit signed integers, and
- doubles for messages that need to be exchanged across languages
- and platforms.</para>
- </tip>
-
- <section id="section-Python-Maps">
- <title>Qpid Maps in Python</title>
+ <example>
+ <title>Specifying Connection Options in C++ and Python</title>
- <para>In Python, Qpid supports the <classname>dict</classname> and
<classname>list</classname> types directly in message content. The following
code shows how to send these structures in a message:</para>
+ <para>In C++, these options can be set using
<function>Connection::setOption()</function> or by passing in a set of options
to the constructor. The options can be passed in as a map or in string
form:</para>
- <example>
- <title>Sending Qpid Maps in Python</title>
<programlisting><![CDATA[
-from qpid.messaging import *
-# !!! SNIP !!!
-
-content = {'Id' : 987654321, 'name' : 'Widget', 'percent' : 0.99}
-content['colours'] = ['red', 'green', 'white']
-content['dimensions'] = {'length' : 10.2, 'width' : 5.1,'depth' : 2.0};
-content['parts'] = [ [1,2,5], [8,2,5] ]
-content['specs'] = {'colors' : content['colours'],
- 'dimensions' : content['dimensions'],
- 'parts' : content['parts'] }
-message = Message(content=content)
-sender.send(message)
- ]]> </programlisting>
- </example>
+Connection connection("localhost:5672", "{reconnect: true}");
+try {
+ connection.open();
+ !!! SNIP !!!
+ ]]></programlisting>
+or
- <para>The following table shows the datatypes that can be sent in a
Python map message,
- and the corresponding datatypes that will be received by clients in Java or
C++.</para>
-
+ <programlisting><![CDATA[
+Connection connection("localhost:5672");
+connection.setOption("reconnect", true);
+try {
+ connection.open();
+ !!! SNIP !!!
+ ]]></programlisting>
- <table id="table-Python-Maps" >
- <title>Python Datatypes in Maps</title>
- <tgroup cols="3">
- <thead>
- <row>
- <entry>Python Datatype</entry>
- <entry>→ C++</entry>
- <entry>→ Java</entry>
- </row>
- </thead>
- <tbody>
-
<row><entry>bool</entry><entry>bool</entry><entry>boolean</entry></row>
- <row><entry>int</entry><entry>int64</entry><entry>long</entry></row>
-
<row><entry>long</entry><entry>int64</entry><entry>long</entry></row>
-
<row><entry>float</entry><entry>double</entry><entry>double</entry></row>
-
<row><entry>unicode</entry><entry>string</entry><entry>java.lang.String</entry></row>
-
<row><entry>uuid</entry><entry>qpid::types::Uuid</entry><entry>java.util.UUID</entry></row>
-
<row><entry>dict</entry><entry>Variant::Map</entry><entry>java.util.Map</entry></row>
-
<row><entry>list</entry><entry>Variant::List</entry><entry>java.util.List</entry></row>
- </tbody>
- </tgroup>
- </table>
+ <para>In Python, these options can be set as attributes of the
connection or using named arguments in
+ the <function>Connection</function> constructor:</para>
- </section>
+ <programlisting><![CDATA[
+connection = Connection("localhost:5672", reconnect=True)
+try:
+ connection.open()
+ !!! SNIP !!!
+ ]]></programlisting>
+or
+ <programlisting><![CDATA[
+connection = Connection("localhost:5672")
+connection.reconnect = True
+try:
+ connection.open()
+ !!! SNIP !!!
+ ]]></programlisting>
+ <para>See the reference documentation for details in each
language.</para>
+ </example>
- <section id="section-cpp-Maps">
- <title>Qpid Maps in C++</title>
-
-
- <para>In C++, Qpid defines the the
- <classname>Variant::Map</classname> and
- <classname>Variant::List</classname> types, which can be
- encoded into message content. The following code shows how to
- send these structures in a message:</para>
-
- <example>
- <title>Sending Qpid Maps in C++</title>
- <programlisting><![CDATA[
-using namespace qpid::types;
-
-// !!! SNIP !!!
-
-Message message;
-Variant::Map content;
-content["id"] = 987654321;
-content["name"] = "Widget";
-content["percent"] = 0.99;
-Variant::List colours;
-colours.push_back(Variant("red"));
-colours.push_back(Variant("green"));
-colours.push_back(Variant("white"));
-content["colours"] = colours;
-
-Variant::Map dimensions;
-dimensions["length"] = 10.2;
-dimensions["width"] = 5.1;
-dimensions["depth"] = 2.0;
-content["dimensions"]= dimensions;
-
-Variant::List part1;
-part1.push_back(Variant(1));
-part1.push_back(Variant(2));
-part1.push_back(Variant(5));
-
-Variant::List part2;
-part2.push_back(Variant(8));
-part2.push_back(Variant(2));
-part2.push_back(Variant(5));
-
-Variant::List parts;
-parts.push_back(part1);
-parts.push_back(part2);
-content["parts"]= parts;
-
-Variant::Map specs;
-specs["colours"] = colours;
-specs["dimensions"] = dimensions;
-specs["parts"] = parts;
-content["specs"] = specs;
-
-encode(content, message);
-sender.send(message, true);
-]]> </programlisting>
- </example>
-
- <para>The following table shows the datatypes that can be sent
- in a C++ map message, and the corresponding datatypes that
- will be received by clients in Java and Python.</para>
-
- <table id="table-cpp-Maps">
- <title>C++ Datatypes in Maps</title>
- <tgroup cols="3">
- <thead>
- <row>
- <entry>C++ Datatype</entry>
- <entry>→ Python</entry>
- <entry>→ Java</entry>
- </row>
- </thead>
- <tbody>
-
<row><entry>bool</entry><entry>bool</entry><entry>boolean</entry></row>
- <row><entry>uint16</entry><entry>int |
long</entry><entry>short</entry></row>
- <row><entry>uint32</entry><entry>int |
long</entry><entry>int</entry></row>
- <row><entry>uint64</entry><entry>int |
long</entry><entry>long</entry></row>
- <row><entry>int16</entry><entry>int |
long</entry><entry>short</entry></row>
- <row><entry>int32</entry><entry>int |
long</entry><entry>int</entry></row>
- <row><entry>int64</entry><entry>int |
long</entry><entry>long</entry></row>
-
<row><entry>float</entry><entry>float</entry><entry>float</entry></row>
-
<row><entry>double</entry><entry>float</entry><entry>double</entry></row>
-
<row><entry>string</entry><entry>unicode</entry><entry>java.lang.String</entry></row>
-
<row><entry>qpid::types::Uuid</entry><entry>uuid</entry><entry>java.util.UUID</entry></row>
-
<row><entry>Variant::Map</entry><entry>dict</entry><entry>java.util.Map</entry></row>
-
<row><entry>Variant::List</entry><entry>list</entry><entry>java.util.List</entry></row>
- </tbody>
- </tgroup>
- </table>
- </section>
-
-
-</section>
- <section>
- <title>Performance</title>
- <para>
- Clients can often be made significantly faster by using asynchronous
operations, batching acknowledgements and setting the capacity of receivers to
allow prefetch. The size of a sender's replay buffer can also affect
performance.
- </para>
-
- <section>
- <title>Asynchronous Operations</title>
-
- <para>The send method on a sender takes an optional second
- argument that controls whether the call is synchronous or
- not
-
- <footnote><para>In C++ the send() is asynchronous by default. In
python however it is synchronous by default.</para></footnote>
-
- . A synchronous send will block until the broker confirms
- receipt of the message. Higher throughput is achieved if the
- call returns before this point, allowing further send calls to
- be made. The sender can be queried to find out how many
- unsettled messages there are. Unsettled messages are those
- that have been sent but not yet acknowledged by the broker. An
- unsettled count of zero means that receipt of all messages
- sent using that sender have been confirmed as received by the
- broker.
- </para>
-
- <para>The acknowledge call on a session may also be
- synchronous or asynchronous. The session can be queried for
- the unsettled acknowledgements also, i.e. the number of
- messages received on the session that have been acknowledged
- by the application but for which the acknowledgements have not
- been confirmed by the broker.</para>
- </section>
-
- <section>
- <title>Batching Acknowledgements</title>
-
- <para>Many of the simple examples we have shown retrieve a message and
immediately acknowledge it. Because each acknowledgement results in network
traffic, you can imporve efficiency and increase performance by acknowledging
messages in batches. For instance, an application can receive and process a
number of related messages, then acknowledge the entire batch, or an
application can acknowledge after a certain number of messages have been
received or a certain time period has elapsed.</para>
- </section>
-
-
- <section id="prefetch">
- <title>Prefetch</title>
-
- <para>By default, a receiver retrieves the next message from the
server, one message at a time, in response to each fetch() call. This provides
intuitive results when writing and debugging programs, but does not provide
optimum performance. By allowing the client to prefetch messages in
anticipation of fetch() calls, round-trips to the broker can be avoided and the
throughput for a receiver can be increased. To enable prefetching, set the
capacity of the receiver to the size of the desired input buffer; for many
applications, a capacity of 100 performs well.</para>
-
-<!--
-### sender: capacity of replay buffer (not yet acknowledged messages),
these are resent with failover
--->
-
- <example>
- <title>Prefetch</title>
-
- <para>C++</para>
- <programlisting>
-Receiver receiver = session.createReceiver(address);
-receiver.setCapacity(100);
-Message message = receiver.fetch();
- </programlisting>
-<!--
- <para>Python</para>
- <programlisting>
- </programlisting>
--->
-
- </example>
- </section>
-
- <section>
- <title>Sizing the Replay Buffer</title>
-
- <para>In order to guarantee delivery, a sender automatically
- keeps messages in a replay buffer until the messaging broker
- acknowledges that they have been received. The replay buffer
- is held in memory, and is never paged to disk. For most
- applications, the default size of the replay buffer works
- well. A large replay buffer requires more memory, a small
- buffer can slow down the client because it can not send new
- messages if the replay buffer is full, and must wait for
- existing sends to be acknowledged.</para>
-
- <example>
- <title>Sizing the Replay Buffer</title>
- <para>C++</para>
-
- <programlisting>
-Sender sender = session.createSender(address);
-sender.setCapacity(100);
- </programlisting>
- </example>
-
- </section>
-
- </section>
-
- <section>
- <title>Connection Options</title>
-
- <para>
- Aspects of the connections behaviour can be controlled through
- specifying connection options. For example, connections can be
- configured to automatically reconnect if the connection to a
- broker is lost.
- </para>
-
- <example>
- <title>Specifying Connection Options in C++ and Python</title>
-
- <para>In C++, these options can be set using
<function>Connection::setOption()</function> or by passing in a set of options
to the constructor. The options can be passed in as a map or in string
form:</para>
-
- <programlisting><![CDATA[
-Connection connection("localhost:5672", "{reconnect: true}");
-try {
- connection.open();
- !!! SNIP !!!
- ]]></programlisting>
-
-or
-
- <programlisting><![CDATA[
-Connection connection("localhost:5672");
-connection.setOption("reconnect", true);
-try {
- connection.open();
- !!! SNIP !!!
- ]]></programlisting>
-
- <para>In Python, these options can be set as attributes of the
connection or using named arguments in
- the <function>Connection</function> constructor:</para>
-
- <programlisting><![CDATA[
-connection = Connection("localhost:5672", reconnect=True)
-try:
- connection.open()
- !!! SNIP !!!
- ]]></programlisting>
-
-or
-
- <programlisting><![CDATA[
-connection = Connection("localhost:5672")
-connection.reconnect = True
-try:
- connection.open()
- !!! SNIP !!!
- ]]></programlisting>
-
- <para>See the reference documentation for details in each
language.</para>
- </example>
-
- <para>The following table lists the supported connection options.</para>
+ <para>The following table lists the supported connection options.</para>
<table pgwide="1">
<title>Connection Options</title>
@@ -2061,61 +1787,314 @@ try:
</section>
- <section>
- <title>Reliability</title>
-
- <para>When creating a sender or a receiver, you can specify a
reliability option in the address string. For instance, the following specifies
<literal>at-least-once</literal> as the reliability mode for a sender:</para>
-
- <programlisting>
-Sender = session.createSender("topic;{link:{reliability:at-least-once}}");
- </programlisting>
-
- <para>The recognised modes are <literal>unreliable</literal>,
<literal>at-most-once</literal>, <literal>at-least-once</literal>, and
<literal>exactly-once</literal>. These modes govern the transfer of messages.
Currently only <literal>unreliable</literal> and
<literal>at-least-once</literal> are supported.
+ <section id="section-Maps">
+ <title>Maps in Message Content</title>
+
+ <para>Many messaging applications need to exchange data across
+ languages and platforms, using the native datatypes of each
+ programming language.</para>
- <footnote><para>If at-most-once is requested, unreliable will be
used and for durable messages on durable queues there is the possibility that
messages will be redelivered; if exactly-once is requested, at-most-once will
be used and the application needs to be able to deal with
duplicates.</para></footnote>
+ <para>The Qpid Messaging API supports maps in message
+ content.
- </para>
+ <footnote><para>Unlike JMS, there is not a specific message type for
+ map messages.</para></footnote>
- <para>The <literal>unreliable</literal> mode means that
- messages may be lost in the event of a client, connection or
- broker failure. The broker will not wait for unreliable
- receivers to acknowledge receipt before discarding
- messages. Likewise an unreliable sender will not wait for
- broker acknowledgement before considering the message
- sent.</para>
-
- <para>The <literal>at-least-once</literal> mode ensures that
- messages are not lost in the event of client, connection or
- broker failure
- <footnote><para>To survive broker failure a message must be durably
recorded.</para></footnote>
- . Duplicates of a message may delivered
- however. The broker will not dequeue messages it sends to
- receivers in at-least-once mode until they are
- acknowledged. Messages sent by senders configure with
- at-least-once mode are kept in a replay buffer until the
- broker acknowledges receipt; in the event of a broker or
- connection failure, messages in the replay buffer are resent
- upon reconnection.</para>
+ These maps are supported in each language using
+ the conventions of the language. In Java, we implement the
+ <classname>MapMessage</classname> interface
+
+ <footnote><para>Note that the Qpid JMS client supports
+ MapMessages whose values can be nested maps or lists. This is
+ not standard JMS behaviour.</para></footnote>
- <!-- What is the default? ### -->
+ ; in Python, we
+ support <classname>dict</classname> and
+ <classname>list</classname> in message content; in C++, we
+ provide the <classname>Variant::Map</classname> and
+ <classname>Variant::List</classname> classes to represent maps
+ and lists. In all languages, messages are encoded using AMQP's
+ portable datatypes.
+ </para>
- </section>
+ <tip>
+ <para>Because of the differences in type systems among
+ languages, the simplest way to provide portable messages is to
+ rely on maps, lists, strings, 64 bit signed integers, and
+ doubles for messages that need to be exchanged across languages
+ and platforms.</para>
+ </tip>
- <section>
- <title>Cluster Failover</title>
+ <section id="section-Python-Maps">
+ <title>Qpid Maps in Python</title>
- <para>The messaging broker can be run in clustering mode, which
provides high reliability through replicating state between brokers in the
cluster. If one broker in a cluster fails, clients can choose another broker in
the cluster and continue their work. Each broker in the cluster also advertises
the addresses of all known brokers
+ <para>In Python, Qpid supports the <classname>dict</classname> and
<classname>list</classname> types directly in message content. The following
code shows how to send these structures in a message:</para>
-<footnote><para>This is done via the amq.failover exchange in AMQP
0-10</para></footnote>
+ <example>
+ <title>Sending Qpid Maps in Python</title>
+ <programlisting><![CDATA[
+from qpid.messaging import *
+# !!! SNIP !!!
-. A client can use this information to dynamically keep the list of
reconnection urls up to date.</para>
+content = {'Id' : 987654321, 'name' : 'Widget', 'percent' : 0.99}
+content['colours'] = ['red', 'green', 'white']
+content['dimensions'] = {'length' : 10.2, 'width' : 5.1,'depth' : 2.0};
+content['parts'] = [ [1,2,5], [8,2,5] ]
+content['specs'] = {'colors' : content['colours'],
+ 'dimensions' : content['dimensions'],
+ 'parts' : content['parts'] }
+message = Message(content=content)
+sender.send(message)
+ ]]> </programlisting>
+ </example>
- <para>In C++, the <classname>FailoverUpdates</classname> class provides
this functionality:</para>
- <example>
- <title>Tracking cluster membership</title>
-
- <para>In C++:</para>
+ <para>The following table shows the datatypes that can be sent in a
Python map message,
+ and the corresponding datatypes that will be received by clients in Java or
C++.</para>
+
+
+ <table id="table-Python-Maps" >
+ <title>Python Datatypes in Maps</title>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Python Datatype</entry>
+ <entry>→ C++</entry>
+ <entry>→ Java</entry>
+ </row>
+ </thead>
+ <tbody>
+
<row><entry>bool</entry><entry>bool</entry><entry>boolean</entry></row>
+ <row><entry>int</entry><entry>int64</entry><entry>long</entry></row>
+
<row><entry>long</entry><entry>int64</entry><entry>long</entry></row>
+
<row><entry>float</entry><entry>double</entry><entry>double</entry></row>
+
<row><entry>unicode</entry><entry>string</entry><entry>java.lang.String</entry></row>
+
<row><entry>uuid</entry><entry>qpid::types::Uuid</entry><entry>java.util.UUID</entry></row>
+
<row><entry>dict</entry><entry>Variant::Map</entry><entry>java.util.Map</entry></row>
+
<row><entry>list</entry><entry>Variant::List</entry><entry>java.util.List</entry></row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </section>
+
+
+
+
+ <section id="section-cpp-Maps">
+ <title>Qpid Maps in C++</title>
+
+
+ <para>In C++, Qpid defines the the
+ <classname>Variant::Map</classname> and
+ <classname>Variant::List</classname> types, which can be
+ encoded into message content. The following code shows how to
+ send these structures in a message:</para>
+
+ <example>
+ <title>Sending Qpid Maps in C++</title>
+ <programlisting><![CDATA[
+using namespace qpid::types;
+
+// !!! SNIP !!!
+
+Message message;
+Variant::Map content;
+content["id"] = 987654321;
+content["name"] = "Widget";
+content["percent"] = 0.99;
+Variant::List colours;
+colours.push_back(Variant("red"));
+colours.push_back(Variant("green"));
+colours.push_back(Variant("white"));
+content["colours"] = colours;
+
+Variant::Map dimensions;
+dimensions["length"] = 10.2;
+dimensions["width"] = 5.1;
+dimensions["depth"] = 2.0;
+content["dimensions"]= dimensions;
+
+Variant::List part1;
+part1.push_back(Variant(1));
+part1.push_back(Variant(2));
+part1.push_back(Variant(5));
+
+Variant::List part2;
+part2.push_back(Variant(8));
+part2.push_back(Variant(2));
+part2.push_back(Variant(5));
+
+Variant::List parts;
+parts.push_back(part1);
+parts.push_back(part2);
+content["parts"]= parts;
+
+Variant::Map specs;
+specs["colours"] = colours;
+specs["dimensions"] = dimensions;
+specs["parts"] = parts;
+content["specs"] = specs;
+
+encode(content, message);
+sender.send(message, true);
+]]> </programlisting>
+ </example>
+
+ <para>The following table shows the datatypes that can be sent
+ in a C++ map message, and the corresponding datatypes that
+ will be received by clients in Java and Python.</para>
+
+ <table id="table-cpp-Maps">
+ <title>C++ Datatypes in Maps</title>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>C++ Datatype</entry>
+ <entry>→ Python</entry>
+ <entry>→ Java</entry>
+ </row>
+ </thead>
+ <tbody>
+
<row><entry>bool</entry><entry>bool</entry><entry>boolean</entry></row>
+ <row><entry>uint16</entry><entry>int |
long</entry><entry>short</entry></row>
+ <row><entry>uint32</entry><entry>int |
long</entry><entry>int</entry></row>
+ <row><entry>uint64</entry><entry>int |
long</entry><entry>long</entry></row>
+ <row><entry>int16</entry><entry>int |
long</entry><entry>short</entry></row>
+ <row><entry>int32</entry><entry>int |
long</entry><entry>int</entry></row>
+ <row><entry>int64</entry><entry>int |
long</entry><entry>long</entry></row>
+
<row><entry>float</entry><entry>float</entry><entry>float</entry></row>
+
<row><entry>double</entry><entry>float</entry><entry>double</entry></row>
+
<row><entry>string</entry><entry>unicode</entry><entry>java.lang.String</entry></row>
+
<row><entry>qpid::types::Uuid</entry><entry>uuid</entry><entry>java.util.UUID</entry></row>
+
<row><entry>Variant::Map</entry><entry>dict</entry><entry>java.util.Map</entry></row>
+
<row><entry>Variant::List</entry><entry>list</entry><entry>java.util.List</entry></row>
+ </tbody>
+ </tgroup>
+ </table>
+ </section>
+
+
+</section>
+
+ <section>
+ <title>The Request / Response Pattern</title>
+ <para>Request / Response applications use the reply-to property,
+ described in <xref
+ linkend="table-amqp0-10-message-properties"/>, to allow a server
+ to respond to the client that sent a message. A server sets up a
+ service queue, with a name known to clients. A client creates a
+ private queue for the server's response, creates a message for a
+ request, sets the request's reply-to property to the address of
+ the client's response queue, and sends the request to the
+ service queue. The server sends the response to the address
+ specified in the request's reply-to property.
+ </para>
+ <example>
+ <title>Request / Response Applications in C++</title>
+
+ <para>This example shows the C++ code for a client and server
+ that use the request / response pattern.</para>
+
+ <para>The server creates a service queue and waits for a
+ message to arrive. If it receives a message, it sends a
+ message back to the sender.</para>
+
+ <programlisting><![CDATA[Receiver receiver =
session.createReceiver("service_queue; {create: always}");
+
+Message request = receiver.fetch();
+const Address& address = request.getReplyTo(); // Get "reply-to" from
request ...
+if (address) {
+ Sender sender = session.createSender(address); // ... send response to
"reply-to"
+ Message response("pong!");
+ sender.send(response);
+ session.acknowledge();
+}
+ ]]></programlisting>
+
+ <para>The client creates a sender for the service queue, and
+ also creates a response queue that is deleted when the
+ client closes the receiver for the response queue. In the C++
+ client, if the address starts with the character
+ <literal>#</literal>, it is given a unique name.</para>
+
+ <programlisting><![CDATA[
+Sender sender = session.createSender("service_queue");
+
+Address responseQueue("#response-queue; {create:always, delete:always}");
+Receiver receiver = session.createReceiver(responseQueue);
+
+Message request;
+request.setReplyTo(responseQueue);
+request.setContent("ping");
+sender.send(request);
+Message response = receiver.fetch();
+std::cout << request.getContent() << " -> " << response.getContent() <<
std::endl;
+]]> </programlisting>
+
+ <para>The client sends the string <literal>ping</literal> to
+ the server. The server sends the response
+ <literal>pong</literal> back to the same client, using the
+ <varname>replyTo</varname> property.</para>
+
+ </example>
+<!--
+ <example>
+ <title>Request / Response Applications in Python</title>
+ <programlisting>### TODO</programlisting>
+ </example>
+-->
+ </section>
+
+
+ <section>
+ <title>Performance Tips</title>
+
+ <itemizedlist>
+ <listitem>
+ <para>Consider prefetching messages for receivers (see
+ <xref linkend="prefetch"/>). This helps eliminate roundtrips
+ and increases throughput. Prefetch is disabled by default,
+ and enabling it is the most effective means of improving
+ throughput of received messages.</para>
+ </listitem>
+ <listitem>
+ <para>Send messages asynchronously. Again, this helps
+ eliminate roundtrips and increases throughput. The C++ and
+ .NET clients send asynchronously by default, however the
+ python client defaults to synchronous sends. </para>
+ </listitem>
+ <listitem>
+ <para>Acknowledge messages in batches (see
+ <xref linkend="acknowledgements"/>). Rather than
+ acknowledging each message individually, consider issuing
+ acknowledgements after n messages and/or after a particular
+ duration has elapsed.</para>
+ </listitem>
+ <listitem>
+ <para>Tune the sender capacity (see
+ <xref linkend="replay"/>). If the capacity is too low the
+ sender may block waiting for the broker to confirm receipt
+ of messages, before it can free up more capacity.</para>
+ </listitem>
+ </itemizedlist>
+ </section>
+
+ <section>
+ <title>Cluster Failover</title>
+
+ <para>The messaging broker can be run in clustering mode, which
provides high reliability through replicating state between brokers in the
cluster. If one broker in a cluster fails, clients can choose another broker in
the cluster and continue their work. Each broker in the cluster also advertises
the addresses of all known brokers
+
+<footnote><para>This is done via the amq.failover exchange in AMQP
0-10</para></footnote>
+
+. A client can use this information to dynamically keep the list of
reconnection urls up to date.</para>
+
+ <para>In C++, the <classname>FailoverUpdates</classname> class provides
this functionality:</para>
+
+ <example>
+ <title>Tracking cluster membership</title>
+
+ <para>In C++:</para>
<programlisting><![CDATA[
#include <qpid/messaging/FailoverUpdates.h>
@@ -2145,48 +2124,56 @@ try:
</example>
</section>
- <section>
- <title>Transactions</title>
- <para>Sometimes it is useful to be able to group messages
- transfers - sent and/or received - on a session into atomic
- grouping. This can be done be creating the session as
- transactional. On a transactional session sent messages only
- become available at the target address on commit. Likewise any
- received and acknowledged messages are only discarded at their
- source on commit
- <footnote><para>Note that this currently is only true for
- messages received using a reliable mode
- e.g. at-least-once. Messages sent by a broker to a receiver in
- unreliable receiver will be discarded immediately regardless of
- transctionality.</para></footnote>
+<section>
+ <title>Logging</title>
- .</para>
+ <para>To simplify debugging, Qpid provides a logging facility
+ that prints out messaging events.</para>
- <example>
- <title>Transactions</title>
- <para>C++:</para>
- <programlisting><![CDATA[
-Connection connection(broker);
-Session session = connection.createTransactionalSession();
-...
-if (smellsOk())
- session.commit();
-else
- session.rollback();
- ]]></programlisting>
-<!--
- <para>Python</para>
- <programlisting><![CDATA[
-### TODO
- ]]></programlisting>
--->
- </example>
+ <section>
+ <title>Logging in C++</title>
+ <para>The Qpidd broker and C++ clients can both use environment
+ variables to enable logging. Use QPID_LOG_ENABLE to set the
+ level of logging you are interested in (trace, debug, info,
+ notice, warning, error, or critical):</para>
- </section>
+<screen>
+$ export QPID_LOG_ENABLE="warning+"
+</screen>
+
+ <para>The Qpidd broker and C++ clients use QPID_LOG_OUTPUT to
+ determine where logging output should be sent. This is either a
+ file name or the special values stderr, stdout, or syslog:</para>
+<screen>
+export QPID_LOG_TO_FILE="/tmp/myclient.out"
+</screen>
+ </section>
+
+ <section>
+ <title>Logging in Python</title>
+ <para>
+ The Python client library supports logging using the
standard Python logging module. The easiest way to do logging is to use the
<command>basicConfig()</command>, which reports all warnings and errors:
+ </para>
+
+<programlisting>from logging import basicConfig
+basicConfig()
+</programlisting>
+ <para>
+ Qpidd also provides a convenience method that makes it
easy to specify the level of logging desired. For instance, the following code
enables logging at the <command>DEBUG</command> level:
+ </para>
+
+<programlisting>from qpid.log import enable, DEBUG
+enable("qpid.messaging.io", DEBUG)
+</programlisting>
+ <para>
+ For more information on Python logging, see <ulink
url="http://docs.python.org/lib/node425.html">http://docs.python.org/lib/node425.html</ulink>.
For more information on Qpid logging, use <command>$ pydoc qpid.log</command>.
+ </para>
+ </section>
+</section>
---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project: http://qpid.apache.org
Use/Interact: mailto:[email protected]