ActiveMQ有三种消息发送方式,同步(syncSendPacket)、异步(asyncSendPacket)、单向(doOnewaySend)
同步:发送者发送消息给Broker,同步等待,当前线程会阻塞,直到Broker返回应答信息。
异步:发送者发送消息给Broker,消息发送线程不阻塞,继续执行下面的逻辑。由其他线程处理Broker返回的应答信息。
单向:发送者发送消息给Broker之后立即返回,不等待Broker端的应答响应,Broker端也不会有对应的响应信息。简单的说就是只管发送,不在乎是否有消息返回。
AMQ中Producer发送消息主要依靠ActiveMQConnection类,其中发送syncSendPacket方法主要发送同步请求,asyncSendPacket方法主要发送异步请求,下面就AMQ的同步请求和异步请求进行分析。
同步发送API核心入口:
public Response syncSendPacket(Command command, int timeout) throws JMSException
其中command是需要发送的请求内容,timeout是响应超时时间,不设置的话默认是0,该参数此时不起任何作用。
关于同步请求我们有三个地方需要注意 1、Broker同步响应 2、发送线程会阻塞 3、响应处理流程
public FutureResponse asyncRequest(Object o, ResponseCallback responseCallback) throws IOException {
Command command = (Command) o;
command.setCommandId(sequenceGenerator.getNextSequenceId());
command.setResponseRequired(true);
FutureResponse future = new FutureResponse(responseCallback, this);
其中command.setResponseRequired(true)表明Broker接收到请求之后要同步返回响应信息,是AMQ同步请求的标志。
public Object request(Object command) throws IOException {
FutureResponse response = asyncRequest(command, null);
return response.getResult();
}
而我们在发送请求之后会调用response.getResult()方法,该方法会调用阻塞队列responseSlot的take方法获取Broker返回的响应信息,如果这个时候还未收到应答信息则当前线程会处于一个阻塞状态,直到有消息返回为止。
接下来我们看下同步请求响应处理流程
ResponseCorrelator类的onCommand方法负责处理同步响应信息
public void onCommand(Object o) {
Command command = null;
if (o instanceof Command) {
command = (Command)o;
} else {
throw new ClassCastException("Object cannot be converted to a Command, Object: " + o);
}
if (command.isResponse()) {
Response response = (Response)command;
FutureResponse future = null;
synchronized (requestMap) {
future = requestMap.remove(Integer.valueOf(response.getCorrelationId()));
}
if (future != null) {
future.set(response);
。。。。。。。。省略
其中future.set(response)负责将应答信息设置到responseSlot队列中
public void set(Response result) {
if (responseSlot.offer(result)) {
if (responseCallback != null) {
responseCallback.onCompletion(this);
}
}
}
因为responseSlot是阻塞队列,因为队列中有数据进入,此时调用take方法阻塞的线程将会被唤醒,继续执行后面的逻辑。
异步发送核心API接口:
public void asyncSendPacket(Command command) throws JMSException
public void oneway(Object o) throws IOException {
Command command = (Command)o;
command.setCommandId(sequenceGenerator.getNextSequenceId());
command.setResponseRequired(false);
next.oneway(command);
}
其中command.setResponseRequired(false)是异步请求的标志,Broker收到请求之后,不会同步响应应答信息,Broker端具体实现后面详细分析。关于异步请求,我们重点关注响应处理流程
TcpTransport类的run方法负责Producer端所有的消息接收,原文描述为reads packets from a Socket。接下来消息会经过如下链式处理流程 AbstractInactivityMonitor------>MutexTransport------>ResponseCorrelator------>ActiveMQConnection
其中ResponseCorrelator类用于处理同步请求,所以接下来重点分析ActiveMQConnection类的处理流程
public void onCommand(final Object o) {
final Command command = (Command)o;
LOG.info("ActiveMQConnection onCommand: " + command);
if (!closed.get() && command != null) {
try {
command.visit(new CommandVisitorAdapter() {
。。。。。。省略
onCommand方法实际处理异步请求响应,此处command.visit(new CommandVisitorAdapter() {…})使用了适配器设计模式,关于MQ中的设计模式使用,后续会有一篇专题进行分析。处理的响应类型有processBrokerInfo ,processConnectionControl ,processWireFormat ,processMessageDispatch等。而其中processWireFormat 的作用就是把broker返回的protocolVersion 设置到客户端。其他响应类型处理流程也较简单,此处不再过多描述,关于processMessageDispatch处理流程会在消费者模块详细介绍。
最后是单向发送。AMQ中的单向发送最常见的应用场景就是心跳检测机制。AMQ的心跳检测机制依靠AbstractInactivityMonitor中的writeChecker和readChecker实现,其中writeChecker会调用doOnewaySend方法发送KeepAliveInfo信息到Broker端,用于保持跟Broker端的连接,而这个请求是不需要Broker端返回任何响应的,因此它是一个典型的单向发送。