ActiveMQ啓動阻塞問題排查

ActiveMQ啓動阻塞問題排查

首先,看一看有問題的代碼。基於activemq-client-5.14.5.jar封裝的啓動方法:

public void start() throws NotifyException {
        try {
            if (this.connection == null) {
                if (this.clientId == null || this.clientId.length() < 1) {
                    throw new NotifyException("the clientId is null");
                }

                this.connection = this.connectionFactory.createConnection();
                this.connection.setClientID(this.clientId);
            }

            LOG.debug("-  start connection AMQ");
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    try {
                        ActiveMqConnection.this.connection.start();
                    } catch (JMSException var2) {
                        ActiveMqConnection.LOG.debug("-  start connection AMQ error[{}]", var2.getMessage());
                    }

                }
            });
            thread.setName("notify-consumer-start:" + this.clientId);
            thread.start();
        } catch (JMSException var2) {
            LOG.debug("-  start connection AMQ error[{}]", var2.getMessage());
        }

    }

首先創建connection、設置clientId,然後啓動。看似沒毛病。如果activemq未開啓,在createConnection這一步就拋出異常了。但是,這次遇到的問題卻是createConnection成功了,在setClientId這一步阻塞了。
ActiveMQConnection中setClientId

 public void setClientID(String newClientID) throws JMSException {
        this.checkClosedOrFailed();
        if (this.clientIDSet) {
            throw new IllegalStateException("The clientID has already been set");
        } else if (this.isConnectionInfoSentToBroker) {
            throw new IllegalStateException("Setting clientID on a used Connection is not allowed");
        } else {
            this.info.setClientId(newClientID);
            this.userSpecifiedClientID = true;
            this.ensureConnectionInfoSent();
        }
    }

阻塞在了ensureConnectionInfoSent這個方法中,該方法會發送一個包到activemq的服務器,檢查該連接。

protected void ensureConnectionInfoSent() throws JMSException {
        Object var1 = this.ensureConnectionInfoSentMutex;
        synchronized(this.ensureConnectionInfoSentMutex) {
            if (!this.isConnectionInfoSentToBroker && !this.closed.get()) {
                if (this.info.getClientId() == null || this.info.getClientId().trim().length() == 0) {
                    this.info.setClientId(this.clientIdGenerator.generateId());
                }

                this.syncSendPacket(this.info.copy(), this.getConnectResponseTimeout());
                this.isConnectionInfoSentToBroker = true;
                ConsumerId consumerId = new ConsumerId(new SessionId(this.info.getConnectionId(), -1L), this.consumerIdGenerator.getNextSequenceId());
                if (this.watchTopicAdvisories) {
                    this.advisoryConsumer = new AdvisoryConsumer(this, consumerId);
                }

            }
        }
    }

一步一步斷點進入之後,在FailoverTransport中發現一個叫oneway的方法,該方法中有一個死循序

while(true) {
                        if (!this.disposed) {
                            try {
                                Transport transport = (Transport)this.connectedTransport.get();
                                long start = System.currentTimeMillis();

                                boolean timedout;
                                for(timedout = false; transport == null && !this.disposed && this.connectionFailure == null && !Thread.currentThread().isInterrupted() && this.willReconnect(); transport = (Transport)this.connectedTransport.get()) {
                                    LOG.trace("Waiting for transport to reconnect..: {}", command);
                                    long end = System.currentTimeMillis();
                                    if (command.isMessage() && this.timeout > 0L && end - start > this.timeout) {
                                        timedout = true;
                                        LOG.info("Failover timed out after {} ms", end - start);
                                        break;
                                    }

                                    try {
                                        this.reconnectMutex.wait(100L);
                                    } catch (InterruptedException var16) {
                                        Thread.currentThread().interrupt();
                                        LOG.debug("Interupted:", var16);
                                    }
                                }

                                if (transport != null) {
                                    Tracked tracked = null;

                                    try {
                                        tracked = this.stateTracker.track(command);
                                    } catch (IOException var15) {
                                        LOG.debug("Cannot track the command {} {}", command, var15);
                                    }

                                    Map var11 = this.requestMap;
                                    synchronized(this.requestMap) {
                                        if (tracked != null && tracked.isWaitingForResponse()) {
                                            this.requestMap.put(command.getCommandId(), tracked);
                                        } else if (tracked == null && command.isResponseRequired()) {
                                            this.requestMap.put(command.getCommandId(), command);
                                        }
                                    }

                                    try {
                                        transport.oneway(command);
                                        this.stateTracker.trackBack(command);
                                        if (command.isShutdownInfo()) {
                                            this.shuttingDown = true;
                                        }
                                    } catch (IOException var17) {
                                        if (tracked == null && this.canReconnect()) {
                                            if (command.isResponseRequired()) {
                                                this.requestMap.remove(command.getCommandId());
                                            }

                                            throw var17;
                                        }

                                        LOG.debug("Send oneway attempt: {} failed for command: {}", i, command);
                                        this.handleTransportFailure(var17);
                                    }
                                    break;
                                }

                                if (this.disposed) {
                                    error = new IOException("Transport disposed.");
                                } else if (this.connectionFailure != null) {
                                    error = this.connectionFailure;
                                } else if (timedout) {
                                    error = new IOException("Failover timeout of " + this.timeout + " ms reached.");
                                } else if (!this.willReconnect()) {
                                    error = new IOException("Reconnect attempts of " + this.maxReconnectAttempts + " exceeded");
                                } else {
                                    error = new IOException("Unexpected failure.");
                                }
                            } catch (IOException var19) {
                                LOG.debug("Send oneway attempt: {} failed for command: {}", i, command);
                                this.handleTransportFailure(var19);
                                ++i;
                                continue;
                            }
                        }
                        break label202;
                    }

該方法會判斷向activemq服務器的請求是否超時,未超時就一直死循環。就是卡在了這個地方,因爲在自己封裝的啓動方法並未設置超時時間,這裏timeout的值一直是0。
最後,改爲設置超時時間,並將setClientId方法也放到線程中執行。

public void start() throws NotifyException {
        try {
            if (this.connection == null) {
                if (this.clientId == null || this.clientId.length() < 1) {
                    throw new NotifyException("the clientId is null");
                }

                this.connection = this.connectionFactory.createConnection();
                setTimeOut(30000);
            }

            LOG.debug("-  start connection AMQ");
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        connection.setClientID(clientId);
                        ActiveNotifyConnection.this.connection.start();
                    } catch (JMSException var2) {
                        LogUtil.debug(ActiveNotifyConnection.class, "start connection AMQ error");
                    }

                }
            });
            thread.setName("notify-consumer-start:" + this.clientId);
            thread.start();
        } catch (JMSException var2) {
            LOG.debug("-  start connection AMQ error[{}]", var2.getMessage());
        }

    }

其實,就原來的代碼,重啓完activemq之後也是正常的,至於爲什麼會出現這種情況依然無法解釋。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章