| <?xml version="1.0" encoding="utf-8"?> |
| <!-- |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| |
| --> |
| |
| <section> |
| <title> |
| Apache Qpid: Open Source AMQP Messaging - .NET User Guide |
| </title> |
| <section role="h1" id="NETUserGuide-Tutorial"> |
| <title> |
| Tutorial |
| </title> |
| <para> |
| This tutorial consists of a series of examples using the three |
| most commonly used exchange types - Direct, Fanout and |
| Topic |
| exchanges. These examples show how to write applications that use |
| the most common messaging paradigms. |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>direct</para> |
| <para>In the direct examples, a message producer writes to the direct |
| exchange, specifying a routing key. A message consumer reads |
| messages from a named queue. This illustrates clean separation |
| of concerns - message producers need to know only the exchange |
| and the routing key, message consumers need to know only which |
| queue to use on the broker. |
| </para> |
| </listitem> |
| <listitem> |
| <para>fanout</para> |
| <para>The fanout examples use a fanout exchange and do not use |
| routing keys. Each binding specifies that all messages for a |
| given exchange should be delivered to a given queue. |
| </para> |
| </listitem> |
| <listitem> |
| <para>pub-sub</para> |
| <para>In the publish/subscribe examples, a publisher |
| application writes messages to an exchange, specifying a |
| multi-part key. A subscriber application subscribes to |
| messages that match the relevant parts of these keys, using a |
| private queue for each subscription. |
| </para> |
| </listitem> |
| <listitem> |
| <para>request-response</para> |
| <para>In the request/response examples, a simple service accepts |
| requests from clients and sends responses back to them. Clients |
| create their own private queues and corresponding routing keys. |
| When a client sends a request to the server, it specifies its |
| own routing key in the reply-to field of the request. The |
| server uses the client's reply-to field as the routing key for |
| the response. |
| </para> |
| </listitem> |
| </itemizedlist> |
| <section role="h2" id="NETUserGuide-RunningtheExamples"> |
| <title> |
| Running the |
| Examples |
| </title> |
| <para> |
| Before running the examples, you need to unzip the file |
| Qpid.NET-net-2.0-M4.zip, the following tree is created: |
| </para> |
| |
| |
| <programlisting> |
| <home> |
| |-qpid |
| |-lib (contains the required dlls) |
| |-examples |
| |- direct |
| | |-example-direct-Listener.exe |
| | |-example-direct-Producer.exe |
| |- fanout |
| | |-example-fanout-Listener.exe |
| | |-example-fanout-Producer.exe |
| |- pub-sub |
| | |-example-pub-sub-Listener.exe |
| | |-example-pub-sub-Publisher.exe |
| |- request-response |
| |-example-request-response-Client.exe |
| |-example-request-response-Server.exe |
| </programlisting> |
| |
| |
| <para> |
| Make sure your PATH contains the directory |
| <home>/qpid/lib |
| The examples can be run by executing the provided exe files: |
| </para> |
| |
| |
| <programlisting> |
| $ cd <home>/qpid/examples/examplefolder |
| $ example-...-.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| where [hostname] is the qpid broker host name |
| (default is localhost) and [portnumber] is the port number on which the |
| qpid broker is accepting connection (default is 5672). |
| </para> |
| <!--h2--> |
| </section> |
| |
| <section role="h2" id="NETUserGuide-CreatingandClosingSessions"> |
| <title> |
| Creating |
| and Closing Sessions |
| </title> |
| |
| <para> |
| All of the examples have been written using the Apache Qpid .NEt |
| 0.10 API. The examples use the same skeleton code to initialize |
| the program, create a session, and clean up before exiting: |
| </para> |
| |
| |
| <programlisting> |
| using System; |
| using System.IO; |
| using System.Text; |
| using System.Threading; |
| using org.apache.qpid.client; |
| using org.apache.qpid.transport; |
| |
| ... |
| |
| private static void Main(string[] args) |
| { |
| string host = args.Length > 0 ? args[0] : "localhost"; |
| int port = args.Length > 1 ? Convert.ToInt32(args[1]) : 5672; |
| Client connection = new Client(); |
| try |
| { |
| connection.connect(host, port, "test", "guest", "guest"); |
| ClientSession session = connection.createSession(50000); |
| |
| //--------- Main body of program -------------------------------------------- |
| |
| connection.close(); |
| } |
| catch (Exception e) |
| { |
| Console.WriteLine("Error: \n" + e.StackTrace); |
| } |
| } |
| ... |
| </programlisting> |
| <!--h2--> |
| </section> |
| |
| <section role="h2" id="NETUserGuide-WritingDirectApplications"> |
| <title> |
| Writing |
| Direct Applications |
| </title> |
| |
| <para> |
| This section describes two programs that implement direct |
| messaging using a Direct exchange: |
| • org.apache.qpid.example.direct.Producer (from |
| example-direct-producer) publishes messages to the amq.direct |
| exchange, using the routing key routing_key. |
| •org.apache.qpid.example.direct.Listener (from |
| example-direct-Listener) uses a message listener to receive |
| messages from the queue named message_queue. |
| </para> |
| <section role="h3" id="NETUserGuide-RunningtheDirectExamples"> |
| <title> |
| Running the |
| Direct Examples |
| </title> |
| <para> |
| 1) Make sure your PATH contains the directory |
| <home>/qpid/lib |
| </para> |
| <para> |
| 2) Make sure that a qpid broker is running: |
| </para> |
| |
| |
| <programlisting> |
| $ ps -eaf | grep qpidd |
| </programlisting> |
| |
| |
| <para> |
| If a broker is running, you should see the qpidd process in the |
| output of the above |
| command. |
| </para> |
| <para> |
| 3) Read the messages from the message queue using direct |
| listener, as follows: |
| </para> |
| |
| |
| <programlisting> |
| $ cd <home>/qpid/examples/direct |
| </programlisting> |
| |
| |
| <para> |
| With cygwin: |
| </para> |
| |
| |
| <programlisting> |
| $ ./example-direct-Listener.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| or with mono: |
| </para> |
| |
| |
| <programlisting> |
| $ mono ./example-direct-Listener.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| This program is waiting for messages to be published, see next |
| step: |
| </para> |
| <para> |
| 4) Publish a series of messages to the amq.direct exchange by |
| running direct producer, as follows: |
| </para> |
| |
| |
| <programlisting> |
| $ cd <home>/qpid/examples/direct |
| </programlisting> |
| |
| |
| <para> |
| With cygwin: |
| </para> |
| |
| |
| <programlisting> |
| $ ./example-direct-Producer.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| or with mono: |
| </para> |
| |
| |
| <programlisting> |
| $ mono ./example-direct-Producer.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| This program has no output; the messages are routed to the |
| message queue, as instructed by the binding. |
| </para> |
| <para> |
| 5) Go to the windows where you are running your listener. You |
| should see the following output: |
| </para> |
| |
| |
| <programlisting> |
| Message: Message 0 |
| Message: Message 1 |
| Message: Message 2 |
| Message: Message 3 |
| Message: Message 4 |
| Message: Message 5 |
| Message: Message 6 |
| Message: Message 7 |
| Message: Message 8 |
| Message: Message 9 |
| Message: That's all, folks! |
| </programlisting> |
| |
| |
| <para> |
| Now we will examine the code for each of these programs. In each |
| section, we will discuss only |
| the code that must be added to the skeleton shown in Section |
| "Creating and Closing Sessions". |
| </para> |
| <!--h3--> |
| </section> |
| |
| <section role="h3" id="NETUserGuide-ReadingMessagesfromtheQueue"> |
| <title> |
| Reading |
| Messages from the Queue |
| </title> |
| |
| <para> |
| The program , listener.cs, is a message listener that receives |
| messages from a queue. |
| </para> |
| <para> |
| First it creates a queue named message_queue, then binds it to |
| the amq.direct exchange using the binding key routing_key. |
| </para> |
| |
| |
| <programlisting> |
| //--------- Main body of program -------------------------------------------- |
| // Create a queue named "message_queue", and route all messages whose |
| // routing key is "routing_key" to this newly created queue. |
| session.queueDeclare("message_queue"); |
| session.exchangeBind("message_queue", "amq.direct", "routing_key"); |
| </programlisting> |
| |
| |
| <para> |
| The queue created by this program continues to exist after the |
| program exits, and any message whose routing key matches the key |
| specified in the binding will be routed to the corresponding |
| queue by the broker. Note that the queue could have been be |
| deleted using the following code: |
| </para> |
| |
| |
| <programlisting> |
| session.queueDelete("message_queue"); |
| </programlisting> |
| |
| |
| <para> |
| To create a message listener, create a class derived from |
| IMessageListener, and override the messageTransfer method, |
| providing the code that should be executed when a message is |
| received. |
| </para> |
| |
| |
| <programlisting> |
| public class MessageListener : IMessageListener |
| { |
| ...... |
| public void messageTransfer(IMessage m) |
| { |
| ..... |
| } |
| </programlisting> |
| |
| |
| <para> |
| The main body of the program creates a listener for the |
| subscription; attaches the listener to a message queue; and |
| subscribe to the queue to receive messages from the queue. |
| </para> |
| |
| |
| <programlisting> |
| lock (session) |
| { |
| // Create a listener and subscribe it to the queue named "message_queue" |
| IMessageListener listener = new MessageListener(session); |
| session.attachMessageListener(listener, "message_queue"); |
| session.messageSubscribe("message_queue"); |
| // Receive messages until all messages are received |
| Monitor.Wait(session); |
| } |
| </programlisting> |
| |
| |
| <para> |
| The MessageListener's messageTransfer() function is called |
| whenever a message is received. In this example the message is |
| printed and tested to see if it is the final message. Once the |
| final message is received, the messages are acknowledged. |
| </para> |
| |
| |
| <programlisting> |
| BinaryReader reader = new BinaryReader(m.Body, Encoding.UTF8); |
| byte[] body = new byte[m.Body.Length - m.Body.Position]; |
| reader.Read(body, 0, body.Length); |
| ASCIIEncoding enc = new ASCIIEncoding(); |
| string message = enc.GetString(body); |
| Console.WriteLine("Message: " + message); |
| // Add this message to the list of message to be acknowledged |
| _range.add(m.Id); |
| if( message.Equals("That's all, folks!") ) |
| { |
| // Acknowledge all the received messages |
| _session.messageAccept(_range); |
| lock(_session) |
| { |
| Monitor.Pulse(_session); |
| } |
| } |
| </programlisting> |
| <!--h3--> |
| </section> |
| |
| <section role="h3" id="NETUserGuide-PublishingMessagestoaDirectExchange"> |
| <title> |
| Publishing |
| Messages to a Direct Exchange |
| </title> |
| <para> |
| The second program in the direct example, Producer.cs, publishes |
| messages to the amq.direct exchange using the routing key |
| routing_key. |
| </para> |
| <para> |
| First, create a message and set a routing key. The same routing |
| key will be used for each message we send, so you only need to |
| set this property once. |
| </para> |
| |
| |
| <programlisting> |
| IMessage message = new Message(); |
| // The routing key is a message property. We will use the same |
| // routing key for each message, so we'll set this property |
| // just once. (In most simple cases, there is no need to set |
| // other message properties.) |
| message.DeliveryProperties.setRoutingKey("routing_key"); |
| </programlisting> |
| |
| |
| <para> |
| Now send some messages: |
| </para> |
| |
| |
| <programlisting> |
| // Asynchronous transfer sends messages as quickly as |
| // possible without waiting for confirmation. |
| for (int i = 0; i < 10; i++) |
| { |
| message.clearData(); |
| message.appendData(Encoding.UTF8.GetBytes("Message " + i)); |
| session.messageTransfer("amq.direct", message); |
| } |
| </programlisting> |
| |
| |
| <para> |
| Send a final synchronous message to indicate termination: |
| </para> |
| |
| |
| <programlisting> |
| // And send a syncrhonous final message to indicate termination. |
| message.clearData(); |
| message.appendData(Encoding.UTF8.GetBytes("That's all, folks!")); |
| session.messageTransfer("amq.direct", "routing_key", message); |
| session.sync(); |
| </programlisting> |
| <!--h3--> |
| </section> |
| |
| <!--h2--> |
| </section> |
| |
| |
| <section role="h2" id="NETUserGuide-WritingFanoutApplications"> |
| <title> |
| Writing |
| Fanout Applications |
| </title> |
| |
| <para> |
| This section describes two programs that illustrate the use of a |
| Fanout exchange. |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>Listener.cs makes a unique queue private for each instance of |
| the listener, and binds that queue to the fanout exchange. All |
| messages sent to the fanout exchange are delivered to each |
| listener's queue. |
| </para> |
| </listitem> |
| <listitem> |
| <para>Producer.cs publishes messages to the fanout exchange. It |
| does not use a routing key, which is not needed by the fanout |
| exchange. |
| </para> |
| </listitem> |
| </itemizedlist> |
| <section role="h3" id="NETUserGuide-RunningtheFanoutExamples"> |
| <title> |
| Running the |
| Fanout Examples |
| </title> |
| |
| <para> |
| 1) Make sure your PATH contains the directory |
| <home>/qpid/lib |
| </para> |
| <para> |
| 2) Make sure that a qpid broker is running: |
| </para> |
| |
| |
| <programlisting> |
| $ ps -eaf | grep qpidd |
| </programlisting> |
| |
| |
| <para> |
| If a broker is running, you should see the qpidd process in the |
| output of the above |
| command. |
| </para> |
| <para> |
| 3) In separate windows, start one or more fanout listeners as |
| follows: |
| </para> |
| |
| |
| <programlisting> |
| $ cd <home>/qpid/examples/direct |
| </programlisting> |
| |
| |
| <para> |
| With cygwin: |
| </para> |
| |
| |
| <programlisting> |
| $ ./example-fanout-Listener.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| or with mono: |
| </para> |
| |
| |
| <programlisting> |
| $ mono ./example-fanout-Listener.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| The listener creates a private queue, binds it to the amq.fanout |
| exchange, and waits for messages to arrive on the queue. When the |
| listener starts, you will see the following message: |
| </para> |
| |
| |
| <programlisting> |
| Listening |
| </programlisting> |
| |
| |
| <para> |
| This program is waiting for messages to be published, see next |
| step: |
| </para> |
| <para> |
| 4) In a separate window, publish a series of messages to the |
| amq.fanout exchange by running fanout producer, as follows: |
| </para> |
| |
| |
| <programlisting> |
| $ cd <home>/qpid/examples/direct |
| </programlisting> |
| |
| |
| <para> |
| With cygwin: |
| </para> |
| |
| |
| <programlisting> |
| $ ./example-fanout-Producer.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| or with mono: |
| </para> |
| |
| |
| <programlisting> |
| $ mono ./example-fanout-Producer.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| This program has no output; the messages are routed to the |
| message queue, as prescribed by the binding. |
| </para> |
| <para> |
| 5) Go to the windows where you are running listeners. You should |
| see the following output for each listener: |
| </para> |
| |
| |
| <programlisting> |
| Message: Message 0 |
| Message: Message 1 |
| Message: Message 2 |
| Message: Message 3 |
| Message: Message 4 |
| Message: Message 5 |
| Message: Message 6 |
| Message: Message 7 |
| Message: Message 8 |
| Message: Message 9 |
| Message: That's all, folks! |
| </programlisting> |
| |
| |
| <para> |
| Now we will examine the code for each of these programs. In each |
| section, we will discuss only |
| the code that must be added to the skeleton shown in Section |
| "Creating and Closing Sessions". |
| </para> |
| |
| <!--h3--> |
| </section> |
| |
| <!--h2--> |
| </section> |
| |
| <section role="h2" id="NETUserGuide-ConsumingfromaFanoutExchange"> |
| <title> |
| Consuming from a |
| Fanout Exchange |
| </title> |
| |
| <para> |
| The first program in the fanout example, Listener.cs, creates a |
| private queue, binds it to the amq.fanout exchange, and waits for |
| messages to arrive on the queue, printing them out as they |
| arrive. It uses a Listener that is identical to the one used in |
| the direct example: |
| </para> |
| |
| |
| <programlisting> |
| public class MessageListener : IMessageListener |
| { |
| private readonly ClientSession _session; |
| private readonly RangeSet _range = new RangeSet(); |
| public MessageListener(ClientSession session) |
| { |
| _session = session; |
| } |
| |
| public void messageTransfer(IMessage m) |
| { |
| BinaryReader reader = new BinaryReader(m.Body, Encoding.UTF8); |
| byte[] body = new byte[m.Body.Length - m.Body.Position]; |
| reader.Read(body, 0, body.Length); |
| ASCIIEncoding enc = new ASCIIEncoding(); |
| string message = enc.GetString(body); |
| Console.WriteLine("Message: " + message); |
| // Add this message to the list of message to be acknowledged |
| _range.add(m.Id); |
| if (message.Equals("That's all, folks!")) |
| { |
| // Acknowledge all the received messages |
| _session.messageAccept(_range); |
| lock (_session) |
| { |
| Monitor.Pulse(_session); |
| } |
| } |
| } |
| } |
| </programlisting> |
| |
| |
| <para> |
| The listener creates a private queue to receive its messages and |
| binds it to the fanout exchange: |
| </para> |
| |
| |
| <programlisting> |
| string myQueue = session.Name; |
| session.queueDeclare(myQueue, Option.EXCLUSIVE, Option.AUTO_DELETE); |
| session.exchangeBind(myQueue, "amq.fanout", "my-key"); |
| </programlisting> |
| |
| |
| <para> |
| Now we create a listener and subscribe it to the queue: |
| </para> |
| |
| |
| <programlisting> |
| lock (session) |
| { |
| Console.WriteLine("Listening"); |
| // Create a listener and subscribe it to my queue. |
| IMessageListener listener = new MessageListener(session); |
| session.attachMessageListener(listener, myQueue); |
| session.messageSubscribe(myQueue); |
| // Receive messages until all messages are received |
| Monitor.Wait(session); |
| } |
| </programlisting> |
| |
| |
| <section role="h3" id="NETUserGuide-PublishingMessagestotheFanoutExchange"> |
| <title> |
| Publishing |
| Messages to the Fanout Exchange |
| </title> |
| |
| <para> |
| The second program in this example, Producer.cs, writes messages |
| to the fanout queue. |
| </para> |
| |
| |
| <programlisting> |
| // Unlike topic exchanges and direct exchanges, a fanout |
| // exchange need not set a routing key. |
| IMessage message = new Message(); |
| // Asynchronous transfer sends messages as quickly as |
| // possible without waiting for confirmation. |
| for (int i = 0; i < 10; i++) |
| { |
| message.clearData(); |
| message.appendData(Encoding.UTF8.GetBytes("Message " + i)); |
| session.messageTransfer("amq.fanout", message); |
| } |
| |
| // And send a syncrhonous final message to indicate termination. |
| message.clearData(); |
| message.appendData(Encoding.UTF8.GetBytes("That's all, folks!")); |
| session.messageTransfer("amq.fanout", message); |
| session.sync(); |
| </programlisting> |
| |
| <!--h3--> |
| </section> |
| |
| <!--h2--> |
| </section> |
| |
| <section role="h2" id="NETUserGuide-WritingPublish-2FSubscribeApplications"> |
| <title> |
| Writing |
| Publish/Subscribe Applications |
| </title> |
| |
| <para> |
| This section describes two programs that implement |
| Publish/Subscribe messaging using a topic exchange. |
| </para> |
| <para> |
| • Publisher.cS sends messages to the amq.topic exchange, |
| using the multipart routing keys usa.news, usa.weather, |
| europe.news, and europe.weather. |
| • Listener.cs creates private queues for news, weather, |
| usa, and europe, binding them to the amq.topic exchange using |
| bindings that match the corresponding parts of the multipart |
| routing keys. |
| </para> |
| <para> |
| In this example, the publisher creates messages for topics like |
| news, weather, and sports that happen in regions like Europe, |
| Asia, or the United States. A given consumer may be interested in |
| all weather messages, regardless of region, or it may be |
| interested in news and weather for the United States, but |
| uninterested in items for other regions. In this example, each |
| consumer sets up its own private queues, which receive precisely |
| the messages that particular consumer is interested in. |
| </para> |
| <section role="h3" id="NETUserGuide-RunningthePublishSubscribeExamples"> |
| <title> |
| Running |
| the Publish-Subscribe Examples |
| </title> |
| <para> |
| 1) Make sure your PATH contains the directory |
| <home>/qpid/lib |
| </para> |
| <para> |
| 2) Make sure that a qpid broker is running: |
| </para> |
| |
| |
| <programlisting> |
| $ ps -eaf | grep qpidd |
| </programlisting> |
| |
| |
| <para> |
| If a broker is running, you should see the qpidd process in the |
| output of the above |
| command. |
| </para> |
| <para> |
| 3) In separate windows, start one or more topic subscribers as |
| follows: |
| </para> |
| |
| |
| <programlisting> |
| $ cd <home>/qpid/examples/direct |
| </programlisting> |
| |
| |
| <para> |
| With cygwin: |
| </para> |
| |
| |
| <programlisting> |
| $ ./example-pub-sub--Listener.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| or with mono: |
| </para> |
| |
| |
| <programlisting> |
| $ mono ./example-pub-sub-Listener.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| You will see output similar to this: |
| </para> |
| |
| |
| <programlisting> |
| Listening for messages ... |
| Declaring queue: usa |
| Declaring queue: europe |
| Declaring queue: news |
| Declaring queue: weather |
| </programlisting> |
| |
| |
| <para> |
| Each topic consumer creates a set of private queues, and binds |
| each queue to the amq.topic exchange together with a binding that |
| indicates which messages should be routed to the queue. |
| </para> |
| <para> |
| 4) In another window, start the topic publisher, which publishes |
| messages to the amq.topic exchange, as follows: |
| </para> |
| |
| |
| <programlisting> |
| $ cd <home>/qpid/examples/direct |
| </programlisting> |
| |
| |
| <para> |
| With cygwin: |
| </para> |
| |
| |
| <programlisting> |
| $ ./example-pub-sub-Producer.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| or with mono: |
| </para> |
| |
| |
| <programlisting> |
| $ mono ./example-pub-sub-Producer.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| This program has no output; the messages are routed to the |
| message queues for each topic_consumer as specified by the |
| bindings the consumer created. |
| </para> |
| <para> |
| 5) Go back to the window for each topic consumer. You should see |
| output like this: |
| </para> |
| |
| |
| <programlisting> |
| Message: Message 0 from usa |
| Message: Message 0 from news |
| Message: Message 0 from weather |
| Message: Message 1 from usa |
| Message: Message 1 from news |
| Message: Message 2 from usa |
| Message: Message 2 from news |
| Message: Message 3 from usa |
| Message: Message 3 from news |
| Message: Message 4 from usa |
| Message: Message 4 from news |
| Message: Message 5 from usa |
| Message: Message 5 from news |
| Message: Message 6 from usa |
| Message: Message 6 from news |
| Message: Message 7 from usa |
| Message: Message 7 from news |
| Message: Message 8 from usa |
| Message: Message 8 from news |
| Message: Message 9 from usa |
| .... |
| Message: That's all, folks! from weather |
| Shutting down listener for control |
| Message: That's all, folks! from europe |
| Shutting down listener for control |
| </programlisting> |
| |
| |
| <para> |
| Now we will examine the code for each of these programs. In each |
| section, we will discuss only |
| the code that must be added to the skeleton shown in Section |
| "Creating and Closing Sessions". |
| </para> |
| <!--h3--> |
| </section> |
| |
| |
| <section role="h3" id="NETUserGuide-PublishingMessagestoaTopicExchange"> |
| <title> |
| Publishing |
| Messages to a Topic Exchange |
| </title> |
| |
| <para> |
| The first program in the publish/subscribe example, Publisher.cs, |
| defines two new functions: one that publishes messages to the |
| topic exchange, and one that indicates that no more messages are |
| coming. |
| </para> |
| <para> |
| The publishMessages function publishes a series of five messages |
| using the specified routing key. |
| </para> |
| |
| |
| <programlisting> |
| private static void publishMessages(ClientSession session, string routing_key) |
| { |
| IMessage message = new Message(); |
| // Asynchronous transfer sends messages as quickly as |
| // possible without waiting for confirmation. |
| for (int i = 0; i < 10; i++) |
| { |
| message.clearData(); |
| message.appendData(Encoding.UTF8.GetBytes("Message " + i)); |
| session.messageTransfer("amq.topic", routing_key, message); |
| } |
| } |
| </programlisting> |
| |
| |
| <para> |
| The noMoreMessages function signals the end of messages using the |
| control routing key, which is reserved for control messages. |
| </para> |
| |
| |
| <programlisting> |
| private static void noMoreMessages(ClientSession session) |
| { |
| IMessage message = new Message(); |
| // And send a syncrhonous final message to indicate termination. |
| message.clearData(); |
| message.appendData(Encoding.UTF8.GetBytes("That's all, folks!")); |
| session.messageTransfer("amq.topic", "control", message); |
| session.sync(); |
| } |
| </programlisting> |
| |
| |
| <para> |
| In the main body of the program, messages are published using |
| four different routing keys, and then the end of messages is |
| indicated by a message sent to a separate routing key. |
| </para> |
| |
| |
| <programlisting> |
| publishMessages(session, "usa.news"); |
| publishMessages(session, "usa.weather"); |
| publishMessages(session, "europe.news"); |
| publishMessages(session, "europe.weather"); |
| |
| noMoreMessages(session); |
| </programlisting> |
| <!--h3--> |
| </section> |
| |
| <section role="h3" id="NETUserGuide-ReadingMessagesfromtheQueue2"> |
| <title> |
| Reading |
| Messages from the Queue |
| </title> |
| |
| <para> |
| The second program in the publish/subscribe example, Listener.cs, |
| creates a local private queue, with a unique name, for each of |
| the four binding keys it specifies: usa.#, europe.#, #.news, and |
| #.weather, and creates a listener. |
| </para> |
| |
| |
| <programlisting> |
| Console.WriteLine("Listening for messages ..."); |
| // Create a listener |
| prepareQueue("usa", "usa.#", session); |
| prepareQueue("europe", "europe.#", session); |
| prepareQueue("news", "#.news", session); |
| prepareQueue("weather", "#.weather", session); |
| </programlisting> |
| |
| |
| <para> |
| The prepareQueue() method creates a queue using a queue name and |
| a routing key supplied as arguments it then attaches a listener |
| with the session for the created queue and subscribe for this |
| receiving messages from the queue: |
| </para> |
| |
| |
| <programlisting> |
| // Create a unique queue name for this consumer by concatenating |
| // the queue name parameter with the Session ID. |
| Console.WriteLine("Declaring queue: " + queue); |
| session.queueDeclare(queue, Option.EXCLUSIVE, Option.AUTO_DELETE); |
| |
| // Route messages to the new queue if they match the routing key. |
| // Also route any messages to with the "control" routing key to |
| // this queue so we know when it's time to stop. A publisher sends |
| // a message with the content "That's all, Folks!", using the |
| // "control" routing key, when it is finished. |
| |
| session.exchangeBind(queue, "amq.topic", routing_key); |
| session.exchangeBind(queue, "amq.topic", "control"); |
| |
| // subscribe the listener to the queue |
| IMessageListener listener = new MessageListener(session); |
| session.attachMessageListener(listener, queue); |
| session.messageSubscribe(queue); |
| </programlisting> |
| <!--h3--> |
| </section> |
| <!--h2--> |
| </section> |
| |
| <section role="h2" id="NETUserGuide-WritingRequest-2FResponseApplications"> |
| <title> |
| Writing |
| Request/Response Applications |
| </title> |
| |
| <para> |
| In the request/response examples, we write a server that accepts |
| strings from clients and converts them to upper case, sending the |
| result back to the requesting client. This example consists of |
| two programs. |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>Client.cs is a client application that sends messages to the |
| server. |
| • Server.cs is a service that accepts messages, converts |
| their content to upper case, and sends the result to the |
| amq.direct exchange, using the request's reply-to property as |
| the routing key for the response. |
| </para> |
| </listitem> |
| </itemizedlist> |
| <section role="h3" id="NETUserGuide-RunningtheRequest-2FResponseExamples"> |
| <title> |
| Running |
| the Request/Response Examples |
| </title> |
| <para> |
| 1) Make sure your PATH contains the directory |
| <home>/qpid/lib |
| </para> |
| <para> |
| 2) Make sure that a qpid broker is running: |
| </para> |
| |
| |
| <programlisting> |
| $ ps -eaf | grep qpidd |
| </programlisting> |
| |
| |
| <para> |
| If a broker is running, you should see the qpidd process in the |
| output of the above |
| command. |
| </para> |
| <para> |
| 3) Run the server. |
| </para> |
| <para> |
| $ cd <home>/qpid/examples/direct |
| </para> |
| |
| |
| <programlisting> |
| With cygwin: |
| </programlisting> |
| |
| |
| <para> |
| $ ./example-request-response-Server.exe [hostname] [portnumber] |
| </para> |
| |
| |
| <programlisting> |
| or with mono: |
| </programlisting> |
| |
| |
| <para> |
| $ mono ./example-request-response-Server.exe [hostname] [portnumber] |
| </para> |
| |
| |
| <programlisting> |
| You will see output similar to this: |
| </programlisting> |
| |
| |
| <para> |
| Waiting for requests |
| </para> |
| |
| |
| <programlisting> |
| 4) In a separate window, start a client: |
| |
| $ cd <home>/qpid/examples/direct |
| </programlisting> |
| |
| |
| <para> |
| With cygwin: |
| </para> |
| |
| |
| <programlisting> |
| $ ./example-request-response-Client.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| or with mono: |
| </para> |
| |
| |
| <programlisting> |
| $ mono ./example-request-response-Client.exe [hostname] [portnumber] |
| </programlisting> |
| |
| |
| <para> |
| You will see output similar to this: |
| </para> |
| |
| |
| <programlisting> |
| Activating response queue listener for: clientSystem.Byte[] |
| Waiting for all responses to arrive ... |
| Response: TWAS BRILLIG, AND THE SLITHY TOVES |
| Response: DID GIRE AND GYMBLE IN THE WABE. |
| Response: ALL MIMSY WERE THE BOROGROVES, |
| Response: AND THE MOME RATHS OUTGRABE. |
| Shutting down listener for clientSystem.Byte[] |
| Response: THAT'S ALL, FOLKS! |
| </programlisting> |
| |
| |
| <para> |
| 4) Go back to the server window, the output should be similar to |
| this: |
| </para> |
| |
| |
| <programlisting> |
| Waiting for requests |
| Request: Twas brillig, and the slithy toves |
| Request: Did gire and gymble in the wabe. |
| Request: All mimsy were the borogroves, |
| Request: And the mome raths outgrabe. |
| Request: That's all, folks! |
| </programlisting> |
| |
| |
| <para> |
| Now we will examine the code for each of these programs. In each |
| section, we will discuss only the code that must be added to the |
| skeleton shown in Section "Creating and Closing Sessions". |
| </para> |
| <!--h3--> |
| </section> |
| |
| |
| <section role="h3" id="NETUserGuide-TheClientApplication"> |
| <title> |
| The Client |
| Application |
| </title> |
| |
| <para> |
| The first program in the request-response example, Client.cs, |
| sets up a private response queue to receive responses from the |
| server, then sends messages the server, listening to the response |
| queue for the server's responses. |
| </para> |
| |
| |
| <programlisting> |
| string response_queue = "client" + session.getName(); |
| // Use the name of the response queue as the routing key |
| session.queueDeclare(response_queue); |
| session.exchangeBind(response_queue, "amq.direct", response_queue); |
| |
| // Create a listener for the response queue and listen for response messages. |
| Console.WriteLine("Activating response queue listener for: " + response_queue); |
| IMessageListener listener = new ClientMessageListener(session); |
| session.attachMessageListener(listener, response_queue); |
| session.messageSubscribe(response_queue); |
| </programlisting> |
| |
| |
| <para> |
| Set some properties that will be used for all requests. The |
| routing key for a request is request. |
| The reply-to property is set to the routing key for the client's |
| private queue. |
| </para> |
| |
| |
| <programlisting> |
| IMessage request = new Message(); |
| request.DeliveryProperties.setRoutingKey("request"); |
| request.MessageProperties.setReplyTo(new ReplyTo("amq.direct", response_queue)); |
| </programlisting> |
| |
| |
| <para> |
| Now send some requests... |
| </para> |
| |
| |
| <programlisting> |
| string[] strs = { |
| "Twas brillig, and the slithy toves", |
| "Did gire and gymble in the wabe.", |
| "All mimsy were the borogroves,", |
| "And the mome raths outgrabe.", |
| "That's all, folks!" |
| }; |
| foreach (string s in strs) |
| { |
| request.clearData(); |
| request.appendData(Encoding.UTF8.GetBytes(s)); |
| session.messageTransfer("amq.direct", request); |
| } |
| </programlisting> |
| |
| |
| <para> |
| And wait for responses to arrive: |
| </para> |
| |
| |
| <programlisting> |
| Console.WriteLine("Waiting for all responses to arrive ..."); |
| Monitor.Wait(session); |
| </programlisting> |
| <!--h3--> |
| </section> |
| |
| <section role="h3" id="NETUserGuide-TheServerApplication"> |
| <title> |
| The Server |
| Application |
| </title> |
| |
| <para> |
| The second program in the request-response example, Server.cs, |
| uses the reply-to property as the routing key for responses. |
| </para> |
| <para> |
| The main body of Server.cs creates an exclusive queue for |
| requests, then waits for messages to arrive. |
| </para> |
| |
| |
| <programlisting> |
| const string request_queue = "request"; |
| // Use the name of the request queue as the routing key |
| session.queueDeclare(request_queue); |
| session.exchangeBind(request_queue, "amq.direct", request_queue); |
| |
| lock (session) |
| { |
| // Create a listener and subscribe it to the request_queue |
| IMessageListener listener = new MessageListener(session); |
| session.attachMessageListener(listener, request_queue); |
| session.messageSubscribe(request_queue); |
| // Receive messages until all messages are received |
| Console.WriteLine("Waiting for requests"); |
| Monitor.Wait(session); |
| } |
| </programlisting> |
| |
| |
| <para> |
| The listener's messageTransfer() method converts the request's |
| content to upper case, then sends a response to the broker, using |
| the request's reply-to property as the routing key for the |
| response. |
| </para> |
| |
| |
| <programlisting> |
| BinaryReader reader = new BinaryReader(request.Body, Encoding.UTF8); |
| byte[] body = new byte[request.Body.Length - request.Body.Position]; |
| reader.Read(body, 0, body.Length); |
| ASCIIEncoding enc = new ASCIIEncoding(); |
| string message = enc.GetString(body); |
| Console.WriteLine("Request: " + message); |
| |
| // Transform message content to upper case |
| string responseBody = message.ToUpper(); |
| |
| // Send it back to the user |
| response.clearData(); |
| response.appendData(Encoding.UTF8.GetBytes(responseBody)); |
| _session.messageTransfer("amq.direct", routingKey, response); |
| </programlisting> |
| |
| <!--h3--> |
| </section> |
| <!--h2--> |
| </section> |
| <!--h1--> |
| </section> |
| |
| |
| </section> |