All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gracerun.summermq.producer.RedisMessageProducer Maven / Gradle / Ivy

There is a newer version: 1.0.6
Show newest version
package com.gracerun.summermq.producer;

import com.alibaba.fastjson.JSON;
import com.gracerun.log.core.TraceRunnableWrapper;
import com.gracerun.summermq.bean.DelayRule;
import com.gracerun.summermq.bean.MessageBody;
import com.gracerun.summermq.constant.ServiceState;
import com.gracerun.summermq.exception.MQClientException;
import com.gracerun.summermq.factory.MQClientInstance;
import com.gracerun.summermq.service.ExecutorUtil;
import com.gracerun.summermq.service.QueueNameService;
import com.gracerun.summermq.util.ThreadUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 消息生产者
 *
 * @author Tom
 * @version 1.0.0
 * @date 12/26/21
 */
@Slf4j
public class RedisMessageProducer implements InitializingBean, DisposableBean {

    private ThreadPoolExecutor producerThread;

    @Getter
    @Setter
    private String producerNamespace;

    @Getter
    @Setter
    private int corePoolSize = 4;

    @Getter
    @Setter
    private int maximumPoolSize = 4;

    @Getter
    @Setter
    private long keepAliveTime = 60;

    @Getter
    @Setter
    private int blockingQueueSize = 2000;

    private StringRedisTemplate stringRedisTemplate;

    private MQClientInstance mqClientInstance;

    private ServiceState serviceState = ServiceState.CREATE_JUST;

    public RedisMessageProducer(String producerNamespace, StringRedisTemplate redisTemplate) {
        this.producerNamespace = producerNamespace;
        this.stringRedisTemplate = redisTemplate;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        start(true);
    }

    public void start(final boolean startFactory) throws MQClientException {
        switch (this.serviceState) {
            case CREATE_JUST:
                log.info("the producer start beginning");
                this.serviceState = ServiceState.START_FAILED;

                producerThread = ExecutorUtil.createExecutor("summer-producer-", corePoolSize, maximumPoolSize, keepAliveTime, blockingQueueSize, new ThreadPoolExecutor.CallerRunsPolicy());

                this.mqClientInstance = MQClientInstance.getInstance();

                boolean registerOK = mqClientInstance.registerProducer(producerNamespace, this);
                if (!registerOK) {
                    this.serviceState = ServiceState.CREATE_JUST;
                    throw new MQClientException("The producer namespace[" + producerNamespace + "] has been created before, specify another name please.", null);
                }
                if (startFactory) {
                    mqClientInstance.start();
                }
                this.serviceState = ServiceState.RUNNING;
                log.info("the producer start OK.");
                break;
            case RUNNING:
            case START_FAILED:
            case SHUTDOWN_ALREADY:
                throw new MQClientException("The producer service state not OK, maybe started once, "
                        + this.serviceState, null);
            default:
                break;
        }
    }

    @Override
    public void destroy() throws Exception {
        ThreadUtils.shutdownGracefully(this.producerThread, 1000, TimeUnit.MILLISECONDS);
    }

    public void asyncSend(List list) {
        if (!CollectionUtils.isEmpty(list)) {
            producerThread.submit(new TraceRunnableWrapper(() -> RedisMessageProducer.this.syncSend("", list), true));
        }
    }

    public void asyncSend(MessageBody messageBody) {
        asyncSend("", messageBody);
    }

    public void asyncSend(String beforeQueueName, MessageBody messageBody) {
        if (Objects.nonNull(messageBody)) {
            producerThread.submit(new TraceRunnableWrapper(() -> RedisMessageProducer.this.syncSend(beforeQueueName, messageBody), true));
        }
    }

    public void syncSend(String beforeQueueName, MessageBody messageBody) {
        if (Objects.nonNull(messageBody)) {
            syncSend(beforeQueueName, Arrays.asList(messageBody));
        }
    }

    public void syncSend(String beforeQueueName, List list) {
        try {
            stringRedisTemplate.executePipelined((RedisCallback) (connection) -> {
                list.forEach(m -> {
                    if (!StringUtils.hasText(m.getNamespace())) {
                        m.setNamespace(producerNamespace);
                    }
                    final String queueName = nextQueueName(m);
                    final String body = JSON.toJSONString(m);
                    try {
                        connection.lPush(stringRedisTemplate.getStringSerializer().serialize(queueName)
                                , stringRedisTemplate.getStringSerializer().serialize(body));
                        log.debug("send success msgId:{}, {}-->{}", m.getId(), beforeQueueName, queueName);
                    } catch (Exception e) {
                        log.error("send fail msgId:{}, {}-->{}", m.getId(), beforeQueueName, queueName);
                        log.error(e.getMessage(), e);
                    }
                });
                return null;
            });
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    private String nextQueueName(MessageBody messageBody) {
        int seconds = (int) ((messageBody.getNextExecuteTime().getTime() - System.currentTimeMillis()) / 1000);
        String queueName = QueueNameService.fmtTopicQueueName(messageBody.getNamespace(), messageBody.getBusinessType());
        if (seconds <= 0) {
            return queueName;
        } else if (seconds == 1) {
            return QueueNameService.fmtDelayQueueName(messageBody.getNamespace(), 0);
        }

        final int secondOfDay = new DateTime().getSecondOfDay();
        int nearestTime = seconds;
        int finalLevel = -1;

        if (seconds <= 120) {
            for (int i = 0; i <= 6; i++) {
                final int nextSeconds = DelayRule.DEFAULT_RULE.getSeconds(i) - secondOfDay % DelayRule.DEFAULT_RULE.getSeconds(i);
                if (nextSeconds == seconds) {
                    finalLevel = i;
                    break;
                }
                int n = seconds - nextSeconds;
                if (n < nearestTime && nextSeconds < seconds) {
                    nearestTime = n;
                    finalLevel = i;
                }
            }
        }

        if (finalLevel >= 0) {
            return QueueNameService.fmtDelayQueueName(messageBody.getNamespace(), finalLevel);
        }

        for (int i = DelayRule.DEFAULT_RULE.size() - 1; i >= 0; i--) {
            if (seconds >= DelayRule.DEFAULT_RULE.getSeconds(i)) {
                queueName = QueueNameService.fmtDelayQueueName(messageBody.getNamespace(), i);
                break;
            }
        }

        return queueName;
    }

}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy