源码解析
发送消息
以basicPublish(…)方法为例。
这个方法用于发布一条消息。如果发布到一个不存在的交换器,会抛出通道级别的协议异常,会关闭通道。如果资源驱动的alarm有效,这个方法最终会阻塞。
先来看下它的重载方法。
void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;
void basicPublish(String exchange, String routingKey, boolean mandatory, BasicProperties props, byte[] body) throws Exception;
void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body) throws Exception;
- exchange:交换器的名字。
- routingKey:路由键。
- mandatory:如果设置为true,当交换器无法根据自身的类型和路由键找到一个符合条件的队列时,那么RabbitMQ会调用Basic.Return命令将消息返回给生产者;如果设置为false,遇到这种情况,直接将消息丢弃。
- immediate:如果设置为true,当交换器将消息路由到队列时,发现没有消费者,那么这条消息不会存入队列中。当与路由键匹配的所有队列都没有消费者时,该消息会通过Basic.Return返回给生产者。
- body:消息体。
- BasicProperties:相关的属性,如下。
对于设置mandatory或者immediate参数值为true的情况,可以添加一个ReturnListener,用于对消息的监听。
e.g.
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties, byte[] body) throws IOException {
......
}
});
实际上,是通过AutorecoveringChannel
调用对应的basicPublish(...)
方法。底层如下:
delegate.basicPublish(exchange, routingKey, mandatory, immediate, props, body);
delegate是RecoveryAwareChannelN类型
方法里面的处理如下:
if (nextPublishSeqNo > 0) {
unconfirmedSet.add(getNextPublishSeqNo());
nextPublishSeqNo++;
}
if (props == null) {
props = MessageProperties.MINIMAL_BASIC;
}
AMQCommand command = new AMQCommand(
new Basic.Publish.Builder()
.exchange(exchange)
.routingKey(routingKey)
.mandatory(mandatory)
.immediate(immediate)
.build(), props, body);
try {
transmit(command);
} catch (IOException e) {
metricsCollector.basicPublishFailure(this, e);
throw e;
}
metricsCollector.basicPublish(this);
nextPublishSeqNo:表示下一条发布的需要确认的消息的序列号。
unconfirmedSet:SortedSet类型,表示存储当前未确认消息的集合。
MessageProperties.MINIMAL_BASIC:用于构造MessageProperties实例。其中相关属性都是null。
AMQCommand:AMQP 0-9-1规范的Command的实现类。其封装了Method、AMQContentHeader、以及消息体。
_channelMutex:是一个对象锁。
ensureIsOpen()
方法用来确保此时是开放状态,通过判断ShutdownSignalException属性是否为空来判断,如果它为空,表示此时是开放状态;否则表示此时是关闭状态。
hasContent():表示有消息头或者消息体。
TrafficListener#write(…):表示通知每一个向外的Command。以LogTrafficListener这个实现类为例,仅仅是日志记录。
接着看==AMQPCommand#transmit(…)==方法,如下:
public void transmit(AMQChannel channel) throws IOException {
/* 获取_channelNumber属性,也就是通道编号 */
int channelNumber = channel.getChannelNumber();
/* 获取_connection属性,也就是关联的连接 */
AMQConnection connection = channel.getConnection();
/* CommandAssembler,负责从一系列的Frame中拼凑出一个命令 */
synchronized (assembler) {
Method m = this.assembler.getMethod();
if (m.hasContent()) {
byte[] body = this.assembler.getContentBody();
Frame headerFrame = this.assembler.getContentHeader().toFrame(channelNumber, body.length);
/* 获取_frameMax属性,也就是最大的Frame长度,0表示不受限制 */
int frameMax = connection.getFrameMax();
boolean cappedFrameMax = frameMax > 0;
/* EMPTY_FRAME_SIZE :8 */
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);
}
connection.writeFrame(m.toFrame(channelNumber));
connection.writeFrame(headerFrame);
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 {
connection.writeFrame(m.toFrame(channelNumber));
}
}
connection.flush();
}
CommandAssembler:负责从一系列的Frame中拼凑出一个命令。
Frame:表示AMQP wire-protocol的Frame,带有Frame的类型,通道编号和负载。
刷新缓冲区。实际上就是DataOutputStream调用flush()方法。
ErrorOnWriteListener接口:监听连接在socket上写数据是否遇到IO错误。这个可以用来触发连接恢复。
void handle(Connection connection, IOException exception) throws IOException;
发送消息到broker的核心方法:
HeartbeatSender:管理心跳的发送。
public void signalActivity() {
this.lastActivityTime = System.nanoTime();
}
将Frame写到数据连接中
accumulator:是ByteArrayOutputStream。
可以看出RabbitMQ生产者发送消息,底层是将相关信息写入到DataOutputStream中。