RabbitMQ客户端源码 (Java)
客户端的使用
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
// 设置连接参数
factory.setHost("");
factory.setPort(5672);
factory.setUsername("user");
factory.setPassword("password");
factory.setVirtualHost("/");
// 创建连接
Connection connection = factory.newConnection();
// 创建一个Channel 由同一个连接创建的Connection是会共享一个Socket的这个后面会看到
// 一个Channel是可以单独的和服务端交互的一个单位,各个Channel之间不会互相影响
Channel channel = connection.createChannel();
// 创建一个交换器
channel.exchangeDeclare(exchange, "topic",true, false, null);
// 推送消息
channel.basicPublish(exchange, toutingKey, null, message.getBytes());
// 创建一个队列
channel.queueDeclare("queue", true, false, false, null);
// 将队列和转换机通过routingKey绑定
channel.queueBind("queue", "exchange", "routingKey");
// 创建一个默认消费者来处理接收到的消息
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
// 回复ack
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
// 将消费者绑定到queue 第二个参数是true就是自动ack不然就要手动
channel.basicConsume(queue, false, consumer);
// 关闭
channel.close();
connection.close();
// 事务
// 开启事务
channel.txSelect();
// 发送消息
channel.basicPublish(exchange, routingKey, props, body);
// 事务提交
channel.txCommit();
// 事务回滚
channel.txRollback();
/**
* 创建一个交换器.
* @param exchange 交换器的名字
* @param type 交换器的类型(fanout:绑定的Queue全推送;direct:完全匹配才推送;topic:规则匹配推送)
* @param durable true的话就是持久性exchange,该exchange将在服务器重新启动后继续运行
* @param autoDelete true的话就是自动删除,当没有Queue或者Exchange与当前Exchange绑定时,就会自动删除
* @param arguments 交换器的其他属性(构造参数)
*/
Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
/**
* 创建一个队列
* @param queue 队列名
* @param durable 如果我们声明的是持久队列,则为true(该队列将在服务器重新启动后继续存在)
* @param exclusive 多个消费者绑定到多个队列时,RabbitMQ会采用轮询进行投递。如果想独占就设置为true
* @param autoDelete 当消费者断开连接时,队列将会被删除。
* @param arguments 队列的其他属性(构造参数)
*/
Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
创建连接
public Connection newConnection(ExecutorService executor, List<Address> addrs, String clientProvidedName)
throws IOException, TimeoutException {
// make sure we respect the provided thread factory
FrameHandlerFactory fhFactory = createFrameHandlerFactory();
// 把连接信息封装成一个类
ConnectionParams params = params(executor);
// 设置clientProvidedName
if (clientProvidedName != null) {
Map<String, Object> properties = new HashMap<String, Object>(params.getClientProperties());
properties.put("connection_name", clientProvidedName);
params.setClientProperties(properties);
}
// 判断是否启动自动恢复 启动的话就生成一个自动恢复connection
if (isAutomaticRecoveryEnabled()) {
AutorecoveringConnection conn = new AutorecoveringConnection(params, fhFactory, addrs);
conn.init();
return conn;
} else {
IOException lastException = null;
for (Address addr : addrs) {
try {
// 创建一个包裹着socket的handler
FrameHandler handler = fhFactory.create(addr);
// 创建连接
AMQConnection conn = new AMQConnection(params, handler);
// 启动
conn.start();
return conn;
}
}
}
// FrameHandlerFactory
public FrameHandler create(Address addr) throws IOException {
String hostName = addr.getHost();
int portNumber = ConnectionFactory.portOrDefault(addr.getPort(), ssl);
Socket socket = null;
try {
// 创建一个正常socket
socket = factory.createSocket();
// 关闭Nagle算法 设置TCP无延时
configurator.configure(socket);
socket.connect(new InetSocketAddress(hostName, portNumber),
connectionTimeout);
// 返回了一个SocketFrameHandler
return create(socket);
} catch (IOException ioe) {
quietTrySocketClose(socket);
throw ioe;
}
}
// SocketFrameHandler控制了输入输出
public SocketFrameHandler(Socket socket) throws IOException {
_socket = socket;
_inputStream = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
_outputStream = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
}
// 再看connection的start()
public void start() {
// 初始化工作线程 // 默认是newFixedThreadPool数量是2倍处理器
initializeConsumerWorkService();
// 初始化心跳
initializeHeartbeatSender();
// 控制MainLoop能否循环执行
this._running = true;
AMQChannel.SimpleBlockingRpcContinuation connStartBlocker =
new AMQChannel.SimpleBlockingRpcContinuation();
// 把当前任务改为connStartBlocker 加锁阻塞
// 客户端会先发送请求头,然后等待connStart的应答
// 告诉channel0设置一个阻塞rpc任务
_channel0.enqueueRpc(connStartBlocker);
_frameHandler.setTimeout(handshakeTimeout);
// 发送Protocol-Header 0-9-1
_frameHandler.sendHeader();
// 开启MainLoop // 这个就循环去处理发送过来的请求 比如ACK
MainLoop loop = new MainLoop();
final String name = "AMQP Connection " + getHostAddress() + ":" + getPort();
mainLoopThread = Environment.newThread(threadFactory, loop, name);
mainLoopThread.start();
AMQP.Connection.Start connStart;
AMQP.Connection.Tune connTune = null;
// 这里是需要等待服务端返回 connStart就服务端返回的数据
connStart =
(AMQP.Connection.Start) connStartBlocker.getReply(handshakeTimeout/2).getMethod();
do {
// 创建Connection.StartOk
Method method = (challenge == null)
? new AMQP.Connection.StartOk.Builder()
.clientProperties(_clientProperties)
.mechanism(sm.getName())
.response(response)
.build()
: new AMQP.Connection.SecureOk.Builder().response(response).build();
try {
Method serverResponse = _channel0.rpc(method, handshakeTimeout/2).getMethod();
if (serverResponse instanceof AMQP.Connection.Tune) {
// 拿到返回就跳出循环
connTune = (AMQP.Connection.Tune) serverResponse;
} else {
challenge = ((AMQP.Connection.Secure) serverResponse).getChallenge();
response = sm.handleChallenge(challenge, this.username, this.password);
}
} while (connTune == null);
int channelMax =
negotiateChannelMax(this.requestedChannelMax,
connTune.getChannelMax());
// 生成一个ChannelManager 管理除_channel0 外的通信channel
_channelManager = instantiateChannelManager(channelMax, threadFactory);
int frameMax =
negotiatedMaxValue(this.requestedFrameMax,
connTune.getFrameMax());
this._frameMax = frameMax;
int heartbeat =
negotiatedMaxValue(this.requestedHeartbeat,
connTune.getHeartbeat());
setHeartbeat(heartbeat);
// 发送TuneOk
_channel0.transmit(new AMQP.Connection.TuneOk.Builder()
.channelMax(channelMax)
.frameMax(frameMax)
.heartbeat(heartbeat)
.build());
// 发送Open
_channel0.exnWrappingRpc(new AMQP.Connection.Open.Builder()
.virtualHost(_virtualHost)
.build());
}
解释下一些东西,可以看到_channel0是由connection一开始创建的,它是负责这个连接和服务端的交互的,比如一开始发送的Header和接收到Start然后回复。这里_channel0是有好几个方法的。
Channel方法
在这里先提一些东西,RabbitMQ客户端和服务端是通过一种叫Frame格式是传送消息的,可以说Frame是传送的最小单元,但是多个Frame可以拼接成一条完整的信息。其实就是手动拆包,来应对可能很大的消息内容。
// AMQChannel的一些方法
// 排队设置当前的Rpc,当然这个队是java的条件队列
public void enqueueRpc(RpcContinuation k)
{
// _channelMutex相当于这个channel上的锁
synchronized (_channelMutex) {
boolean waitClearedInterruptStatus = false;
while (_activeRpc != null) {
try {
_channelMutex.wait();
} catch (InterruptedException e) {
waitClearedInterruptStatus = true;
}
}
if (waitClearedInterruptStatus) {
Thread.currentThread().interrupt();
}
_activeRpc = k;
}
}
// 向客户端发送当前Method(比如StartOk这种)
public AMQCommand rpc(Method m)
throws IOException, ShutdownSignalException
{
return privateRpc(m);
}
// 同上但是有超时时间
public AMQCommand rpc(Method m, int timeout)
throws IOException, ShutdownSignalException, TimeoutException {
return privateRpc(m, timeout);
}
// 可能会有异常的情况类似上面的rpc
public AMQCommand exnWrappingRpc(Method m)
throws IOException
{
try {
return privateRpc(m);
} catch (AlreadyClosedException ace) {
throw ace;
} catch (ShutdownSignalException ex) {
throw wrap(ex);
}
}
// RPC具体实现
private AMQCommand privateRpc(Method m, int timeout)
throws IOException, ShutdownSignalException, TimeoutException {
// 这个东西下面分析
SimpleBlockingRpcContinuation k = new SimpleBlockingRpcContinuation();
// rpc发送
rpc(m, k);
// 阻塞等待有超时时间
return k.getReply(timeout);
}
// 发送
public void rpc(Method m, RpcContinuation k)
throws IOException
{
synchronized (_channelMutex) {
// 检查状态是不是开启
ensureIsOpen();
quiescingRpc(m, k);
}
}
public void quiescingRpc(Method m, RpcContinuation k)
throws IOException
{
synchronized (_channelMutex) {
// 第一个方法,抢占当前Rpc
enqueueRpc(k);
// 排队发送信息
quiescingTransmit(m);
}
}
// 发送消息
public void transmit(Method m) throws IOException {
synchronized (_channelMutex) {
transmit(new AMQCommand(m));
}
}
public void transmit(AMQCommand c) throws IOException {
synchronized (_channelMutex) {
ensureIsOpen();
// 排队发送消息
quiescingTransmit(c);
}
}
BlockingRpcContinuation
到这里必须先看一下BlockingRpcContinuation这个东西了
public static abstract class BlockingRpcContinuation<T> implements RpcContinuation {
public final BlockingValueOrException<T, ShutdownSignalException> _blocker =
new BlockingValueOrException<T, ShutdownSignalException>();
// 会把transforReply方法的结果交给_blocker 唤醒等待值的阻塞
public void handleCommand(AMQCommand command) {
_blocker.setValue(transformReply(command));
}
// 给定一个值结束掉阻塞
public void handleShutdownSignal(ShutdownSignalException signal) {
_blocker.setException(signal);
}
// 阻塞获取值
public T getReply() throws ShutdownSignalException
{
return _blocker.uninterruptibleGetValue();
}
// 有超时时间的获取值
public T getReply(int timeout)
throws ShutdownSignalException, TimeoutException
{
return _blocker.uninterruptibleGetValue(timeout);
}
public abstract T transformReply(AMQCommand command);
}
Channel方法续
// 这时候再回头看这个getReply,这个就是代表发送后一直等待响应
private AMQCommand privateRpc(Method m, int timeout)
throws IOException, ShutdownSignalException, TimeoutException {
// 这个东西下面分析
SimpleBlockingRpcContinuation k = new SimpleBlockingRpcContinuation();
// rpc发送
rpc(m, k);
// 阻塞等待有超时时间
return k.getReply(timeout);
}
// 处理消息
public void handleFrame(Frame frame) throws IOException {
AMQCommand command = _command;
// 这个handleFrame();是拼接Frame的,可以把Frame拼接成完成的信息放入command
if (command.handleFrame(frame)) { // a complete command has rolled off the assembly line
_command = new AMQCommand(); // prepare for the next one
// 处理command
handleCompleteInboundCommand(command);
}
}
//
public void handleCompleteInboundCommand(AMQCommand command) throws IOException {
// 是不是被统一处理了,统一处理就直接返回了
if (!processAsync(command)) {
// 不处理就说明可以k.getReply(),也就是会唤醒上面privateRpc里面的getReply操作
nextOutstandingRpc().handleCommand(command);
markRpcFinished();
}
}
// 这个是一个抽象方法
public abstract boolean processAsync(Command command) throws IOException;
// 子类ChannelN(也就是正常创建的channel)的处理通信相关消息的处理
public boolean processAsync(Command command) throws IOException
{
// 只列出几个
if (method instanceof Basic.Deliver) {
// 处理推送给消费者消息的
processDelivery(command, (Basic.Deliver) method);
return true;
} else if (method instanceof Basic.Ack) {
// 发送出消息的Ack
Basic.Ack ack = (Basic.Ack) method;
callConfirmListeners(command, ack);
handleAckNack(ack.getDeliveryTag(), ack.getMultiple(), false);
return true;
}
}
// 这是_channel0的实现
private final AMQChannel _channel0 = new AMQChannel(this, 0) {
@Override public boolean processAsync(Command c) throws IOException {
return getConnection().processControlCommand(c);
}
};
// 点进
public boolean processControlCommand(Command c) throws IOException {
// 处理一些和Connection有关的
// 不全列出
if (method instanceof AMQP.Connection.Close) {
handleConnectionClose(c);
return true;
}
}
再看一下创建过程
public Channel createChannel() throws IOException {
ensureIsOpen();
// Channel的管理类
ChannelManager cm = _channelManager;
if (cm == null) return null;
return cm.createChannel(this);
}
// 创建channel
public ChannelN createChannel(AMQConnection connection) throws IOException {
ChannelN ch;
synchronized (this.monitor) {
// 分配编号
int channelNumber = channelNumberAllocator.allocate();
if (channelNumber == -1) {
return null;
} else {
// 新创建一个通道
ch = addNewChannel(connection, channelNumber);
}
}
ch.open(); // now that it's been safely added
return ch;
}
Channel总结
使用rpc(Method m)这种方式就是发送消息阻塞,然后等待回复。 使用transmit(Method m)这种方式就是发送消息不需要等待回复
Channel分两种,一种是正常的手动创建的,一种是伴随着Connection的_channel0,两者的编号是不同的,0号负责的是整个连接的信息交互。不同的Channel通过同一个Socket通信但是靠唯一的编号来控制信息不会出现问题。 我们点几个用到的看看
// 开启事务
public Tx.SelectOk txSelect()
throws IOException
{
// 可以看到是阻塞的那种
return (Tx.SelectOk) exnWrappingRpc(new Tx.Select()).getMethod();
}
// 这个就不是阻塞,就是发出去不需要回复的
// 所以不开启事务的话,消息是无法确保发送到服务端的
public void basicPublish(String exchange, String routingKey,
boolean mandatory, boolean immediate,
BasicProperties props, byte[] body)
throws IOException
{
if (nextPublishSeqNo > 0) {
unconfirmedSet.add(getNextPublishSeqNo());
nextPublishSeqNo++;
}
BasicProperties useProps = props;
if (props == null) {
useProps = MessageProperties.MINIMAL_BASIC;
}
transmit(new AMQCommand(new Basic.Publish.Builder()
.exchange(exchange)
.routingKey(routingKey)
.mandatory(mandatory)
.immediate(immediate)
.build(),
useProps, body));
}
消费者
// 按照刚刚阻塞的讲的方式,看一下消费消息
public String basicConsume(String queue, boolean autoAck, String consumerTag,
boolean noLocal, boolean exclusive, Map<String, Object> arguments,
final Consumer callback)
throws IOException
{
// 这个就是接到服务端应答要触发的
// 这个会在收到消息后调用的
BlockingRpcContinuation<String> k = new BlockingRpcContinuation<String>() {
public String transformReply(AMQCommand replyCommand) {
String actualConsumerTag = ((Basic.ConsumeOk) replyCommand.getMethod()).getConsumerTag();
// 看到返回了一个Tag 以他为key放到了map里面
_consumers.put(actualConsumerTag, callback);
// 这里调用了callback的handleConsumeOk
// 默认实现是把tag放到callback里面
dispatcher.handleConsumeOk(callback, actualConsumerTag);
return actualConsumerTag;
}
};
// RPC,会阻塞在这
rpc(new Basic.Consume.Builder()
.queue(queue)
.consumerTag(consumerTag)
.noLocal(noLocal)
.noAck(autoAck)
.exclusive(exclusive)
.arguments(arguments)
.build(),
k);
try {
return k.getReply();
} catch(ShutdownSignalException ex) {
throw wrap(ex);
}
}
// 使用线程池执行传入的方法
public void handleConsumeOk(final Consumer delegate,
final String consumerTag) {
executeUnlessShuttingDown(
new Runnable() {
public void run() {
try {
delegate.handleConsumeOk(consumerTag);
} catch (Throwable ex) {
connection.getExceptionHandler().handleConsumerException(
channel,
ex,
delegate,
consumerTag,
"handleConsumeOk");
}
}
});
}
注意注意,这里使用的是rpc,但是这个推送的消息却是统一处理的消息,也就是不会有释放的时候,这个channel就只能等待推送消息了。