public <T extends Message> SendResult syncSendOrderly(String destination, Collection<T> messages, String hashKey, long timeout) {
        if (Objects.isNull(messages) || messages.isEmpty()) {
            log.error("syncSendOrderly failed. destination:{}, message is null ", destination);
            throw new IllegalArgumentException("`messages` can not be empty");
        }
        try {
            long now = System.currentTimeMillis();
            Collection<org.apache.rocketmq.common.message.Message> rmqMsgs = new ArrayList<>();
            for (T message : messages) {
                if (Objects.isNull(message)) {
                    continue;
                }
                rmqMsgs.add(this.createRocketMqMessage(destination, message));
            }
            MessageBatch messageBatch = batch(rmqMsgs);
            SendResult sendResult = producer.send(messageBatch, this.messageQueueSelector, hashKey, timeout);
            long costTime = System.currentTimeMillis() - now;
            if (log.isDebugEnabled()) {
                log.debug("send message cost: {} ms, msgId:{}", costTime, sendResult.getMsgId());
            }
            return sendResult;
        } catch (Exception e) {
            throw new MessagingException(e.getMessage(), e);
        }
    }
Same to {@link #syncSendOrderly(String, Collection, String)} with send timeout specified in addition.

@param destination formats: `topicName:tags`
@param messages    Collection of {@link org.springframework.messaging.Message}
@param hashKey     use this key to select queue. for example: orderId, productId ...
@param timeout     send timeout with millis
@return {@link SendResult}
rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/core/RocketMQTemplate.java