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

io.github.rhwayfun.springboot.rocketmq.starter.common.AbstractRocketMqConsumer Maven / Gradle / Ivy

package io.github.rhwayfun.springboot.rocketmq.starter.common;

import com.alibaba.fastjson.JSON;
import io.github.rhwayfun.springboot.rocketmq.starter.constants.ConsumeMode;
import io.github.rhwayfun.springboot.rocketmq.starter.constants.RocketMqContent;
import io.github.rhwayfun.springboot.rocketmq.starter.constants.RocketMqTopic;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.*;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Abstract message listener.
 * For consume message, you should inherit this class to implement three methods.
 * @author rhwayfun
 * @since 0.0.1
 */
public abstract class AbstractRocketMqConsumer {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    protected Class topicClazz;

    protected Class contentClazz;

    /**
     * is started.
     *
     */
    private boolean isStarted;

    /**
     * min consume thread.
     *
     */
    private Integer consumeThreadMin;

    /**
     * max consume thread.
     *
     */
    private Integer consumeThreadMax;

    /**
     * consume from where, default is
     * @see ConsumeFromWhere
     * @value ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET
     *
     */
    private ConsumeFromWhere consumeFromWhere = ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET;

    /**
     * Message consume retry strategy
* -1, no retry,put into DLQ directly
* 0, broker control retry frequency
* >0, client control retry frequency * */ private int delayLevelWhenNextConsume = 0; private long suspendCurrentQueueTimeMillis = -1; /** * consume message mode, default is CLUSTERING * @see MessageModel * @value MessageModel.CLUSTERING * */ private MessageModel messageModel = MessageModel.CLUSTERING; /** * consume mode, default is CONCURRENTLY * @see ConsumeMode * @value ConsumeMode.CONCURRENTLY * */ private ConsumeMode consumeMode = ConsumeMode.CONCURRENTLY; /** * consumer holder. * */ private DefaultMQPushConsumer consumer; /** * subscribeTopicTags for specific consumer. * * @return subscribeTopicTags */ public abstract Map> subscribeTopicTags(); /** * specific consumer group. * * @return consumer group */ public abstract String getConsumerGroup(); /** * consumer msg. * * @param content cntent * @param msg msg * @return consume result */ public abstract boolean consumeMsg(Content content, MessageExt msg); @PostConstruct @SuppressWarnings("unchecked") public void init() throws MQClientException { Class parentClazz = this.getClass(); // 得到泛型父类 Type genType = parentClazz.getGenericSuperclass(); // 一个泛型类可能有多个泛型形参,比如ClassName 这里有两个泛型形参T和K,Class Name 这里只有1个泛型形参T Type[] types = ((ParameterizedType) genType).getActualTypeArguments(); topicClazz = (Class) types[0]; contentClazz = (Class) types[1]; if (this.isStarted()) { throw new IllegalStateException("container already started. " + this.toString()); } initRocketMQPushConsumer(); } @PreDestroy public void destroy() throws Exception { if (Objects.nonNull(consumer)) { consumer.shutdown(); } logger.info("consumer shutdown, {}", this.toString()); } public class DefaultMessageListenerConcurrently implements MessageListenerConcurrently { @Override @SuppressWarnings("unchecked") public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) { for (MessageExt messageExt : msgs) { try { long now = System.currentTimeMillis(); consumeMsg(parseMsg(messageExt.getBody(), contentClazz), messageExt); long costTime = System.currentTimeMillis() - now; logger.info("consume {} cost: {} ms", messageExt.getMsgId(), costTime); } catch (Exception e) { logger.warn("consume message failed. messageExt:{}", messageExt, e); context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume); return ConsumeConcurrentlyStatus.RECONSUME_LATER; } } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } } public class DefaultMessageListenerOrderly implements MessageListenerOrderly { @Override @SuppressWarnings("unchecked") public ConsumeOrderlyStatus consumeMessage(List msgs, ConsumeOrderlyContext context) { for (MessageExt messageExt : msgs) { try { long now = System.currentTimeMillis(); consumeMsg(parseMsg(messageExt.getBody(), contentClazz), messageExt); long costTime = System.currentTimeMillis() - now; logger.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime); } catch (Exception e) { logger.warn("consume message failed. messageExt:{}", messageExt, e); context.setSuspendCurrentQueueTimeMillis(suspendCurrentQueueTimeMillis); return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT; } } return ConsumeOrderlyStatus.SUCCESS; } } private void initRocketMQPushConsumer() throws MQClientException { Assert.notNull(getConsumerGroup(), "Property 'consumerGroup' is required"); Assert.notEmpty(subscribeTopicTags(), "subscribeTopicTags method can't be empty"); consumer = new DefaultMQPushConsumer(getConsumerGroup()); if (consumeThreadMax != null) { consumer.setConsumeThreadMax(consumeThreadMax); } if (consumeThreadMax != null && consumeThreadMax < consumer.getConsumeThreadMin()) { consumer.setConsumeThreadMin(consumeThreadMax); } consumer.setConsumeFromWhere(consumeFromWhere); consumer.setMessageModel(messageModel); switch (consumeMode) { case Orderly: consumer.setMessageListener(new DefaultMessageListenerOrderly()); break; case CONCURRENTLY: consumer.setMessageListener(new DefaultMessageListenerConcurrently()); break; default: throw new IllegalArgumentException("Property 'consumeMode' was wrong."); } } private T parseMsg(byte[] body, Class clazz){ T t = null; if (body != null) { try { t = JSON.parseObject(body, clazz); } catch (Exception e) { logger.error("can not parse to object", e); } } return t; } public Integer getConsumeThreadMin() { return consumeThreadMin; } public void setConsumeThreadMin(Integer consumeThreadMin) { this.consumeThreadMin = consumeThreadMin; } public Integer getConsumeThreadMax() { return consumeThreadMax; } public void setConsumeThreadMax(Integer consumeThreadMax) { this.consumeThreadMax = consumeThreadMax; } public boolean isStarted() { return isStarted; } public void setStarted(boolean started) { isStarted = started; } public ConsumeFromWhere getConsumeFromWhere() { return consumeFromWhere; } public void setConsumeFromWhere(ConsumeFromWhere consumeFromWhere) { this.consumeFromWhere = consumeFromWhere; } public int getDelayLevelWhenNextConsume() { return delayLevelWhenNextConsume; } public void setDelayLevelWhenNextConsume(int delayLevelWhenNextConsume) { this.delayLevelWhenNextConsume = delayLevelWhenNextConsume; } public long getSuspendCurrentQueueTimeMillis() { return suspendCurrentQueueTimeMillis; } public void setSuspendCurrentQueueTimeMillis(long suspendCurrentQueueTimeMillis) { this.suspendCurrentQueueTimeMillis = suspendCurrentQueueTimeMillis; } public MessageModel getMessageModel() { return messageModel; } public void setMessageModel(MessageModel messageModel) { this.messageModel = messageModel; } public ConsumeMode getConsumeMode() { return consumeMode; } public void setConsumeMode(ConsumeMode consumeMode) { this.consumeMode = consumeMode; } public DefaultMQPushConsumer getConsumer() { return consumer; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy