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之後也是正常的,至於爲什麼會出現這種情況依然無法解釋。