RabbitMQ Explain in Detail

Posted Erato Rabbit

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RabbitMQ Explain in Detail相关的知识,希望对你有一定的参考价值。

“Hello World”(using the Java Client)

RabbitMQ is a message broker: it accepts and forwards messages.

A queue. Although messages flow through RabbitMQ and your applications, they can only be stored inside a queue. A queue is only bound by the host’s memory & disk limits, it’s essentially a large message buffer.

Note that the producer, consumer, and broker do not have to reside on the same host; indeed in most applications they don’t. An application can be both a producer and consumer, too.

Java Client Library

Download the client library and its dependencies (SLF4J API and SLF4J Simple). Copy those files in your working directory, along the tutorials Java files.

Send

We’ll call our message publisher (sender) Send and our message consumer (receiver) Recv. The publisher will connect to RabbitMQ, send a single message, then exit.

public class Send
    private final static String QUEUE_NAME="hello";
    public static void main(String[] args) throw Exception

create a connection to the server:

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try (Connection connection = factory.new Connection();
    Channel channel = connection.createChannel()) 

The connection abstracts the socket connection, and takes care of protocol version negotiation and authentication and so on for us. Here we connect to a RabbitMQ node on the local machine - hence the localhost. If we wanted to connect to a node on a different machine we’d simply specify its hostname or IP address here.

Next we create a channel, which is where most of the API for getting things done resides. Note we can use a try-with-resources statement because both Connection and Channel implement java.lang.AutoCloseable. This way we don’t need to close them explicitly in our code.

To send, we must declare a queue for us to send to; then we can publish a message to the queue, all of this in the try-with-resources statement:

channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "Hello World!";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");

Declaring a queue is idempotent - it will only be created if it doesn’t exist already. The message content is a byte array, so you can encode whatever you like there.

Receive

public class Recev 
    private final static String QUEQUE_NAME="hello";
    
    public static void main(String[] args) throw Exception 
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection conn = factory.newConnection();
        Channel channel = connection.createChannel();
        
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
         System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
    

DeliverCallback deliverCallback = (consunerTag, delivery) -> 
    String message = new String(delivery.getBody(), "UTF-8");
    System.out.println("[x] Received: " + message);

channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -< );

Extend: native JAVA compile

javac & java

javac FirstJavaProgram.java

You may get this error when you try to compile the program: “javac’ is not recognized as an internal or external command, operable program or batch file“. This error occurs when the java path is not set in your system

If you get this error then you first need to set the path before compilation.

After compilation the .java file gets translated into the .class file(byte code). Now we can run the program.

java FirstJavaProgram

Note that you should not append the .java extension

Essentials, Part 1, Lesson 3: Building Applets (oracle.com)

import java.applet.Applet;
import java.awt.Graphics;
import java.awt.Color;

public class SimpleApplet extends Applet

  String text = "I'm a simple applet";

  public void init() 
  //....

packages

The applet code also has three import statements at the top. Applications of any size and all applets use import statements to access ready-made Java API classes in packages.

任何规模的应用程序和所有的小程序都使用import语句来访问packages中现成的Java API类。

-This is true whether the Java API classes come in the Java platform download, from a third-party, or are classes you write yourself and store in a directory separate from the program. At compile time, a program uses import statements to locate and reference compiled Java API classes stored in packages elsewhere on the local or networked system. A compiled class in one package can have the same name as a compiled class in another package. The package name differentiates the two classes.

An application deployed in a JAR archive uses a manifest to describe the contents of the archive. For more information, refer to the Packaging Programs in JAR Files lesson.

Checking the CLASSPATH variable

(All platforms)

The CLASSPATH variable is one way to tell applications, including the JDK tools, where to look for user classes. (Classes that are part of the JRE, JDK platform, and extensions should be defined through other means, such as the bootstrap class path or the extensions directory.)

CLASSPATH 变量是告诉应用程序(包括 JDK 工具)去哪里寻找用户类的一种方法。(作为 JRE、JDK 平台和扩展的一部分的类应通过其他方式定义,例如 bootstrap 类路径或扩展目录)。

The preferred way to specify the class path is by using the -cp command line switch. This allows the CLASSPATH to be set individually for each application without affecting other applications. Setting the CLASSPATH can be tricky and should be performed with care.

指定类路径的首选方法是使用-cp命令行开关。

The default value of the class path is “.”, meaning that only the current directory is searched. Specifying either the CLASSPATH variable or the -cp command line switch overrides this value.

To check whether CLASSPATH is set on Microsoft Windows NT/2000/XP, execute the following:

C:> echo %CLASSPATH%

On Solaris or Linux, execute the following:

% echo $CLASSPATH

If CLASSPATH is not set you will get a CLASSPATH: Undefined variable error (Solaris or Linux) or simply %CLASSPATH% (Microsoft Windows NT/2000/XP).

To modify the CLASSPATH, use the same procedure you used for the PATH variable.

Class path wildcards allow you to include an entire directory of .jar files in the class path without explicitly naming them individually. For more information, including an explanation of class path wildcards, and a detailed description on how to clean up the CLASSPATH environment variable, see the Setting the Class Path technical note.

类路径通配符允许你在类路径中包括整个目录的.jar文件,而不需要明确地单独命名它们。

Answers to Questions: The Platform Environment

Questions

**Question 1.**A programmer installs a new library contained in a .jar file. In order to access the library from his code, he sets the CLASSPATH environment variable to point to the new .jar file. Now he finds that he gets an error message when he tries to launch simple applications:

java Hello
Exception in thread "main" java.lang.NoClassDefFoundError: Hello

In this case, the Hello class is compiled into a .class file in the current directory — yet the java command can’t seem to find it. What’s going wrong?

Answer 1. A class is only found if it appears in the class path. By default, the class path consists of the current directory. If the CLASSPATH environment variable is set, and doesn’t include the current directory, the launcher can no longer find classes in the current directory. The solution is to change the CLASSPATH variable to include the current directory. For example, if the CLASSPATH value is c:\\java\\newLibrary.jar (Windows) or /home/me/newLibrary.jar (UNIX or Linux) it needs to be changed to .;c:\\java\\newLibrary.jar or .:/home/me/newLibrary.jar.

Putting it all together

You can compile both of these with just the RabbitMQ java client on the classpath:

javac -cp amqp-client-5.16.0.jar Send.java Recv.java

To run them, you’ll need rabbitmq-client.jar and its dependencies on the classpath. In a terminal, run the consumer (receiver):

java -cp .:amqp-client-5.16.0.jar:slf4j-api-1.7.36.jar:slf4j-simple-1.7.36.jar Recv

then, run the publisher (sender):

java -cp .:amqp-client-5.16.0.jar:slf4j-api-1.7.36.jar:slf4j-simple-1.7.36.jar Send

On Windows, use a semicolon instead of a colon to separate items in the classpath.

The consumer will keep running, waiting for messages (Use Ctrl-C to stop it), so try running the publisher from another terminal.

x

Messaging with RabbitMQ - SpringBoot

This guide walks you through the process of setting up a RabbitMQ AMQP server that publishes and subscribes to messages and creating a Spring Boot application to interact with that RabbitMQ server.

RabbitMQ in Depth札记——AMQ协议

RPC传输

作为AMQP的实现,RabbitMQ使用RPC(remote procedure call)模式进行远程会话。而不同于一般的RPC会话——客户端发出指令,服务端响应,但服务端不会向客户端发出指令;在AMQP规范中,服务端与客户端皆会发出指令。
对于AMQP,客户端首先发送protocol header至服务端。不过其并不能被当作一条请求指令,RabbitMQ的第一条指令是响应该信息的Connection.Start指令。其后客户端通过Connection.StartOK回应此RPC请求。

为了建立客户端与服务端的AMQP连接(connection),需要一连串的三次同步RPC请求,才能开始(start),调整(tune),打开(open)一条连接。一旦此过程完成,RabbitMQ便为应用程序即将发出的请求作好准备了。
AMQP规范里定义了信道(channel)的概念用于通信。信道使用AMQP连接作为通信的媒介,而其本身有着隔离会话的功能。一个AMQP连接可以包含多个信道,允许客户端与服务端之间的多个会话同时发生,从技术上讲,这被称作复用,它对于处理多个任务的多线程或异步应用十分有用。

AMQP的PRC帧结构

当指令发送至RabbitMQ及由其发出时,所需的参数皆被封装为“帧”(frame)的数据结构——你可以将其想象成火车的运货车厢。AMQP的帧由五个部分组成:

  1. 帧的类型
  2. 信道编号
  3. 帧的大小
  4. 帧的载荷(payload)
  5. 结束字节标志(ASCII值206)

AMQP规范定义了五种帧类型:协议header帧,方法帧,内容header帧,body帧以及心跳帧。每一种帧类型都有其特定功能。

  • 协议header帧仅在连接RabbitMQ时使用一次。
  • 方法帧在发送至RabbitMQ或由其发送的请求或应答中使用。
  • 内容header帧包含消息的大小与属性。
  • body帧包含消息的内容。
  • 心跳帧用于检查确认连接双方是否可用且工作正常。

当向RabbitMQ发送一条消息,方法帧,header帧与body帧会被用到。首先被发送的是方法帧,其携带指令及所需的参数。之后的内容header帧包含消息属性及body大小。AMQP会有最大帧大小的限制(默认为131KB),如果消息的body超过这个大小,内容会被分隔成多个body帧。这些帧始终以同样顺序发送:一个方法帧,内容header帧,和一或多个body帧。

如上图所示,当发送一条消息,方法帧内包含Basic.Publish指令,其后内容header帧包含着消息属性。这些属性被封装在AMQP规范的数据结构——Basic.Properties。最后消息的内容被整理成一定数量的body帧。
为了减小数据大小提高传输效率,方法帧与内容header帧内的数据都经过压缩。但body帧里的数据是未被压缩的,并且对于RabbitMQ而言这部分数据也是不会被检验的(相对于方法帧与内容header帧里的数据)。

AMQP协议的使用

在发送消息之前,至少需要完成三步配置,设置交换机(Exchange)及队列(Queue),并将二者绑定(Binding)。
交换机通过Exchange.Declare指令创建。一旦RabbitMQ完成创建,一个Exchange.DeclareOK的方法帧会被发送以作为响应。若是有任何缘由导致创建指令失败,RabbitMQ会关闭相应的信道。

队列由Queue.Declare指令创建。同样地,若创建失败,相应信道会被关闭。

当交换机与队列都完成创建后,通过Queue.Bind指令,可以绑定一个队列至一个交换机。

RabbitMQ接收一条消息时,会从方法帧开始检测。Basic.Publish方法帧会包含交换机名称,路由键值等关键信息。当RabbitMQ评估这些数据时,会尝试用交换机名称与所配置的交换机相匹配。当找到合适的交换机,它会再评估其中的绑定关系,通过路由键值以找到正确的队列。如果能找到符合条件的队列,RabbitMQ服务器会以先进先出(FIFO)的顺序对消息进行排列。加入队列的并非实际的消息数据,而是其引用。这样做可以大幅提升性能,尤其当消息需要发布到多个队列时。只有所有队列中所引用的消息皆被投递或移除,实际的消息数据才会被RabbitMQ从内存中移除。

在消费(consume)消息时,需要注意Basic.Consume指令中no_ack参数的设置。如果no_ack标置设为true时,RabbitMQ将会持续发送消息,除非消费侧发送Basic.Cancel指令或者消费侧断开连接;若设置为false时,消费侧必须在每次接收到消息时发送Basic.Ack请求。这种情形下,消费侧必须在Basic.Deliver方法帧中携带delivery tag参数。RabbitMQ使用这个投递标签与信道一并作为通信的唯一标识,以用于消息应答,拒绝及否定应答。

以上是关于RabbitMQ Explain in Detail的主要内容,如果未能解决你的问题,请参考以下文章

error in config file "/etc/rabbitmq/rabbitmq.config"

分析查询语句:EXPLAIN

RabbitMQ in Depth札记——AMQ协议

解读EXPLAIN执行计划中的key_len(转)

Elasticsearch es 排查问题 explain 使用 内容解释

MySQL优化—工欲善其事,必先利其器之EXPLAIN