Vorwort
Dieses Mal plane ich, weiterhin zu teilen RabbitMQ Client pulish
--Nachrichten zu senden. Zuerst werde ich eine kurze Zusammenfassung des RabbitMQ-Client-Quellcodes - Connection und des RabbitMQ-Client-Quellcodes - Channel发布消息 - Pulish Message
und Shared Before machen (immer noch basierend auf den vorherigen Java Client Connecting to RabbitMQ Demo
) .
Aus der Abbildung ist ersichtlich, dass RabbitMQ den Nachrichtenprozess veröffentlicht:ConnectionFactory --> Connection --> Channel --> Pulish Message
Interaktive Paketerfassung von Pulish-Nachrichten
Alte Routine – erfassen Sie das Paket, bevor Sie den Quellcode freigeben ( zum schnellen Verständnis )
Sie können anhand der Paketerfassung sehen: Nachdem der Pulisher (Absender der RabbitMQ-Nachricht) und der Broker (RabbitMQ-Broker) Channel
geöffnet wurden, initiieren sie erneut Confirm.Select/Select-Ok
– und benachrichtigen den Broker, dass der Empfang der veröffentlichten Nachricht bestätigt werden muss, also gibt es a nachverfolgenBasic.Pulish/Ack
Nachdem wir den Interaktionsprozess geklärt haben, beginnen wir mit dem heutigen ThemaPulish Message
Analyse des Quellcodes von Pulish Messages
Allgemeiner Eintrag für Nachrichten veröffentlichen – ChannelN.basicPublish()
/** Public API - {@inheritDoc} */
@Override
public void basicPublish(String exchange, String routingKey,
boolean mandatory,
BasicProperties props, byte[] body)
throws IOException
{
basicPublish(exchange, routingKey, mandatory, false, props, body);
}
/** Public API - {@inheritDoc} */
@Override
public void basicPublish(String exchange, String routingKey,
boolean mandatory, boolean immediate,
BasicProperties props, byte[] body)
throws IOException
{
// Pulisher 配置了 `Confirm.Select` nextPublishSeqNo 设置从 1 开始
// 将未确认的消息 放入 unconfirmedSet,并 自增加一
if (nextPublishSeqNo > 0) {
unconfirmedSet.add(getNextPublishSeqNo());
nextPublishSeqNo++;
}
BasicProperties useProps = props;
if (props == null) {
useProps = MessageProperties.MINIMAL_BASIC;
}
// 构造 AMQCommand 并传输
transmit(new AMQCommand(new Basic.Publish.Builder()
.exchange(exchange)
.routingKey(routingKey)
.mandatory(mandatory)
.immediate(immediate)
.build(),
useProps, body));
// 用于指标统计和监控,默认是 NoOpMetricsCollector,需要配置才会可以使用 提供的 MicrometerMetricsCollector 和 StandardMetricsCollector(引入对应的包和配置 开箱即可食用~)
metricsCollector.basicPublish(this);
}
复制代码
Erstellen Sie AMQCommand
Es ist erwähnenswert, dass die kleinste Einheit der RabbitMQ-Client-Anwendungsnachricht Frame
(Frame, erwähnt im Kapitel Connection) ist, und Frame besteht hauptsächlich type 类型、channel 通道、payload 消息内容字节、accumulator 写出数据、NON_BODY_SIZE
aus
Rahmenstruktur
public class Frame {
/** Frame type code */
// FRAME_HEARTBEAT :心跳, FRAME_METHOD: 方法, FRAME_HEADER : 头部信息, FRAME_BODY 内容主题
public final int type;
/** Frame channel number, 0-65535 */
// channel 序列号
public final int channel;
/** Frame payload bytes (for inbound frames) */
// 消息内容字节
private final byte[] payload;
/** Frame payload (for outbound frames) */
// 写出数据
private final ByteArrayOutputStream accumulator;
private static final int NON_BODY_SIZE = 1 /* type */ + 2 /* channel */ + 4 /* payload size */ + 1 /* end character */;
...
}
复制代码
AMQP 0-9-1 spezifischer Lesebefehl , akkumuliert aus einer Reihe von Frames方法、头部和正文
/**
* Construct a command with a specified method, header and body.
* @param method the wrapped method
* @param contentHeader the wrapped content header
* @param body the message body data
*/
public AMQCommand(com.rabbitmq.client.Method method, AMQContentHeader contentHeader, byte[] body) {
this.assembler = new CommandAssembler((Method) method, contentHeader, body);
}
// AMQP 0-9-1 特定的Command,构造 方法、头部和正文
public CommandAssembler(Method method, AMQContentHeader contentHeader, byte[] body) {
this.method = method;
this.contentHeader = contentHeader;
this.bodyN = new ArrayList<byte[]>(2);
this.bodyLength = 0;
this.remainingBodyBytes = 0;
appendBodyFragment(body);
if (method == null) {
this.state = CAState.EXPECTING_METHOD;
} else if (contentHeader == null) {
this.state = method.hasContent() ? CAState.EXPECTING_CONTENT_HEADER : CAState.COMPLETE;
} else {
this.remainingBodyBytes = contentHeader.getBodySize() - this.bodyLength;
updateContentBodyState();
}
}
复制代码
AMQCommand übertragen - Channel.transmit()
public void transmit(AMQCommand c) throws IOException {
synchronized (_channelMutex) {
// 确认 channel 是否打开(逻辑比较简单:判断 shutdownCause 为空即是打开)
ensureIsOpen();
quiescingTransmit(c);
}
}
public void quiescingTransmit(AMQCommand c) throws IOException {
// 防止并发同时使用 同一个channel
synchronized (_channelMutex) {
// 判断 该消息是否 携带content,如果有 需要判断该 channel是否是阻塞(如果channel state为 `FLOW` 即为 阻塞 _blockContent = true)
if (c.getMethod().hasContent()) {
while (_blockContent) {
try {
_channelMutex.wait();
} catch (InterruptedException ignored) {}
// 防止 从阻塞中被唤醒时,channel 已经关闭(挺好的一个 多线程操作的案例)
ensureIsOpen();
}
}
c.transmit(this);
}
}
复制代码
AMQCommand.transmit
/**
* Sends this command down the named channel on the channel's
* connection, possibly in multiple frames.
* @param channel the channel on which to transmit the command
* @throws IOException if an error is encountered
*/
public void transmit(AMQChannel channel) throws IOException {
// 每个 channel 都有序列号 从 0开始,(0是特殊的channel)
int channelNumber = channel.getChannelNumber();
AMQConnection connection = channel.getConnection();
synchronized (assembler) {
// 方法:FRAME_HEARTBEAT :心跳, FRAME_METHOD: 方法, FRAME_HEADER : 头部信息, FRAME_BODY 内容主题
Method m = this.assembler.getMethod();
if (m.hasContent()) {
byte[] body = this.assembler.getContentBody();
// FRAME_HEADER : 头部信息
Frame headerFrame = this.assembler.getContentHeader().toFrame(channelNumber, body.length);
int frameMax = connection.getFrameMax();
boolean cappedFrameMax = frameMax > 0;
int bodyPayloadMax = cappedFrameMax ? frameMax - EMPTY_FRAME_SIZE : body.length;
if (cappedFrameMax && headerFrame.size() > frameMax) {
String msg = String.format("Content headers exceeded max frame size: %d > %d", headerFrame.size(), frameMax);
throw new IllegalArgumentException(msg);
}
// 1. 写 channelNumber帧 FRAME_METHOD
connection.writeFrame(m.toFrame(channelNumber));
// 2. 写 头部信息帧 AMQP.FRAME_HEADER
connection.writeFrame(headerFrame);
// 3. 如果 body过多,会拆成多个帧 AMQP.FRAME_BODY
for (int offset = 0; offset < body.length; offset += bodyPayloadMax) {
int remaining = body.length - offset;
int fragmentLength = (remaining < bodyPayloadMax) ? remaining
: bodyPayloadMax;
Frame frame = Frame.fromBodyFragment(channelNumber, body,
offset, fragmentLength);
connection.writeFrame(frame);
}
} else {
// 1. 写 channelNumber帧 FRAME_METHOD
connection.writeFrame(m.toFrame(channelNumber));
}
}
// 最后刷新 输出缓冲区
connection.flush();
}
复制代码
Analysieren Sie abschließend connection.writeFrame(frame)
/**
* Public API - sends a frame directly to the broker.
*/
public void writeFrame(Frame f) throws IOException {
_frameHandler.writeFrame(f);
// lastActivityTime
_heartbeatSender.signalActivity();
}
@Override
public void writeFrame(Frame frame) throws IOException {
synchronized (_outputStream) {
frame.writeTo(_outputStream);
}
}
/**
* Public API - writes this Frame to the given DataOutputStream
*/
public void writeTo(DataOutputStream os) throws IOException {
// 1. 写type 类型
os.writeByte(type);
// 2. 写channel 序列号
os.writeShort(channel);
if (accumulator != null) {
// 3. 写出数据大小
os.writeInt(accumulator.size());
// 4. 输出数据
accumulator.writeTo(os);
} else {
// 3. 写消息内容字节大小
os.writeInt(payload.length);
// 4. 写消息内容
os.write(payload);
}
// 5. 帧结束标志位
os.write(AMQP.FRAME_END);
}
复制代码
endlich
Ich hoffe, es kann Ihnen ein klares Verständnis RabbitMQ Client
des AMQPRabbitMQ Broker
-Protokolls vermitteln und Ihnen helfen, sich mit dem Quellcode vertraut zu machen.Natürlich gibt es viele Details des Quellcodes, die von den Lesern langsam probiert werden müssen~发布消息
ChannelN.basicPublish
Mein öffentliches WeChat-Konto: Java Architect Advanced Programming
Konzentrieren Sie sich auf das Teilen von Java-Technologie-Trockenwaren und freuen Sie sich auf Ihre Aufmerksamkeit!