RabbitMQ客户端源码分析(一)之Connection与ConnectionFactory(好几篇可以分别查看)

文章目录
版本声明
Connection
连接过程分析
连接过程抓包分析
源码分析
ConnectionFactory
AMQConnection分析
版本声明
com.rabbitmq:amqp-client:4.3.0
RabbitMQ版本声明: 3.6.15
Connection
这里的连接其实就表示的是TCP/IP socket连接,一个Connection有多个Channel,意味Connection会被设计为长连接,并且每个Channel一定有一个唯一标识,客户端请求和服务端响应都会携带这个标识,以区分是哪个通道。
连接过程分析
client打开与服务器的TCP/IP连接并发送一个协议头(protocol header).这只是client发送的数据,而不是作为方法格式的数据.
server使用其协议版本和其它属性,包括它支持安全机制列表(Start方法)进行响应.
client选择一种安全机制(Start-Ok).
server开始认证过程, 它使用SASL的质询-响应模型(challenge-response model). 它向客户端发送一个质询(Secure).
client向server发送一个认证响应(Secure-Ok). 例如,对于使用"plain"机制,响应会包含登录用户名和密码.
server 重复质询(Secure) 或转到协商,发送一系列参数,如最大帧大小(Tune).
client接受或降低这些参数(Tune-Ok).
client 正式打开连接并选择一个虚拟主机(Open).
服务器确认虚拟主机是一个有效的选择 (Open-Ok).
客户端现在使用希望的连接.
一个节点(client 或 server) 结束连接(Close).
另一个节点对连接结束握手(Close-Ok).
server 和 client关闭它们的套接字连接.
连接流转图

连接过程抓包分析
整个连接过程,我们从抓包结果来分析,只关注AMQP协议即可


第一步客户端发送Protocol-Hearder


服务端响应Connection.start


客户端发送Connection.Start-Ok


服务端发送Connection.Tune


客户端发送Connection.Tune-Ok


客户端发送Connection.Open


服务端发送Connection.Open-Ok


源码分析
uml图

创建连接时序图

ConnectionFactory
创建连接的工厂ConnectionFactory,包含创建连接的一些必备参数。

核心方法newConnection()


    public Connection newConnection(ExecutorService executor, AddressResolver addressResolver, String clientProvidedName)
            throws IOException, TimeoutException {
            if(this.metricsCollector == null) {
                //这是一个空的收集,没有任何操作
                this.metricsCollector = new NoOpMetricsCollector();
            }
            // make sure we respect the provided thread factory
            //根据参数创建FrameHandlerFactory,BIO和NIO
            FrameHandlerFactory fhFactory = createFrameHandlerFactory();
            //此executor是用于消费者消费使用的(consumerWorkServiceExecutor)
            ConnectionParams params = params(executor);
            // set client-provided via a client property
            if (clientProvidedName != null) {
                Map<String, Object> properties = new HashMap<String, Object>(params.getClientProperties());
                properties.put("connection_name", clientProvidedName);
                params.setClientProperties(properties);
            }
            //默认自动恢复连接
            if (isAutomaticRecoveryEnabled()) {
                // see com.rabbitmq.client.impl.recovery.RecoveryAwareAMQConnectionFactory#newConnection
                AutorecoveringConnection conn = new AutorecoveringConnection(params, fhFactory, addressResolver, metricsCollector);
    
                conn.init();
                return conn;
            } else {
                List<Address> addrs = addressResolver.getAddresses();
                Exception lastException = null;
                for (Address addr : addrs) {
                    try {
                        FrameHandler handler = fhFactory.create(addr);
                        AMQConnection conn = createConnection(params, handler, metricsCollector);
                        conn.start();
                        this.metricsCollector.newConnection(conn);
                        return conn;
                    } catch (IOException e) {
                        lastException = e;
                    } catch (TimeoutException te) {
                        lastException = te;
                    }
                }
                if (lastException != null) {
                    if (lastException instanceof IOException) {
                        throw (IOException) lastException;
                    } else if (lastException instanceof TimeoutException) {
                        throw (TimeoutException) lastException;
                    }
                }
                throw new IOException("failed to connect");
            }
        }
   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
createFrameHandlerFactory, FrameHandlerFactory

  protected synchronized FrameHandlerFactory createFrameHandlerFactory() throws IOException {
           if(nio) {
               if(this.frameHandlerFactory == null) {
                   if(this.nioParams.getNioExecutor() == null && this.nioParams.getThreadFactory() == null) {
                       this.nioParams.setThreadFactory(getThreadFactory());
                   }
                   this.frameHandlerFactory = new SocketChannelFrameHandlerFactory(connectionTimeout, nioParams, isSSL(), sslContext);
               }
               return this.frameHandlerFactory;
           } else {
               return new SocketFrameHandlerFactory(connectionTimeout, factory, socketConf, isSSL(), this.shutdownExecutor);
           }
   
       }    

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
AMQConnection分析
AMQConnection详细注释

起来启动的核心代码

     /**
         * 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.
         *
         *
         * client收到Connection.Tune方法后,必须要开始发送心跳,
         * 并在收到Connection.Open后,必须要开始监控.server在收到Connection.Tune-Ok后,
         * 需要开始发送和监控心跳.
         */
        public void start()
                throws IOException, TimeoutException {
            //创建Consumer服务
            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
    
            /**
             * 先发送header,确保socket是否会发生错误,比在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);
            try {
                // The following two lines are akin to AMQChannel's
                // transmit() method for this pseudo-RPC.
                _frameHandler.setTimeout(handshakeTimeout);
                //发送一个协议头开始新的连接,格式为'AMQP0091'
                _frameHandler.sendHeader();
            } catch (IOException ioe) {
                _frameHandler.close();
                throw ioe;
            }
            /**
             * 此处就是启动MainLoop(源码是 connection.startMainLoop())
             * 把连接启动放入到framehandler的  initialize()方法中,这样的设计是否合理??
             */
    
            this._frameHandler.initialize(this);
    
            AMQP.Connection.Start connStart;
            AMQP.Connection.Tune connTune = null;
            try {
                connStart =
                        (AMQP.Connection.Start) connStartBlocker.getReply(handshakeTimeout/2).getMethod();
    
                _serverProperties = Collections.unmodifiableMap(connStart.getServerProperties());
    
                Version serverVersion =
                        new Version(connStart.getVersionMajor(),
                                           connStart.getVersionMinor());
    
                if (!Version.checkVersion(clientVersion, serverVersion)) {
                    throw new ProtocolVersionMismatchException(clientVersion,
                                                                      serverVersion);
                }
                //mechanisms(机制),返回的数据形如:AMQPLAIN PLAIN
                String[] mechanisms = connStart.getMechanisms().toString().split(" ");
                SaslMechanism sm = this.saslConfig.getSaslMechanism(mechanisms);
                if (sm == null) {
                    throw new IOException("No compatible authentication mechanism found - " +
                                                  "server offered [" + connStart.getMechanisms() + "]");
                }
    
                LongString challenge = null;
                LongString response = sm.handleChallenge(null, this.username, this.password);
    
                do {
                    //构建Start-OK(认证机制)
                    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);
                        }
                    } catch (ShutdownSignalException e) {
                        Method shutdownMethod = e.getReason();
                        if (shutdownMethod instanceof AMQP.Connection.Close) {
                            AMQP.Connection.Close shutdownClose = (AMQP.Connection.Close) shutdownMethod;
                            if (shutdownClose.getReplyCode() == AMQP.ACCESS_REFUSED) {
                                throw new AuthenticationFailureException(shutdownClose.getReplyText());
                            }
                        }
                        throw new PossibleAuthenticationFailureException(e);
                    }
                } while (connTune == null);
            } catch (TimeoutException te) {
                _frameHandler.close();
                throw te;
            } catch (ShutdownSignalException sse) {
                _frameHandler.close();
                throw AMQChannel.wrap(sse);
            } catch(IOException ioe) {
                _frameHandler.close();
                throw ioe;
            }
    
            try {
                //协商通道最大编号,协商规则如下
                // (clientValue == 0 || serverValue == 0) ?Math.max(clientValue, serverValue) :Math.min(clientValue, serverValue);
                int channelMax =
                    negotiateChannelMax(this.requestedChannelMax,
                                        connTune.getChannelMax());
                //创建通道管理器
                _channelManager = instantiateChannelManager(channelMax, threadFactory);
    
                //协商Frame的最大长度
                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());
            } catch (IOException ioe) {
                _heartbeatSender.shutdown();
                _frameHandler.close();
                throw ioe;
            } catch (ShutdownSignalException sse) {
                _heartbeatSender.shutdown();
                _frameHandler.close();
                throw AMQChannel.wrap(sse);
            }
    
            // We can now respond to errors having finished tailoring the connection
            this._inConnectionNegotiation = false;
    }
    
————————————————
版权声明:本文为CSDN博主「jannals」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/usagoole/article/details/83048039

来源:RabbitMQ客户端源码分析(一)之Connection与ConnectionFactory_jannals的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值