RABBITMQ Learning: (10) AMQP and RABBITMQ Introduction (repost + my comments)

Source: Internet
Author: User
Tags mercurial amq

From:http://www.infoq.com/cn/articles/amqp-rabbitmq

Ready to start

The Advanced Message Queuing Protocol (AMQP1) is an application-layer protocol specification used by asynchronous messaging. As a line-layer protocol, rather than an API (for example, JMS2), AMQP clients can send and receive information arbitrarily regardless of the source of the message. Now, there are quite a few different platforms that server 3 and clients can put into use 4.

The original purpose of AMQP is simply to provide the financial community with a message protocol that can collaborate with one another, and now the goal is to provide common building tools for the common Message Queuing architecture. Therefore, message-oriented middleware (MOM) systems, such as publish/subscribe queues, are not implemented as basic elements. Instead, by sending simplified AMQ entities, users are given the ability to build such entities. These entities are also part of the specification, forming a hierarchy at the top of the line layer protocol: the AMQP model. This model unifies the message pattern, such as the previously mentioned publish/subscribe, queue, transaction, and streaming data, and adds additional features such as easier to scale, content-based routing.

The difference between publishing/subscribing in this article is to split producers and consumers: producers don't need to know what standards consumers are receiving messages for. A queue is a first-in, first-out data structure. A route encapsulates information about a message in a message queue, which determines the final presentation of the message in an asynchronous message system.

Here, I try to explain some of the concepts of this model, Aman Gupta uses RUBY5 to implement the AMQP model 6. It uses an event-driven architecture (based on EventMachine7), which makes people feel a bit unfamiliar when reading and using it. But the design of the API shows how simple it is to communicate between AMQ model entities, so even if the developer is not familiar with Ruby, he can reap the benefits as well.

It should be noted that there are at least three or more Ruby clients 8, 9, 10 to choose from. One of the client carrot is clearly using a non-event-driven synchronous ruby architecture, so the client has a very concise style when using the event-driven Ruby API.

The AMQP server in this article is a RABBITMQ written using Erlang11. It implements the AMQP specification version 0-8, and will implement content 12 for the 0-9-1 version in the near future.

Tell something before you start: asynchronous messaging is a very common and widely used technology, from a variety of instant messaging protocols such as Skype or xmpp/jabber to old email. However, these services have the following characteristics:

-they will add more or less random content when transmitting messages (e.g. an email may contain a text and a PPT about Office jokes) and some more formal routing information (such as an email address).

-They are all asynchronous, that is, they differentiate the producer from the consumer, so the message may be queued (for example, someone sends you a message, but you are not online or you receive an email in your mailbox).

-producers and consumers are different roles with different knowledge. I don't need to know your IMAP user name and password to send you an email. In fact, I don't even need to know if your email address is a vest or a "real" address. This feature means that the producer cannot control what content is read or subscribed to-just as my email client will discard most unsolicited medical ads.

The fact that AMQP is an abstract protocol (i.e. it is not responsible for handling specific data) does not complicate things. Instead, the Internet makes messages everywhere. People often use them and asynchronous messages to solve many problems in a simple and flexible way. and the most difficult part of building an asynchronous message Schema model in AMQ is when you get started, and once these difficulties are overcome, the build process will be simple.

You may need to install some software to implement these examples yourself. If you already have Ruby installed on your system, it will take less than 10 minutes to set up the time. RABBITMQ website also has a lot of information 13 to help you start as soon as possible. All you need to do is prepare for the job:

-ERLANG/OTP bag. is http://erlang.org/download.html, installation instructions in http://www.erlang.org/doc/installation_guide/part_frame.html.

-RabbitMQ. is http://www.rabbitmq.com/download.html, installation instructions in http://www.rabbitmq.com/install.html.

-A ruby virtual machine. If you don't have a choice of Ruby interpreter on your system platform, you may need to download the Ruby MRI VM. Instructions can be found and installed in http://www.ruby-lang.org/en/downloads/.

-Two Ruby "gems" (packaged libraries). The Gem tool should be distributed along with your Ruby installation package.

-If you need a new installation or are unsure if it is not the current version, then you can choose to upgrade the Gem tool. Enter the gem update--system. In a Bsd/unix system, you may need to have superuser privileges to run this command (and subsequent instructions).
-Tell Gem on GitHub search package: Gem sources-a http://gems.github.com.
-Installs Amqpgem:gem install TMM1-AMQP. This will also install the Event-machine gem.

Now all you have to do is start RABBITMQ server 14.

AMQ model

Some entities are described in the AMQ specification. One way to distinguish these entities is to check whether they are configured by the server administrator or declared by the client at run time.

The entities that can be configured are:

-Message Broker, which listens for AMQ messages on ports such as TCP/IP.

-Divides the message negotiation data into multiple virtual hosts of different collections, much like a virtual host in webserver, such as the Apache HTTP daemon.

-Users who connect to the virtual host with security credentials.

1 Require ' RubyGems ' 2 require ' mq ' Event_loop = thread.new do5 em.run do6     em.add_timer (1) Do7       EM.STOP8     end9   end10 end1112 # Connect to the RABBITMQ Demonstration broker Server (http://www.rabbitmq.com/examples.html #demoserver) 1314 AMQP.start:host = ' dev.rabbitmq.com ',:p ort = 5672,: User = ' guest ',:p assword = ' guest ' ,:vhost=> ' localhost ' 1516 event_loop.join

It is worth noting that the specification only grants users access to the virtual host, and does not adopt other advanced access control measures, so RABBITMQ does not support these advanced access control measures. A vendor-developed solution 15 phase hope will be added to the next major release. However, this feature 16 can be achieved by using the default BRANCH17 of the mercurial code base, and there are already some RABBITMQ users in use.

In order to communicate with the communicator, a client needs to establish one or more connections. These connections are only limited to connecting users and virtual hosts. By default, clients use Guest/guest access and access to the root directory of the virtual host, which is also the default installation option for RABBITMQ.

In a connection, the client declares a channel. This channel is a logical connection in the network connection of the message-negotiating device. This multi-work mechanism is necessary because some of the operations in the protocol require such a channel. Therefore, concurrency control through a single connection to a negotiation requires a reliable model, where you can use a channel pool and serial access, or a thread concurrency model such as a thread-local channel. In this example, the Ruby API hides the details of channel management from the user.

If you need to operate on one channel, the client needs to declare the AMQ component. Declaring a component is asserting that a particular component exists in the negotiation-if it does not exist, it is created at run time.

These components include:

-Exchanger (Exchange), which is the entity that sends the message.

-Queue, which is the entity that receives the message.

-The binder (BIND), which connects the exchanger and the queue, and encapsulates the routing information for the message.

The properties of all these components are different, but only the exchanger and the queue are also named. The client can send the message through the name of the exchanger, or it can collect information from the queue's name. Because the AMQ protocol does not have a common standard method to obtain the names of all components, client access to queues and switches is restricted to names that are only known or known only (see 18 For information about this access control).

The bindings have no names, and their lifetimes depend on the tightly connected switches and queues. If either of these is removed, then the binder fails. This means that to know the name of the exchanger and queue, you also need to set up message routing.

The message is an opaque packet that has the following properties:

-metadata, such as the encoding of the content or the field that indicates the source.

-Flag bits that mark some of the security mechanisms at the time of message delivery.

-A special field is called Routing key.

2.1 Receiving and sending messages: exchanger type

Sending a message is a very simple process. The client declares a destination exchanger for which it wants to send messages, and then passes the message to the exchanger.

The simplest way to accept a message is to set up a subscription. The client needs to declare a queue and use a binder to bind the previous switch and queue so that the subscription is set up.

1 Require ' RubyGems ' 2 require ' mq ' Event_loop = thread.new do5 em.run do6     em.add_timer (1) Do7       EM.STOP8     end9   end10 end1112 def subscribe_to_queue1314   exchange = mq.fanout (' my-fanout-exchange ')   \ queue = Mq.queue (' My-fanout-queue ') 1617   queue.bind (Exchange). Subscribe do |header, body|18     yield header, body19   end2021 end2223 def send_to_exchange (message) 2425   exchange = mq.fanout (' My-fanout-exchange ')   Exchange.publish message2728 end2930 subscribe_to_queue do |header, body|31   P "I received a message: #{body}" end33 Send_to_exchange ' Hello ' send_to_exchange ' World ' 3637 event_loop.join

Three criteria determine whether a message is actually delivered to the queue:

    1. The type of the exchanger. In this example, the type is fanout.
    2. The properties of the message. In this case, the message has no attributes, just the content (first Hello, then world).
    3. The unique optional property of the given binder: the key value. In this example, the binder does not have any key values.

The type of exchanger determines how it interprets the connection. In our case, the fanout switch does not explain anything: it simply posts the message to all the queues that are bound to it.

Without the binder, even the simplest of messages, the exchanger cannot post it to the queue, only discard it. By subscribing to a queue, consumers can get messages from the queue and then delete them from the queue after they are used.

The following exchanger types are mentioned in the specification. Then I will introduce them in an easy-to-digest manner.

-the direct exchanger posts the message to the binder containing the corresponding key property based on its Routing-key property.

1 Require ' RubyGems ' 2 require ' mq ' Event_loop = thread.new do5 em.run do6     em.add_timer (1) Do7       EM.STOP8     end9   end10 end1112 def subscribe_to_queue (key) 1314   exchange = Mq.direct (' My-direct-exchange ')   Queue = Mq.queue (' my-direct-queue ') 1617   queue.bind (exchange,: key = = key). Subscribe do |header, body|18     Yield header, Body19   end2021 end2223 def send_to_exchange (message, key) 2425   exchange = Mq.direct (' My-direct-exchange ')   exchange.publish message,: Routing_key = key2728 end2930 subscribe_to_queue (' Hello_ World ') do |header, body|31   P "I received a message: #{body}" end3334 send_to_exchange ' Hello ', ' Hello_world ' sen D_to_exchange ' cruel ', ' ignored ' send_to_exchange ' world ', ' Hello_world ' 3738 event_loop.join

-The Routing-key property of the topic switch with the over-pattern matching analysis message. It divides the strings of routing-key and binding-key into words. These words are separated by dots. It will also recognize two wildcard characters: #匹配0个或者多个单词, * match one word. For example, the binding key *.stock. #匹配routing key Usd.stcok and eur.stock.db, but does not match Stock.nasdaq.

1 Require ' RubyGems ' 2 require ' mq ' Event_loop = thread.new do5 em.run do6     em.add_timer (1) Do7       EM.STOP8     end9   end10 end1112 def subscribe_to_queue (key) 1314   exchange = mq.topic (' My-topic-exchange ')   Queue = Mq.queue (' my-topic-queue ') 1617   queue.bind (exchange,: key = = key). Subscribe do |header, body|18     Yield header, Body19   end2021 end2223 def send_to_exchange (message, key) 2425   exchange = Mq.topic (' My-topic-exchange ')   exchange.publish message,: Routing_key = key2728 end2930 subscribe_to_queue (' hello.*. message.# ') do |header, body|31   P "I received a message: #{body}" end3334 send_to_exchange ' Hello ', ' hello.world.me Ssage.example.in.ruby ' send_to_exchange ' cruel ', ' cruel.world.message ' send_to_exchange ' world ', ' Hello.world.message ' 3738 event_loop.join

-Other switches are mentioned in the specification, such as the header switch, which matches the specific properties of the application message, which may be marked as optional or required in the binding key, failover and the system switch. However, these switches are now not implemented in the current RABBITMQ version.

Unlike queues, exchangers have a corresponding type, indicating how they are delivered (usually when collaborating with the binder). Because the exchanger is a named entity, it declares an existing exchanger, but trying to give different types is an error. The client needs to delete the existing switch and then re-declare and give the new type.

The exchanger also has some properties:

-Persistence: If enabled, the switch will be valid until the negotiation is restarted.

-Automatic deletion: If enabled, the switch will automatically delete itself after its bound queue has been deleted.

-Inertia: If the switch is not declared, it will cause an exception when executed to use, and will not be actively declared.

2.2 Default Switch and binder

The AMQP renegotiation declares an instance of each type of exchanger it supports (for each virtual host). The naming rules for these switches are amq. prefix plus type name. such as Amq.fanout. The empty exchanger name equals Amq.direct. For this default direct switch (also just for this switch), the linker declares a binder that binds all the queues in the system.

This feature tells us that in the system, any queue can be bound together with the default direct switch, as long as its routing-key equals the queue name.

2.3 Queue properties and multiple binders

The behavior of the default binder reveals the existence of multiple binders-binding one or more queues to one or more switches. This allows messages sent to different switches with different routing keys (or other properties) to be sent to the same queue.

1 Require ' RubyGems ' 2 require ' mq ' Event_loop = thread.new do5 em.run do6     em.add_timer (1) Do7       EM.STOP8     end9   end10 end1112 def subscribe_to_queue (*keys) 1314   exchange = Mq.direct (' My-direct-exchange ')   Queue = Mq.queue (' my-direct-queue-with-multiple-bindings ') 1617   bindings = Keys.map do |key|18     queue.bind ( Exchange,: Key = = key)   end2021   bindings.last.subscribe do |header, body|22     yield header, body23   end2425 end2627 def send_to_exchange (message, key) 2829   exchange = Mq.direct (' My-direct-exchange ')   Exchange.publish message,: Routing_key = key3132 end3334 subscribe_to_queue (' foo ', ' Bar ', ' wee ') do |header, body|35< C15/>p "I received a message: #{body}"

3738 send_to_exchange ' Hello ', ' foo ' send_to_exchange ' You ', ' gee ' send_to_exchange ' cruel ', ' Bar ' send_to_ Exchange ' world ', ' Wee ' 4243 event_loop.join

Although it cannot be named, the queue also has the following properties, which are similar to the properties that the exchanger has.

-Persistence: If enabled, the queue will be valid until the negotiation is restarted.

-Automatic deletion: If enabled, the queue will automatically delete itself after all consumers have stopped using it.

-Inertia: If no queue is declared, it will cause an exception when executed to use, and will not be actively declared.

-Exclusivity: If enabled, the queue can only be used by consumers who declare it.

These properties can be used to create transient or private queues such as exclusive and self-deleting. Such a queue will be automatically deleted after all the clients that link to it are disconnected-they are only briefly connected to the renegotiation, but can be used to implement peer-communication such as RPC or on AMQ.

RPC on AMQP is like this: The RPC client declares a reply queue, uniquely named (for example, with UUID19), and is self-deleted and exclusive. It then sends a request to some exchanger, which contains the name of the previously declared reply queue in the Reply-to field of the message. The RPC server will answer these requests, using the reply-to of the message as routing key (previously mentioned that the default binder binds all queues to the default switch) to the default switch. Attention is just a formality. Depending on the contract with the RPC server, it can interpret any property of the message (even the data body) to decide who to reply to.

Queues can also be persistent, shareable, non-auto-deleted, and non-exclusive. Multiple users using the same queue receive a copy of the message that is not sent to this queue, but rather they share one piece of data in the queue and then delete it after they have finished using it.

2.4 Security mechanism for message delivery

The consumer will explicitly or implicitly notify the message to be used. When implicitly notified, the message is considered to be consumed after delivery. Otherwise, the client needs to explicitly send a validation message. Only after this authentication message is received will the message be considered to have been received and removed from the queue. If not, the renegotiation attempts to resend the message before channel 20 is closed.

1 Require ' RubyGems ' 2 require ' mq ' Event_loop = thread.new do5 em.run do6 em.add_timer (1) Do7 EM.STOP8 E Nd9 END10 end1112 def subscribe_to_queue1314 exchange = mq.fanout (' my-fanout-exchange-with-acks ') queue = Mq.queue (' My-fanout-queue-with-acks ') 1617 Queue.bind (Exchange). Subscribe (: Ack = = true) do |header, body|18 yield header, Body19 header.ack unless BODY = = ' Cruel ' end2122 end2324 def send_to_exchange (message) 2526 exchange = Mq.fanout ( ' My-fanout-exchange-with-acks ') exchange.publish message2829 end3031 subscribe_to_queue do |header, body|32 P "I rec eived a message: #{body} "end3435 send_to_exchange ' Hello ' send_to_exchange ' cruel ' Notoginseng send_to_exchange ' world ' 3839 ev  ent_loop.join4041 __end__4243 First run:4445 "I received a Message:hello" "I received a Message:cruel" J "I received a Message:world "4849 Second run:5051" I received a Message:cruel "I received a Message:hello" "I received a message : Cruel "I received a"Message:world "5556 ... and so forth 

The message producer can choose whether to be notified when a message is sent to the exchanger and not delivered to the queue (no binder exists) and/or if no consumer can handle it immediately. By setting the mandatory and/or immediate properties of the message to true, the ability to deliver the guarantee mechanism has been enhanced.

The Ruby AMQP APIs that are now used in this example do not yet fully support these flag bits. However, there are already two patch21 on GitHub, and 22 shows the situation after full support.

In addition, a producer can set the message's persistent property to true. As a result, the renegotiation will attempt to store these messages in a stable position until the renegotiation crashes. Of course, these messages will certainly not be delivered to a non-durable queue.

2.5 Congestion Control

In the example given, the use of the message is always considered a subscription. So, considering congestion control? The specification developed the QOS23 feature, which limits the amount of messages sent to a consumer through a single channel. Unfortunately, this feature is not supported in the current RABBITMQ version (planned at 1.6), but in principle it should be supported by the AMQP API.

As an alternative, the client can choose to remove the message from the queue instead of subscribing to it. When this method is used, congestion control can be implemented manually.

1 Require ' RubyGems ' 2 require ' mq ' Event_loop = thread.new do5 em.run do6     em.add_timer (5) Do7       EM.STOP8     end9   end10 end1112 def subscribe_to_queue1314   exchange = mq.fanout (' my-fanout-exchange ')   \ queue = Mq.queue (' My-fanout-queue ') 1617   queue.bind (Exchange). Pop do |header, body|18     yield header, Body19   end2021   Em.add_periodic_timer (0.25) do22     queue.pop23   end2425 end2627 def send_to_exchange (message) 2829   exchange = mq.fanout (' my-fanout-exchange ')   exchange.publish message3132 end3334 received = 03536 Subscribe_to_queue do |header, body|37   P "I received a message: #{body}" end3940 Send_to_exchange ' Hello ' in send_to _exchange ' World ' 4243 event_loop.join
A sample example of a model

Imagine that you want to create a common chat app, so there are a few basic features:

-Chat-Two users should be able to send messages to one another.

-A buddy system-users can control who sends messages to him.

We assume that there are two kinds of consumers on the renegotiation: friend Server and chat client.

3.1 Become a Friend

To be Bob's friend, Alice first has to send a message to the fanout exchanger iends, and we assume that the switch is restricted to 24: The normal user cannot bind the queue to it. In this message, Alice says she wants to be friends with Bob.

There is a large number of chat servers on the negotiation server, which continuously extracts messages from a single persistent queue bound to the friends switch. The name of this queue is such as friends.298f2dbc6865-4225-8a73-8ff6175d396d, the hard-to-guess name prevents the chat client from extracting the information directly-remember: You cannot set a subscription without knowing the name of the queue.

When a chat server receives Alice's message (only one gets the message because they are all taken from the same queue), determines whether the request is valid, and then sends it (perhaps with some adjustments or parameterization) to the default switch (which can be either direct or persistent). It uses another routing key that only Bob knows to deliver. When Bob goes online (or a server does this), he declares a queue whose name is the previous routing key (remember that the default binder on the virtual host binds all queues and default switches together).

Bob's chat client now asks Bob if he wants to be friends with Alice. In her request message, there is a special attribute called Reply-to-this property includes the name of a persistent and exclusive buddy queue that Alice declares will use for future chats with Bob. If Bob wants to be friends with Alice, he will use the name of the queue as routing key, sending a message to the default switch. He will also need to declare a persistent and exclusive buddy queue and set its name to the value of reply-to.

For example, Alice and Bob's buddy queue name is b5725c4a-6621463e-aaf1-8222aa3ad601. The Routing-key value of the message Bob sends to Alice is the name, and the value reply-to in the message Alice sends to Bob.

Because the buddy queue is persistent, sending to a message is not lost when the user is offline. When the user is online, all messages in the buddy queue are sent to the user before they get a new message.

When Bob no longer wants to be a friend with Alice, he can simply delete the friend queue for Alice's claim. When she sends a message using the mandatory flag, Alice also notices that Bob no longer wants to be her friend. Because the exchanger will return her message as undeliverable. [Puncha: This is a pretty interesting example.] ]

Things that are still not mentioned

There are still a lot of things that are not covered in this article, such as transactional semantics, the rerouting of information, the specification of the header exchanger, and the differences between different AMQP specifications-especially before the 1.0 version of the model changes. For the sake of brevity, a chat model has also been skipped.

This also does not introduce the management of the entire system, as it is unclear where AMQP and RABBITMQ will go. Now there is a topic about the switch that is available in the reserved Amq namespace, which can get all the log information of the renegotiation. However, the ability to list the components that are now declared and the connected users is implemented using the RABBITMQCTL command-line interface instead of the AMQ entity.

1 Require ' RubyGems ' 2 require ' mq ' Path_to_rabbitmqctl = '/usr/local/sbin/rabbitmqctl ', Event_loop = thread.new {em.ru n}78 def subscribe_to_logger910 random_name = (0...50). map{(' A ' ... ' Z '). To_a[rand (+)]}.join1112 Exchange = mq.topic (' amq.rabbitmq.log ') queue = Mq.queue (Random_name,: Autodelete =&gt ; True,: Exclusive = true) + binding = Queue.bind (Exchange,: key = ' # ') 1516 binding.subscribe do |header, body|1 7 body.split ("\ n"). Each do |message|18 yield header, Message19 end20 end2122 end2324 def exchange_info (VHO   st = '/') Info:exchange, Vhost,%w (name type durable auto_delete arguments) end2728 def queue_info (vhost = '/') 29                            Info:queue, Vhost,%w (name durable auto_delete arguments node Messages_ready messages_unacknowledged messages_uncommitted Messages acks_uncommitted Consumers Transactions memory) end3132 def binding_info (vhost = ' /') info:binding, Vhost34 end3536 def connection_info37 Info:exchaNge, Nil,%w (node address port peer_address peer_port State Channels user Vhost Timeout frame_ Max recv_oct recv_cnt send_oct send_cnt send_pend) end3940 def info (about, Vhost = nil, items = []) 4142 column_length = 204344 puts "#{about} info\n" 4546 cmd = "#{path_to_rabbitmqctl} list_#{about}s" << "-p #{vhost}" if V host48 cmd << "#{items.join (')} 2>&1" 4950 pipe = Io.popen (cmd) 5152 pipe.readlines.map {|line| line. Chomp.split ("\ T"). Map {|item| item.ljust (column_length) [0, Column_length]}}.slice (1..-2). ea CH Do |exchange|53 print Exchange.join (") +" \ n "end5556 end5758 subscribe_to_logger do |message|59 p" RabbitMQ Logger: #{message} "end6162%w (Connection Exchange Queue binding). Each does |method|63 self.send" #{method}_info ". To_sym end6566 Event_loop.join

It must be mentioned that there are already some distributed architectures that use AMQP (or RABBITMQ). These schemas, such as Nanite25 or Lizzy26, introduce some abstraction layers at the top of AMQP, simplifying operations such as the assignment of work between Ruby clients in cluster.

4.1 What should I do next?

The first step to using a local negotiation to play friends and mailing lists is to learn about AMQP and RABBITMQ. Not only should you read slides and articles on the RABBITMQ website, you should also communicate with community members on the #rabbitmq channel on IRC or read blogs about RABBITMQ and/or AMQP 27, 28, such as LShift. On Twitter You can also find a lot of content about AMQP or rabbitsmq via the #rabbitmq or #amqp tabs 29, 30, 31, 32, 33, 34, 35.

Welcome to the world of asynchronous information and have a good time!

1 HTTP://WWW.INFOQ.COM/AMQP
2 ttp://java.sun.com/products/jms/
3 Advanced Message Queuing protocol/implementations
4 http://www.rabbitmq.com/how.html#clients
5 Http://github.com/tmm1/amqp/tree/master
6 http://www.infoq.com/ruby/
7 http://rubyeventmachine.com/
8 Http://github.com/famoseagle/carrot/tree/master
9 Http://github.com/celldee/bunny/tree/master
Ten http://qpid.apache.org/download.html
Erlang
Http://www.rabbitmq.com/specification.html
Http://www.rabbitmq.com/how.html
Http://www.rabbitmq.com/how.html
Accesscontroldesign
ACLs
Http://www.rabbitmq.com/mercurial.html#defaultbranch
Minimum Air Induction
Universally Unique Identifier
20 or a connection to this channel.
Somic/amqp
Yawn/amqp
Basicqosdesign
24 as an alternative, Alice can receive another special Routing-key to request a friend. All chat servers will be bound to friends.298f2dbc-6865-4225-8a73-8ff6175d396d using the Binding-key of all users in the chat system. There are other options available-in fact, all the behaviors in this example are implemented in many ways.
Ezmobius/nanite
Bmizerany/lizzy
lists.rabbitmq.com Mailing Lists
RabbitMQ
Rabbitm-highlights:presentations, blogs and code
Freenode
Minimum Air Induction
Kirk ' s rants blogspot
Http://somic.org/category/rabbitmq/34 HTTP://WWW.LSHIFT.NET/BLOG/CATEGORY/LSHIFT-SW/RABBITMQ
Twitter

View English Original: Getting started with AMQP and RabbitMQ.

http://blog.csdn.net/puncha/article/details/8450702

RABBITMQ Learning: (10) AMQP and RABBITMQ Introduction (repost + my comments)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.