《RabbitMQ in Action》以及官方文档读书笔记

2018-03-31  本文已影响253人  AlstonWilliams

Official documentation

  1. Round-robin dispatching:
    Every node will get the same number of messages.
    But the nodes are different actually, some are light, and some are heavy. How to assign more messages to light nodes, and less to heavy nodes?

  2. Message acknowledgement
    There aren't any message timeouts; RabbitMQ will redeliver the message when the consumer dies. It's time even if processing a message a very very long time.
    Manual message acknowledgements are turn on by default. And auto message acknowledgements are turn off.

  3. Message durability
    Two things are required to make sure that messages are not lost: Mark both the queue and messages are durable.
    The persistence guarantees are't strong because of disk cache. If you need a stronger guarantee then you can use "publisher confirm".

  4. Fair dispatch
    Assume we have two workers, and all odd messages are heavy and even messages are light. Then one worker will be constantly busy and the other one will do hardly any work.
    In order to detect that we can use the basicQos method with the prefetchCount=1 setting. This tells RabbitMQ server don't dispatch a new message to a worker until it has processed and acknowledged the previous one.

  5. Exchange
    Determine how producer pushs messages to queue, fanout or other ways?

  6. Temporary queue
    The queue will be delete after losing connection.

  7. Binding
    Bind queue with exchange.

  8. Routing
    Producer publishes messages by routing key, consumer binds queue to exchange by routing key.
    Routing key in some exchange types is not available, like "fanout". But "direct" exchange can work.

  9. Exchange type:

RabbitMQ in Action

Queue

  1. Consumers receive messages from a particular queue in one of two ways:
    1.1 By subscribing to it via the basic.consume AMQP command. This will place the channel being used into a receive mode until unsubscribed from the queue. While subscribed, your consumer will automatically receive another message from the queue after consuming the last received message. You should use basic.consume if your consumer is processing many messages out of a queue and/or needs to automatically receive messages from a queue as soom as they arrive.
    1.2 Sometimes, you just want a single message from a queue and don't need to be persistently subscribed. Requesting a single message from the queue is done by using the basic.get AMQP command. This will cause the consumer to receive the next message in the queue and then not receive further messages until the next basic.get. You shouldn't use basic.get in a loop as an alternative to basic.consume, because it's much more intensive on Rabbit.

  2. When a RabbitMQ queue has multiple consumers, messages received by the queue are served in a round-robin fashion to the consumers. Each message is sent to only one consumer subscribed to the queue.

  3. Every message that's received by a consumer is required to be acknowledged. Either the consumer must explicitly send an acknowledgement to RabbitMQ using the basic.ack AMQP command, or it can set the auto_ack parameter to true when it subscribes to the queue.

  4. Reject a message
    4.1 From all versions, you can disconnect the consumer. The disadvantage is the extra load put on the RabbitMQ server from the connecting and disconnecting of your consumer.
    4.2 If you're running RabbitMQ 2.0.0 or newer, use the basic.reject AMQP command.

  5. Both consumers and producers can create queues by using the queue.declare AMQP command. But consumers can't declare a queue while subscribed to another one on the same channel. They must first unsubscribe in order to place the channel in a transmit mode.

  6. Queue's properties
    6.1 exclusive: When set to true, your quue becomes private and can only be consumed by your app.
    6.2 auto-delete: The queue is automatically deleted when the last consumer unsubscribes.

  7. Messages that get published into an exchange but have no queue to be routed to are discarded by Rabbit. So if you can't afford for your messages to be black-holed, both your producers and your consumers should attempt to create the queues that will be needed.

Exchange and bindings

  1. Exchange Type:

headers exchange allows you to match against a header in the AMQP message instead of the routing key. Other than that, it operates identically to the direct exchange but with worse performance. As a result, it doesn't provide much real-world benefit and is almost never used.

direct exchange is pretty simple: If the routing key matches, then the message is delivered to the corresponding queue.

When a queue is declared, it will be automatically bound to that exchange using the queue name as routing key.

fanout exchange is simple: When you send a message to a fanout exchange, it will be delivered to all the queues attached to this exchange.

Message durability

  1. For a message that's in flight inside RabbitMQ to survive a crash, the message must:
  1. The way that RabbitMQ ensures persistent messages survive a restart is by writing them to the disk inside of a persistency log file. When you publish a persistent message to a durable exchange, Rabbit won't send the response until the message is committed to the log file. Keep in mind, though, that if it gets routed to a nondurable queue after that, it's automatically removed from the persistency log and won't survive a restart. When you use persistent messages it's crucial that you make sure all three elements required for a message to persist are in place. Once you consume a persistent message from a durable queue, RabbitMQ flags it in the persistency log for garbage collection. If RabbitMQ restarts anytime before you consume a persistent message, it'll automatically re-create the exchanges and queues(and bindings) and replay any messages in the persistency log into the appropriate queues or exchanges.

  2. The performance will be decreased if you persistent all your messages.

  3. Though RabbitMQ clustering allows you to talk to any queue present in the cluster from any node, those queues are actually evenlly distributed among the nodes without redundancy. If the cluster node hosting your seed_bin queue crashes, the queue disappears from the cluster until the node is restored If the queue was durable. More important, while the node is down its queues are't available and the durable ones can't be re-created. This can lead to black-holing of messages.

  4. When should you use persistent/durable messaging? If you require high performance, for example, 100000 messages per second on a single server ,you should probably look at other ways to ensuring message delivery. For example, your producer could listen to a reply queue on a separate channel. Every time it publishes a message, it includes the name of the reply queue so that the consumer can send a reply back to confirm the receipt. If a message isn't replied to within a reasonable amount of time, the producer can republish the message. That said, the critical nature of messages requiring guaranteed delivery generally means they're slower in volumes than other types of messages.

  5. We're just selective about what types of content use persistent messaging. For example, we run two types of RabbitMQ clusters: traditional RabbitMQ clustering for nonpersistent messaging, and pairs ofactive/hot-standby nonclustered RabbitMQ servers for persistent messaging.

  6. Do keep in mind that while RabbitMQ can help ensure delivery, it can never absolutely guarantee it. Hard driver corruption, buggy behaviour by a consumer, or other extreme events can trash/black-hole persistent messages.

  7. When you absolutely need to be sure the broker has the message in custody before you moving into another task, you need to wrap it in a transaction. Don't confuse the transtion in databases and in AMQP. In AMQP, after you place a channel into transaction mode, you send it the publsih you want to confirm, followed by zero or more other AMQP commands that should be executed or ignored depending on whether the initial publish succeed. Once you've sent all of the commands, you commit the transaction. If the transaction's initial publish succeds, then the channel will complete the other AMQP commands in the transaction. If the publish fails, none of the other AMQP commands will be executed.

  8. Though transactions are part of the formal AMQP 0-9-1 specification, they have an Achilles heels in that they're huge drains on RabbitMQ performance. Not only can using transactions drop your message throught put by a factor of 2-10x, but they also make your producer app synchronous.

  9. To ensure message delivery, the guys at RabbitMQ decides to come up a better concept: producer confirms. Same to transactions, you have to tell RabbitMQ to place the channel into confirm mode, and you can't turn it off without re-creating the channel. Once a channel is in confirm mode, every message published on the channel will be assigned a unique ID number. Once the message has been delivered to all queues that have bindings matching the message's routing key, the channel will issue a publisher confirm to the producer app. The major benefit of publisher confirms is that they're asynchronous. Once a message has been published, the producer app can go on to the next message while waiting for the confirm. When the confirm for that message is finally received, a callback function in the producer app will be fired so it can make up and handle the confirmation. If an internal error occurs inside Rabbit taht causes a message to be lost, RabbitMQ will send a message "nack" that's like a publisher confirm but indicates the message was lost. Also, since there's no concept of message rollback, publisher confrms are much ligher weight and have an almost negligible performance hit on the RabbitMQ broker.

Running and administering Rabbit

  1. The metadata for every queue, exchange, and binding in RabbitMQ(but not message content) is written to Mnesia, which is a non-SQL database built light into Erlang.

  2. Mnesia configuration options:

  1. Rabbit configuration options
  1. User-related operation:
  1. AMQP operations-to-RabbitMQ permissions map


  2. An access control entry consists of four parts:

  1. RabbitMQ-related operation:
  1. The log of RabbitMQ is under LOG_BASE environment variable. It is set to /var/log/rabbitmq by default.
    Inside that folder RabbitMQ will create two log files: RABBITMQ_NODENAME-sasl.log and RABBITMQ_NODENAME.log.
    When RabbitMQ logs Erlang-related information, it'll go to the rabbit-sasl.log files. For example, there you can find Erlang's crash reports that can be helpful when debugging a RabbitMQ node that doesn't want to start.

  2. There is an exchange called amq.rabbitmq.log whose type is "topic". RabbitMQ will publish its logs to that exchange using the severity level as a routing key - you'll get error, warning and info. You can build a real-time system based on this point.

  3. How rabbitmqctl works?
    rabbitmqctl will start up an Erlang node, and from there it'll try to communicate with the RabbitMQ node using the Erlang distribution system. For this to work, you need two things: the proper Erlang cookie and the proper node name. So what's an Erlang cookie? An Erlang cookie acts as a secret token that two Erlang nodes exchange in order to authenticate themselves. Since you can execute commands on the remote node once you're connected to it, there's a need to make sure that the peer is trusted.
    In order for rabbitmqctl to communicate with the RabbitMQ node, it needs to share the same cookie. In production you'll probably want to create arabbitmq user and run the server with that user. This means that you must share the cookie with the rabbitmq user or you have to switch to that user to be able to execute rabbitmqctl successfully.

Clustering and dealing with failure

  1. RabbitMQ is keeping track of four kinds of internal metadata:
  1. In a cluster when you create queues, the cluster only creates the full information about the queue(metadata, state, contents) on a single node in the cluster rather than on all of them(queues are created on the node to which the client declaring the queue is connected). The result is that only the owner node for a queue knows the full information about that queue. All of the non-owner nodes only know the queue's metadata and a pointer to the node where the queue actually lives. So when a cluster node dies, that node's queues and associated binding disappear. Consumers attached to these queues lose their subscriptions, and any new messages that would've matched that queue's binding become black-holed.

  2. Exchanges are a figment of your imagination, they don't have their processes, they are just a name and a list of queue bindings. When you publish a message into an exchange, what really happens is the channel you're connect to compares the routing key on the message to the list of bindings for that exchange, and then route it. Then channel does the actual routing of the message to the queue as specified by the matching binding.

  3. Every node in the cluster has all of the information about every exchange. For availablity this is great, because it means you don't have to worry about redeclaring an exchange when a node goes down.

  4. The basic.publish AMQP command doesn't return the status of message. It means that you risk lossing messages. To solve this problems, try AMQP transaction or publisher confirms.

  5. Every RabbitMQ node, whether it's a single node system or a part of a larger cluster, is either a RAM node or a disk node. A RAM node stores all of the metadata defining the queues, exchanges, binding, users, permissions, and vhosts only in RAM, whereas a disk node also saves the metadata to disk.
    When you declare a queue, exchange, or binding in a cluster, the operation won't return until all of the cluster nodes have successfully committed the metadata changes. For a RAM node, this means writing the changes into memory, but for a disk node this means an expensive disk write that must complete before the node can say "I've got it."
    RabbitMQ only requires that one node in a cluster be a disk node. Every other node can be a RAM node. Keep in mind that when nodes join or leave a cluster, they need to be able to notify at least one disk node of the change. If you only have one disk node and that node happens to be down, your cluster can continue to route messages but you can't do any of the following:

  1. RabbitMQ clustering is sensitive to latency and should only be used over a local area network. Using it to provide geographic availability/routing over a WAN will cause timeouts and strange cluster behaviour, so it's ill-advised.

Mirrored queue

  1. Mirrored queues have slave copies on other nodes in the cluster. In the event that the queue's master node becomes unavailable, the oldest slave will be elected as the new master.

  2. Declaring a mirrored queue is just like declaring a normal queue; you pass an extra argument called x-ha-policy to the "queue.declare" call.

  3. If a mirrored queue loses a slave node, any consumers attached to the mirrored queue don't notice the loss. That's because technically they're attached to the queue's master copy. But if the node hosting the master copy fails, all of the queue's consumers need to reattach to start listening to the new queue master. For consumers that were connected through the node that actually failed, this isn't hard. Since they've lost their TCP onnection to the node, they'll automatically pick up the new queue master when they reattach to a new node in the cluster. But for consumers that wre attached to the mirrored queue through a node that didn't fail, RabbitMQ will send those consumer a consumer cancellation notification telling them they've no longer attached to the queue master. If your AMQP client library understands consumer cancellation notifications, it'll raise an exception and your app will know it's no longer attached to the queue and need to reattach. On the other hand, if your client library doesn't understand consumer cancellations, you're in a bind. The client has no way of telling your app that its consumption loops point to a master queue copy that no longer exists. Unfortuntely, there's no clever way around this situation. So if your client library doesn't understand consumer cancellation notifications, you should avoid mirrored queues until it does.

  4. When the master node of mirrored queue fails, consumed but unacknowledged messages are required to their original positions in the queue.

Lost connections and failing clients between servers

  1. Consider what assumptions you can make before writing your code:
  1. You should always treat failover as if you were connecting to a completely unrelated RabbitMQ server, rather than a cluster node with some shared state. As a result, whenever a node failure occurs, the first order of business after detecting the failure and reconnecting is to rebuild the fabric of exchanges, queues, and binding that your app needs to operate.

Warrens and shovels: failover and replication

  1. Clustering makes you trade the benefit of all the nodes acting as single unit to distribute the load for the drawable of not being able to use durable queues on downed nodes until the nodes are restored. Also, clustering won't give you what you need to build a RabbitMQ architecture that's distributed across more than one data center.

  2. Since version 1.8.0, when a node with a durable queue goes down, that queue can't be re-created. Any client that attempts to redeclare the queue will receive a 404 NOT_FOUND AMQP error. Until that node is restored, any messages that would've been delivered to it are either black-holed or errors are sent to the clients that set mandatory publish flags.

  3. If your application can't risk losing messages or deal with the latency of continuously republishing messages until therir downed queue returns, then you need what we call warrens. In our parlance, a warren is a pair of active/standby standalone servers with a load balancer in front handling failover.

  4. The Warren can't make standby node to have all of the messages that were in the active node when if failed. The approach with load balancers and shared-nothing architecture doesn't give you this. Instead the approach gives you an immediate place to start publishing and consuming messages again, and when you restore the active node, it allows your consumers to reattach and drain the messages that wre in the queues when the active node went down. You don't lose any messages old or new, but you do have to wait for the active node to be restored for the old messages to become available again.

  5. The other school of thought for building a warren says you should instead build it with shared storage betwen your active and standby servers with RabbitMQ not running on the standby node. Then when a failure of the active server happens you use Pacemaker to transfer the RabbitMQ IP address to the standby node and then start up Rabbit on that node to pick up your current metadata, contents, and state from the shared storage. There are only a couple of problems with that setup in our opinion. First, the storage is shared, so if some kinds of corruption kills your active node, that corruption will be present on the standby node too and prevent RabbitMQ from starting there. Second, you need to be sure that the standby RabbitMQ has the same node name and UID as RabbitMQ on the primary node. If either of these aren't true, the standby Rabbit won't be able to access the files on the shared storage and fire up. Lastly, using this setup for a warren means your standby Rabbit isn't actually running. So there's a possibility that something you have changed on the standby node that will prevent Rabbit from starting up when you need it.

Long-distance communication and replication

  1. RabbitMQ has no strategy to cope with network partitioning if that WAN link fails.

  2. Shovel is a plugin for RabbitMQ that enables you to define replication relationships betwen a queue on one RabbitMQ server and an exchange on another.

  3. Whether you need to replicate messages betwen RabbitMQ servers across the country or across the street, shovel is your go-to solution.

上一篇 下一篇

猜你喜欢

热点阅读