首先来看看start()方法的源码,这个方法有点长,这里拆开来一一分析,首先是注释:
/**
* Start up the connection, including the MainLoop thread.
* Sends the protocol
* version negotiation header, and runs through
* Connection.Start/.StartOk, Connection.Tune/.TuneOk, and then
* calls Connection.Open and waits for the OpenOk. Sets heart-beat
* and frame max values after tuning has taken place.
* @throws IOException if an error is encountered
* either before, or during, protocol negotiation;
* sub-classes {@link ProtocolVersionMismatchException} and
* {@link PossibleAuthenticationFailureException} will be thrown in the
* corresponding circumstances. {@link AuthenticationFailureException}
* will be thrown if the broker closes the connection with ACCESS_REFUSED.
* If an exception is thrown, connection resources allocated can all be
* garbage collected when the connection object is no longer referenced.
*/
首先来看看方法上的注释说了什么:
- 方法的作用是启动连接(start up the connection), 包括启动MainLoop线程,这个MainLoop线程主要是和broker进行通信交互处理通信帧(Frame)的一个线程(非常的重要!!!)。
- 这个方法会在建立连接的初始化阶段(negotiation)会进行Connection.Start/.StartOk, Connection.Tune/.TuneOk, 调用Connection.Open之后再等待Conenction.OpenOk(这里的都是指AMQP协议层面的),这个可以参考本文中第一张使用wireshark抓包的网络截图,一一对应的关系。
- 通过broker回复的Connection.Tune帧(帧中包含Channel-Max, Frame-Max, Heartbeat三个参数)设置channelMax, frameMax以及Heartbeat的参数值。
- 一些异常情况。
public void start()
throws IOException, TimeoutException {
initializeConsumerWorkService();
initializeHeartbeatSender();
this._running = true;
// Make sure that the first thing we do is to send the header,
// which should cause any socket errors to show up for us, rather
// than risking them pop out in the MainLoop
AMQChannel.SimpleBlockingRpcContinuation connStartBlocker =
new AMQChannel.SimpleBlockingRpcContinuation();
// We enqueue an RPC continuation here without sending an RPC
// request, since the protocol specifies that after sending
// the version negotiation header, the client (connection
// initiator) is to wait for a connection.start method to
// arrive.
_channel0.enqueueRpc(connStartBlocker);
首先是初始化工作线程池(initializeConsumerWorkService)和初始化心跳线程(initializeHeartbeatSender)并设置运行状态为true(this._isrunning=true,这个值会在MainLoop线程中有用,控制MainLoop线程是否继续运行)。
“AMQChannel.SimpleBlockingRpcContinuation connStartBlocker = new AMQChannel.SimpleBlockingRpcContinuation();”这句代码,从命名上来说像是rpc, 其实这么理解也没错。RabbitMQ-Client这个版本(3.5.3)的客户端与broker端的通信是采用java原生socket.当然后面也改成了NIO,这个自然是后话。RabbitMQ-Client程序中会对各种帧进行处理,处理的方式也不是单一化的,这里举Connection.Start这个类型的报文做分析。当broker发送Connection.Start至client端,client收到之后进行处理(MainLoop线程中),然后将此报文存入SimpleBlockingRpcContinuation中,照着SimpleBlockingRpcContinuation深究下去,其就是一个容量为1的BlockingQueue,也就是当MainLoop主导的线程将收到的Connection.Start存入其中,然后AMQConnction类的start()线程在等待(start()方法下面的代码):
connStart = (AMQP.Connection.Start) connStartBlocker.get