RocketMQ release-5.1.0 Client源码阅读-消息发送

RocketMQ官方文档地址

https://rocketmq.apache.org/zh/docs/

Client发送消息的方式

Producer与Broker的交互过程

单向发送

在这里插入图片描述

  1. 客户端线程(Producer)使用Netty将消息发送给服务端(Broker)后,交互就结束了
同步发送

在这里插入图片描述

  1. 客户端线程(Producer)将消息发送给服务端(Broker),发送成功后阻塞线程,等待服务端响应
  2. 服务端(Broker)将响应信息发送给客户端(Producer)
  3. 客户端(Producer)从本地缓存中获取收到的响应对应的消息,并唤醒消息对应的线程去执行收到响应后的动作
异步发送

在这里插入图片描述

  1. 客户端线程(Producer)将消息加入DefaultMQProducerImpl的asyncSenderExecutor中后,发送消息的方法就结束了
  2. asyncSenderExecutor线程池将需要发送的消息发送给服务端(Broker)
  3. 服务端(Broker)将响应信息发送给客户端(Producer),Producer收到响应后从本地缓存中获取对应的消息,并执行该消息对应的回调方法

示例

示例都是来源于源码,源码地址:https://gitee.com/apache/rocketmq?_from=gitee_search

单向发送

	DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
    // Specify name server addresses.
    producer.setNamesrvAddr("localhost:9876");
    //Launch the instance.
    producer.start();
    for (int i = 0; i < 100; i++) {
        //Create a message instance, specifying topic, tag and message body.
        Message msg = new Message("TopicTest" /* Topic */,
                "TagA" /* Tag */,
                ("Hello RocketMQ " +
                        i).getBytes(StandardCharsets.UTF_8) /* Message body */
        );
        //Call send message to deliver message to one of brokers.
        producer.sendOneway(msg);
    }
    //Wait for sending to complete
    Thread.sleep(5000);
    producer.shutdown();
同步发送
	DefaultMQProducer producer = new DefaultMQProducer(PRODUCER_GROUP);
    producer.setNamesrvAddr(DEFAULT_NAMESRVADDR);
	producer.start();
	for (int i = 0; i < 128; i++) {
		try {
			Message msg = new Message(TOPIC, TAG, "OrderID188", "Hello world".getBytes(StandardCharsets.UTF_8));
			// 消息和过期时间,也可以这样producer.send(msg)过期时间为producer的默认时间3000ms
			SendResult sendResult = producer.send(msg, 1000);
			System.out.printf("%s%n", sendResult);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	producer.shutdown();
异步发送
	DefaultMQProducer producer = new DefaultMQProducer("Jodie_Daily_test");
	producer.setNamesrvAddr("localhost:9876");
	producer.start();
	// suggest to on enableBackpressureForAsyncMode in heavy traffic, default is false
	producer.setEnableBackpressureForAsyncMode(true);
	producer.setRetryTimesWhenSendAsyncFailed(0);
	
	int messageCount = 100;
	final CountDownLatch countDownLatch = new CountDownLatch(messageCount);
	for (int i = 0; i < messageCount; i++) {
	    try {
	        final int index = i;
	        Message msg = new Message("TopicTest",
	            "TagA",
	            "OrderID188",
	            "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
	        producer.send(msg, new SendCallback() {
	            @Override
	            public void onSuccess(SendResult sendResult) {
	                countDownLatch.countDown();
	                System.out.printf("%-10d OK %s %n", index, sendResult.getMsgId());
	            }
	            @Override
	            public void onException(Throwable e) {
	                countDownLatch.countDown();
	                System.out.printf("%-10d Exception %s %n", index, e);
	                e.printStackTrace();
	            }
	        });
	    } catch (Exception e) {
	        e.printStackTrace();
	    }
	}
	countDownLatch.await(5, TimeUnit.SECONDS);
	producer.shutdown();

源码阅读

DefaultMQProducer

对DefaultMQProducerImpl的包装,几乎DefaultMQProducer的所有方法都是调用DefaultMQProducerImpl的方法来实现

  • retryTimesWhenSendFailed:同步模式下消息发送的重试次数,默认为2
  • retryTimesWhenSendAsyncFailed:异步模式下的消息发送的重试次数,默认为2
单向发送:sendOneway(Message msg)

在返回之前不等待broker的ack,发送失败不重试,具有最大的吞吐量,但是有可能丢失消息,调用DefaultMQProducerImpl的sendOneway方法实现

    public void sendOneway(Message msg) throws MQClientException, RemotingException, InterruptedException {
        msg.setTopic(withNamespace(msg.getTopic()));
        this.defaultMQProducerImpl.sendOneway(msg);
    }
同步发送:SendResult send(Message msg, long timeout)

发送消息完成时返回,方法具有内部重试机制,内部在声明失败之前会重试{retryTimesWhenSendFailed}次。因此,broker可能会存在消息重复的问题

    public SendResult send(Message msg,
        long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
        msg.setTopic(withNamespace(msg.getTopic()));
        return this.defaultMQProducerImpl.send(msg, timeout);
    }
异步发送:void send(Message msg, SendCallback sendCallback)

将msg放到DefaultMQProducerImpl内部的线程池里执行,msg添加到线程池后,此方法结束,消息发送发送失败最多重试retryTimesWhenSendAsyncFailed次,发送成功或失败后执行sendCallback中的方法

    public void send(Message msg,
        SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException {
        msg.setTopic(withNamespace(msg.getTopic()));
        this.defaultMQProducerImpl.send(msg, sendCallback);
    }

DefaultMQProducerImpl
sendOneway(Message msg):单向发送
    public void sendOneway(Message msg) throws MQClientException, RemotingException, InterruptedException {
        try {
            this.sendDefaultImpl(msg, CommunicationMode.ONEWAY, null, this.defaultMQProducer.getSendMsgTimeout());
        } catch (MQBrokerException e) {
            throw new MQClientException("unknown exception", e);
        }
    }
SendResult send(Message msg, long timeout):同步发送
    public SendResult send(Message msg,
        long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
        return this.sendDefaultImpl(msg, CommunicationMode.SYNC, null, timeout);
    }
send(Message msg, SendCallback sendCallback):异步发送
    public void send(Message msg,
        SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException {
        msg.setTopic(withNamespace(msg.getTopic()));
        this.defaultMQProducerImpl.send(msg, sendCallback);
    }
    public void send(Message msg,
        SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException {
        send(msg, sendCallback, this.defaultMQProducer.getSendMsgTimeout());
    }
    // 将需要发送的消息包装成一个Runnable,然后放到线程池中执行
    public void send(final Message msg, final SendCallback sendCallback, final long timeout)
        throws MQClientException, RemotingException, InterruptedException {
        final long beginStartTime = System.currentTimeMillis();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                long costTime = System.currentTimeMillis() - beginStartTime;
                if (timeout > costTime) {
                    try {
                        sendDefaultImpl(msg, CommunicationMode.ASYNC, sendCallback, timeout - costTime);
                    } catch (Exception e) {
                        sendCallback.onException(e);
                    }
                } else {
                    sendCallback.onException(
                            new RemotingTooMuchRequestException("DEFAULT ASYNC send call timeout"));
                }
            }
        };
        executeAsyncMessageSend(runnable, msg, sendCallback, timeout, beginStartTime);
        
    }
    // 如果打开了流量控制即DefaultMQProducer的enableBackpressureForAsyncMode为true,会通过构造方法中初始化的Semaphore来控制正在执行的消息数量和总的消息的大小,这个值如果为默认值(false),该方法等同于executor.submit(runnable);
	public void executeAsyncMessageSend(Runnable runnable, final Message msg, final SendCallback sendCallback,
                                         final long timeout, final long beginStartTime)
            throws MQClientException, InterruptedException {
        ExecutorService executor = this.getAsyncSenderExecutor();
        boolean isEnableBackpressureForAsyncMode = this.getDefaultMQProducer().isEnableBackpressureForAsyncMode();
        boolean isSemaphoreAsyncNumAquired = false;
        boolean isSemaphoreAsyncSizeAquired = false;
        int msgLen = msg.getBody() == null ? 1 : msg.getBody().length;

        try {
        	// 打开了流量控制,获取信号量,先获取数量的,再获取大小的,获取失败就认为消息发送失败,将调用sendCallback的onException方法
            if (isEnableBackpressureForAsyncMode) {
                long costTime = System.currentTimeMillis() - beginStartTime;
                isSemaphoreAsyncNumAquired = timeout - costTime > 0
                        && semaphoreAsyncSendNum.tryAcquire(timeout - costTime, TimeUnit.MILLISECONDS);
                if (!isSemaphoreAsyncNumAquired) {
                    sendCallback.onException(
                            new RemotingTooMuchRequestException("send message tryAcquire semaphoreAsyncNum timeout"));
                    return;
                }
                costTime = System.currentTimeMillis() - beginStartTime;
                isSemaphoreAsyncSizeAquired = timeout - costTime > 0
                        && semaphoreAsyncSendSize.tryAcquire(msgLen, timeout - costTime, TimeUnit.MILLISECONDS);
                if (!isSemaphoreAsyncSizeAquired) {
                    sendCallback.onException(
                            new RemotingTooMuchRequestException("send message tryAcquire semaphoreAsyncSize timeout"));
                    return;
                }
            }
            // 将消息对应的线程加入线程池
            executor.submit(runnable);
        } catch (RejectedExecutionException e) {
            if (isEnableBackpressureForAsyncMode) {
                runnable.run();
            } else {
                throw new MQClientException("executor rejected ", e);
            }
        } finally {
            if (isSemaphoreAsyncSizeAquired) {
                semaphoreAsyncSendSize.release(msgLen);
            }
            if (isSemaphoreAsyncNumAquired) {
                semaphoreAsyncSendNum.release();
            }
        }

    }
SendResult sendDefaultImpl(Message msg, final CommunicationMode communicationMode, final SendCallback sendCallback, final long timeout ):真正的消息发送方法

单向发送、同步发送以及异步发送都是通过对这个方法的包装来实现,找broker的路由信息,确认msg发送的MessageQueue,真正的发送消息通过调用sendKernelImpl方法来实现。
CommunicationMode枚举类

  • SYNC:同步发送标识,如果是broker的返回值显示失败,retryAnotherBrokerWhenNotStoreOK为true时会重试,为false不重试,其他失败会默认重试
  • ASYNC:异步发送标识,在这里只循环一次,无论成功还是失败
  • ONEWAY:单向发送标识,在这里只循环一次,无论成功还是失败
	// 这个方法太长了,只展示正向的逻辑了
	private SendResult sendDefaultImpl(
        Message msg,
        final CommunicationMode communicationMode,
        final SendCallback sendCallback,
        final long timeout
    ) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
    	......
    	// 获取msg的topic对应的broker,messageQueue等信息
        TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());
        // if的逻辑一定成立,如果不成立,认为存在问题,会抛出异常
        if (topicPublishInfo != null && topicPublishInfo.ok()) {
        	......
        	// 获取重试次数
            int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;
            for (; times < timesTotal; times++) {
                String lastBrokerName = null == mq ? null : mq.getBrokerName();
                // 获取具体的MessageQueue,发送失败后会选择其他的再试一次,底层逻辑相当于messageQueueList.get(上一个MessageQueue的索引+1 % this.messageQueueList.size())
                MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);
                if (mqSelected != null) {
                    mq = mqSelected;
                    try {
                        if (times > 0) {
                            //Reset topic with namespace during resend.
                            msg.setTopic(this.defaultMQProducer.withNamespace(msg.getTopic()));
                        }
                        sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout - costTime);
                        switch (communicationMode) {
                            case ASYNC:
                                return null;
                            case ONEWAY:
                                return null;
                            case SYNC:
                                if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
                                    if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) {
                                        continue;
                                    }
                                }
                                return sendResult;
                            default:
                                break;
                        }
                    }
                } 
            }

            if (sendResult != null) {
                return sendResult;
            }
        }
    }
SendResult sendKernelImpl

包装参数,然后调用MQClientInstance.getMQClientAPIImpl().sendMessage方法进行真正的消息发送

  • final Message msg:需要发送的消息
  • final MessageQueue mq:消息的目标MessageQueue
  • final CommunicationMode communicationMode:发送方式,SYNC,ASYNC,ONEWAY
  • final SendCallback sendCallback:异步发送后完成后的回调处理
  • final TopicPublishInfo topicPublishInfo:消息发送的目标的路由信息
  • final long timeout:消息发送的超时时间
	// 这个 
    private SendResult sendKernelImpl(final Message msg,
        final MessageQueue mq,
        final CommunicationMode communicationMode,
        final SendCallback sendCallback,
        final TopicPublishInfo topicPublishInfo,
        final long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
        String brokerName = this.mQClientFactory.getBrokerNameFromMessageQueue(mq);
        String brokerAddr = this.mQClientFactory.findBrokerAddressInPublish(brokerName);
       	......
        if (brokerAddr != null) {
            byte[] prevBody = msg.getBody();
            try {
                ......
                // 包装消息头
                SendMessageRequestHeader requestHeader = new SendMessageRequestHeader();
                requestHeader.setProducerGroup(this.defaultMQProducer.getProducerGroup());
                requestHeader.setTopic(msg.getTopic());
                requestHeader.setDefaultTopic(this.defaultMQProducer.getCreateTopicKey());
                requestHeader.setDefaultTopicQueueNums(this.defaultMQProducer.getDefaultTopicQueueNums());
                requestHeader.setQueueId(mq.getQueueId());
                requestHeader.setSysFlag(sysFlag);
                requestHeader.setBornTimestamp(System.currentTimeMillis());
                requestHeader.setFlag(msg.getFlag());
                requestHeader.setProperties(MessageDecoder.messageProperties2String(msg.getProperties()));
                requestHeader.setReconsumeTimes(0);
                requestHeader.setUnitMode(this.isUnitMode());
                requestHeader.setBatch(msg instanceof MessageBatch);
                requestHeader.setBname(brokerName);
                ......
                SendResult sendResult = null;
                switch (communicationMode) {
                    case ASYNC:
                        Message tmpMessage = msg;
                        ......
                        sendResult = this.mQClientFactory.getMQClientAPIImpl().sendMessage(
                            brokerAddr,
                            brokerName,
                            tmpMessage,
                            requestHeader,
                            timeout - costTimeAsync,
                            communicationMode,
                            sendCallback,
                            topicPublishInfo,
                            this.mQClientFactory,
                            this.defaultMQProducer.getRetryTimesWhenSendAsyncFailed(),
                            context,
                            this);
                        break;
                    case ONEWAY:
                    case SYNC:
                        sendResult = this.mQClientFactory.getMQClientAPIImpl().sendMessage(
                            brokerAddr,
                            brokerName,
                            msg,
                            requestHeader,
                            timeout - costTimeSync,
                            communicationMode,
                            context,
                            this);
                        break;
                    default:
                        assert false;
                        break;
                }
                return sendResult;
            } 
            ......
        }
    }
MQClientAPIImpl
sendMessage
public SendResult sendMessage(
        final String addr,
        final String brokerName,
        final Message msg,
        final SendMessageRequestHeader requestHeader,
        final long timeoutMillis,
        final CommunicationMode communicationMode,
        final SendCallback sendCallback,
        final TopicPublishInfo topicPublishInfo,
        final MQClientInstance instance,
        final int retryTimesWhenSendFailed,
        final SendMessageContext context,
        final DefaultMQProducerImpl producer
    ) throws RemotingException, MQBrokerException, InterruptedException {
        long beginStartTime = System.currentTimeMillis();
        RemotingCommand request = null;
        String msgType = msg.getProperty(MessageConst.PROPERTY_MESSAGE_TYPE);
        boolean isReply = msgType != null && msgType.equals(MixAll.REPLY_MESSAGE_FLAG);
        if (isReply) {
            if (sendSmartMsg) {
                SendMessageRequestHeaderV2 requestHeaderV2 = SendMessageRequestHeaderV2.createSendMessageRequestHeaderV2(requestHeader);
                request = RemotingCommand.createRequestCommand(RequestCode.SEND_REPLY_MESSAGE_V2, requestHeaderV2);
            } else {
                request = RemotingCommand.createRequestCommand(RequestCode.SEND_REPLY_MESSAGE, requestHeader);
            }
        } else {
            if (sendSmartMsg || msg instanceof MessageBatch) {
                SendMessageRequestHeaderV2 requestHeaderV2 = SendMessageRequestHeaderV2.createSendMessageRequestHeaderV2(requestHeader);
                request = RemotingCommand.createRequestCommand(msg instanceof MessageBatch ? RequestCode.SEND_BATCH_MESSAGE : RequestCode.SEND_MESSAGE_V2, requestHeaderV2);
            } else {
                request = RemotingCommand.createRequestCommand(RequestCode.SEND_MESSAGE, requestHeader);
            }
        }
        request.setBody(msg.getBody());

        switch (communicationMode) {
            case ONEWAY:
                this.remotingClient.invokeOneway(addr, request, timeoutMillis);
                return null;
            case ASYNC:
                final AtomicInteger times = new AtomicInteger();
                long costTimeAsync = System.currentTimeMillis() - beginStartTime;
                if (timeoutMillis < costTimeAsync) {
                    throw new RemotingTooMuchRequestException("sendMessage call timeout");
                }
                this.sendMessageAsync(addr, brokerName, msg, timeoutMillis - costTimeAsync, request, sendCallback, topicPublishInfo, instance,
                    retryTimesWhenSendFailed, times, context, producer);
                return null;
            case SYNC:
                long costTimeSync = System.currentTimeMillis() - beginStartTime;
                if (timeoutMillis < costTimeSync) {
                    throw new RemotingTooMuchRequestException("sendMessage call timeout");
                }
                return this.sendMessageSync(addr, brokerName, msg, timeoutMillis - costTimeSync, request);
            default:
                assert false;
                break;
        }

        return null;
    }
sendMessageSync:同步消息发送
    private SendResult sendMessageSync(
        final String addr,
        final String brokerName,
        final Message msg,
        final long timeoutMillis,
        final RemotingCommand request
    ) throws RemotingException, MQBrokerException, InterruptedException {
        RemotingCommand response = this.remotingClient.invokeSync(addr, request, timeoutMillis);
        assert response != null;
        return this.processSendResponse(brokerName, msg, response, addr);
    }
sendMessageAsync:异步消息发送

将方法的参数封装到实现自InvokeCallback接口的类中,然后调用RemotingClient的invokeAsync方法,需要注意这里面调用的onExceptionImpl方法,通过这个方法实现了异步发送的失败重试

private void sendMessageAsync(
        final String addr,
        final String brokerName,
        final Message msg,
        final long timeoutMillis,
        final RemotingCommand request,
        final SendCallback sendCallback,
        final TopicPublishInfo topicPublishInfo,
        final MQClientInstance instance,
        final int retryTimesWhenSendFailed,
        final AtomicInteger times,
        final SendMessageContext context,
        final DefaultMQProducerImpl producer
    ) {
        final long beginStartTime = System.currentTimeMillis();
        try {
            this.remotingClient.invokeAsync(addr, request, timeoutMillis, new InvokeCallback() {
                @Override
                public void operationComplete(ResponseFuture responseFuture) {
                    long cost = System.currentTimeMillis() - beginStartTime;
                    RemotingCommand response = responseFuture.getResponseCommand();
                    if (null == sendCallback && response != null) {

                        try {
                            SendResult sendResult = MQClientAPIImpl.this.processSendResponse(brokerName, msg, response, addr);
                            if (context != null && sendResult != null) {
                                context.setSendResult(sendResult);
                                context.getProducer().executeSendMessageHookAfter(context);
                            }
                        } catch (Throwable e) {
                        }

                        producer.updateFaultItem(brokerName, System.currentTimeMillis() - responseFuture.getBeginTimestamp(), false);
                        return;
                    }

                    if (response != null) {
                        try {
                            SendResult sendResult = MQClientAPIImpl.this.processSendResponse(brokerName, msg, response, addr);
                            assert sendResult != null;
                            if (context != null) {
                                context.setSendResult(sendResult);
                                context.getProducer().executeSendMessageHookAfter(context);
                            }

                            try {
                                sendCallback.onSuccess(sendResult);
                            } catch (Throwable e) {
                            }

                            producer.updateFaultItem(brokerName, System.currentTimeMillis() - responseFuture.getBeginTimestamp(), false);
                        } catch (Exception e) {
                            producer.updateFaultItem(brokerName, System.currentTimeMillis() - responseFuture.getBeginTimestamp(), true);
                            onExceptionImpl(brokerName, msg, timeoutMillis - cost, request, sendCallback, topicPublishInfo, instance,
                                retryTimesWhenSendFailed, times, e, context, false, producer);
                        }
                    } else {
                        producer.updateFaultItem(brokerName, System.currentTimeMillis() - responseFuture.getBeginTimestamp(), true);
                        if (!responseFuture.isSendRequestOK()) {
                            MQClientException ex = new MQClientException("send request failed", responseFuture.getCause());
                            onExceptionImpl(brokerName, msg, timeoutMillis - cost, request, sendCallback, topicPublishInfo, instance,
                                retryTimesWhenSendFailed, times, ex, context, true, producer);
                        } else if (responseFuture.isTimeout()) {
                            MQClientException ex = new MQClientException("wait response timeout " + responseFuture.getTimeoutMillis() + "ms",
                                responseFuture.getCause());
                            onExceptionImpl(brokerName, msg, timeoutMillis - cost, request, sendCallback, topicPublishInfo, instance,
                                retryTimesWhenSendFailed, times, ex, context, true, producer);
                        } else {
                            MQClientException ex = new MQClientException("unknow reseaon", responseFuture.getCause());
                            onExceptionImpl(brokerName, msg, timeoutMillis - cost, request, sendCallback, topicPublishInfo, instance,
                                retryTimesWhenSendFailed, times, ex, context, true, producer);
                        }
                    }
                }
            });
        } catch (Exception ex) {
            long cost = System.currentTimeMillis() - beginStartTime;
            producer.updateFaultItem(brokerName, cost, true);
            onExceptionImpl(brokerName, msg, timeoutMillis - cost, request, sendCallback, topicPublishInfo, instance,
                retryTimesWhenSendFailed, times, ex, context, true, producer);
        }
    }
NettyRemotingClient
  • Semaphore semaphoreOneway:同步发送消息的信号量
  • Semaphore semaphoreAsync:异步发送消息的信号量
单向发送
invokeOneway

检查channel,调用invokeOnewayImpl方法发送消息

    public void invokeOneway(String addr, RemotingCommand request, long timeoutMillis) throws InterruptedException,
        RemotingConnectException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException {
        final Channel channel = this.getAndCreateChannel(addr);
        if (channel != null && channel.isActive()) {
            try {
                doBeforeRpcHooks(addr, request);
                this.invokeOnewayImpl(channel, request, timeoutMillis);
            }
        }
    }
invokeOnewayImpl:真正的单向发送消息的逻辑

获取同步发送的信号量,收到了channel的发送结果后就释放信号量

public void invokeOnewayImpl(final Channel channel, final RemotingCommand request, final long timeoutMillis)
        throws InterruptedException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException {
        request.markOnewayRPC();
        boolean acquired = this.semaphoreOneway.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS);
        if (acquired) {
            final SemaphoreReleaseOnlyOnce once = new SemaphoreReleaseOnlyOnce(this.semaphoreOneway);
            try {
                channel.writeAndFlush(request).addListener((ChannelFutureListener) f -> {
                    once.release();
                });
            } catch (Exception e) {
                once.release();
            }
        }
    }
同步发送
invokeSync

// 检查channel和超时,调用invokeSyncImpl方法发送

public RemotingCommand invokeSync(String addr, final RemotingCommand request, long timeoutMillis)
        throws InterruptedException, RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException {
        long beginStartTime = System.currentTimeMillis();
        final Channel channel = this.getAndCreateChannel(addr);
        if (channel != null && channel.isActive()) {
            try {
                doBeforeRpcHooks(addr, request);
                long costTime = System.currentTimeMillis() - beginStartTime;
                if (timeoutMillis < costTime) {
                    throw new RemotingTimeoutException("invokeSync call the addr[" + addr + "] timeout");
                }
                RemotingCommand response = this.invokeSyncImpl(channel, request, timeoutMillis - costTime);
                doAfterRpcHooks(RemotingHelper.parseChannelRemoteAddr(channel), request, response);
                this.updateChannelLastResponseTime(addr);
                return response;
            }
        }
    }
invokeSyncImpl:真正的同步发送方法

初始话ResponseFuture时,初始化了一个CountDownLatch,invokeSyncImpl在通过channel将消息发送给broker后,会调用ResponseFuture的waitResponse的方法,这个方法调用了CountDownLatch的await方法,ResponseFuture的putResponse调用了CountDownLatch的countDown方法,即invokeSyncImpl通过CountDownLatch实现了发送消息的同步。

public RemotingCommand invokeSyncImpl(final Channel channel, final RemotingCommand request,
        final long timeoutMillis)
        throws InterruptedException, RemotingSendRequestException, RemotingTimeoutException {
        //get the request id
        final int opaque = request.getOpaque();

        try {
        	// 同时生成了new CountDownLatch(1)
            final ResponseFuture responseFuture = new ResponseFuture(channel, opaque, timeoutMillis, null, null);
            this.responseTable.put(opaque, responseFuture);
            final SocketAddress addr = channel.remoteAddress();
            channel.writeAndFlush(request).addListener((ChannelFutureListener) f -> {
                if (f.isSuccess()) {
                    responseFuture.setSendRequestOK(true);
                    return;
                }

                responseFuture.setSendRequestOK(false);
                responseTable.remove(opaque);
                responseFuture.setCause(f.cause());
                responseFuture.putResponse(null);
                log.warn("Failed to write a request command to {}, caused by underlying I/O operation failure", addr);
            });
            // 等待responseFuture设置响应值和调用CountDownLatch的countDown方法
            RemotingCommand responseCommand = responseFuture.waitResponse(timeoutMillis);
            if (null == responseCommand) {
                if (responseFuture.isSendRequestOK()) {
                    throw new RemotingTimeoutException(RemotingHelper.parseSocketAddressAddr(addr), timeoutMillis,
                        responseFuture.getCause());
                } else {
                    throw new RemotingSendRequestException(RemotingHelper.parseSocketAddressAddr(addr), responseFuture.getCause());
                }
            }

            return responseCommand;
        } finally {
            this.responseTable.remove(opaque);
        }
    }
异步发送
invokeAsync

检查channel和是否超时,调用invokeAsyncImpl方法发送消息

    public void invokeAsync(String addr, RemotingCommand request, long timeoutMillis, InvokeCallback invokeCallback)
        throws InterruptedException, RemotingConnectException, RemotingTooMuchRequestException, RemotingTimeoutException,
        RemotingSendRequestException {
        long beginStartTime = System.currentTimeMillis();
        final Channel channel = this.getAndCreateChannel(addr);
        if (channel != null && channel.isActive()) {
            try {
                doBeforeRpcHooks(addr, request);
                long costTime = System.currentTimeMillis() - beginStartTime;
                if (timeoutMillis < costTime) {
                    throw new RemotingTooMuchRequestException("invokeAsync call the addr[" + addr + "] timeout");
                }
                this.invokeAsyncImpl(channel, request, timeoutMillis - costTime, new InvokeCallbackWrapper(invokeCallback, addr));
            }
        }
    }
invokeAsyncImpl:真正的消息异步发送方法

控制同时最多发送的任务的数量(包括正在发送和发送成功等待响应的消息),将消息包装成Future,然后放到ConcurrentHashMap中,如果消息发送成功,则在netty收到响应的地方把Future从ConcurrentHashMap移除并释放信号量,如果发送失败,则立即释放信号量并调用InvokeCallback的回调方法

    public void invokeAsyncImpl(final Channel channel, final RemotingCommand request, final long timeoutMillis,
        final InvokeCallback invokeCallback)
        throws InterruptedException, RemotingTooMuchRequestException, RemotingTimeoutException, RemotingSendRequestException {
        long beginStartTime = System.currentTimeMillis();
        final int opaque = request.getOpaque();
        // 获取信号量,控制同时处理的消息个数
        boolean acquired = this.semaphoreAsync.tryAcquire(timeoutMillis, TimeUnit.MILLISECONDS);
        if (acquired) {
        	// 对信号量的包装,里面只有一个释放信号量的方法,且通过cas控制只能释放一次
            final SemaphoreReleaseOnlyOnce once = new SemaphoreReleaseOnlyOnce(this.semaphoreAsync);
            long costTime = System.currentTimeMillis() - beginStartTime;
            if (timeoutMillis < costTime) {
                once.release();
                throw new RemotingTimeoutException("invokeAsyncImpl call timeout");
            }
            final ResponseFuture responseFuture = new ResponseFuture(channel, opaque, timeoutMillis - costTime, invokeCallback, once);
            this.responseTable.put(opaque, responseFuture);
            try {
                channel.writeAndFlush(request).addListener((ChannelFutureListener) f -> {
                    if (f.isSuccess()) {
                        responseFuture.setSendRequestOK(true);
                        return;
                    }
                    // 发送失败的处理,从responseTable中删除消息,调用future中包装的回调方法
                    requestFail(opaque);
                });
            } catch (Exception e) {
                responseFuture.release();
            }
        }
    }
发送的消息的响应处理
NettyClientHandler:Client的Netty消息处理类

NettyRomotingClient的内部类,channelRead0调用了NettyRomotingClient的processMessageReceived方法

    class NettyClientHandler extends SimpleChannelInboundHandler<RemotingCommand> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception {
            processMessageReceived(ctx, msg);
        }
    }
processMessageReceived:处理client收到的netty消息
    public void processMessageReceived(ChannelHandlerContext ctx, RemotingCommand msg) {
        if (msg != null) {
            switch (msg.getType()) {
                case REQUEST_COMMAND:
                    processRequestCommand(ctx, msg);
                    break;
                case RESPONSE_COMMAND:
                	// 处理响应的消息
                    processResponseCommand(ctx, msg);
                    break;
                default:
                    break;
            }
        }
    }
processResponseCommand:同步和异步消息的响应处理
    public void processResponseCommand(ChannelHandlerContext ctx, RemotingCommand cmd) {
        final int opaque = cmd.getOpaque();
        // 获取发送消息时保存的Future
        final ResponseFuture responseFuture = responseTable.get(opaque);
        if (responseFuture != null) {
            responseFuture.setResponseCommand(cmd);

            responseTable.remove(opaque);
            // 异步发送的消息的invokeCallback不为null,同步发送和单向发送的为null
            if (responseFuture.getInvokeCallback() != null) {
            	// 异步发送消息的回调处理,调用invokeCallback的回调处理方法
            	// 对responseFuture.executeInvokeCallback()的封装,
                executeInvokeCallback(responseFuture);
            } else {
            	// 同步发送消息的回调处理,设置responseFuture的响应值,然后调用responseFuture内部的countDownLatch的countDown()方法欢迎发送的线程
                responseFuture.putResponse(cmd);
                responseFuture.release();
            }
        } else {
            log.warn("receive response, but not matched any request, " + RemotingHelper.parseChannelRemoteAddr(ctx.channel()));
            log.warn(cmd.toString());
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值