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

org.redkalex.mq.kafka.KafkaMessageConsumer Maven / Gradle / Ivy

There is a newer version: 2.7.7
Show newest version
/*
 *
 */
package org.redkalex.mq.kafka;

import java.time.Duration;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.ByteArrayDeserializer;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.redkale.mq.MessageConsumer;
import org.redkale.mq.MessageEvent;
import org.redkale.mq.spi.MessageAgent.MessageConsumerWrapper;
import org.redkale.util.Utility;

/** @author zhangjx */
class KafkaMessageConsumer implements Runnable {

    private final ReentrantLock startCloseLock = new ReentrantLock();

    private final KafkaMessageAgent messageAgent;

    private final Logger logger = Logger.getLogger(this.getClass().getSimpleName());

    private CompletableFuture startFuture;

    private CompletableFuture closeFuture;

    private boolean autoCommit;

    private final String group;

    private final Map topicConsumerMap;

    private final List topics;

    private final MessageConsumerWrapper regexConsumerWrapper;

    private final String regexTopic;

    private KafkaConsumer consumer;

    private Thread thread;

    private boolean closed;

    protected KafkaMessageConsumer(
            KafkaMessageAgent messageAgent,
            String group,
            MessageConsumerWrapper regexConsumerWrapper,
            Map topicConsumerMap) {
        this.messageAgent = messageAgent;
        this.group = group;
        if (regexConsumerWrapper != null) {
            this.regexConsumerWrapper = regexConsumerWrapper;
            this.regexTopic = regexConsumerWrapper.getRegexTopic();
            this.topicConsumerMap = null;
            this.topics = List.of(this.regexTopic);
        } else {
            this.regexConsumerWrapper = null;
            this.regexTopic = null;
            this.topicConsumerMap = topicConsumerMap;
            this.topics = new ArrayList<>(topicConsumerMap.keySet());
        }
    }

    @Override
    public void run() {
        this.consumer = new KafkaConsumer<>(
                this.messageAgent.createConsumerProperties(this.group),
                new StringDeserializer(),
                new ByteArrayDeserializer());
        final boolean regex = Utility.isNotEmpty(this.regexTopic);
        if (regex) {
            this.consumer.subscribe(Pattern.compile(this.regexTopic));
        } else {
            this.consumer.subscribe(this.topics);
        }
        this.startFuture.complete(null);
        MessageConsumerWrapper regexConsumer = this.regexConsumerWrapper;
        try {
            // key: topic
            Map>> map = new LinkedHashMap<>();
            ConsumerRecords records;
            while (!this.closed) {
                try {
                    records = this.consumer.poll(Duration.ofMillis(Long.MAX_VALUE));
                } catch (Exception ex) {
                    if (!this.closed) {
                        logger.log(
                                Level.WARNING,
                                getClass().getSimpleName() + "(topics=" + this.topics + ") poll error",
                                ex);
                    }
                    break;
                }
                int count = records.count();
                if (count == 0) {
                    continue;
                }
                if (!this.autoCommit) {
                    long cs = System.currentTimeMillis();
                    this.consumer.commitSync();
                    long ce = System.currentTimeMillis() - cs;
                    if (ce > 100 && logger.isLoggable(Level.FINE)) {
                        logger.log(
                                Level.FINE,
                                getClass().getSimpleName() + "(topics=" + this.topics + ") processor async commit in "
                                        + ce + "ms");
                    }
                }
                map.clear();
                long s = System.currentTimeMillis();
                try {
                    if (regex) {
                        List events = new ArrayList<>();
                        for (ConsumerRecord r : records) {
                            events.add(new MessageEvent<>(r.topic(), r.partition(), r.key(), r.value()));
                        }
                        regexConsumer.onMessage(events);
                    } else {
                        for (ConsumerRecord r : records) {
                            map.computeIfAbsent(r.topic(), t -> new ArrayList<>())
                                    .add(new MessageEvent<>(r.topic(), r.partition(), r.key(), r.value()));
                        }
                        map.forEach((topic, events) -> {
                            MessageConsumerWrapper wrapper = topicConsumerMap.get(topic);
                            if (wrapper != null) {
                                wrapper.onMessage(events);
                            }
                        });
                    }
                } catch (Throwable e) {
                    logger.log(
                            Level.SEVERE,
                            getClass().getSimpleName() + "(topics=" + this.topics + ") process " + map + " error",
                            e);
                }
                long e = System.currentTimeMillis() - s;
                if (e > 1000 && logger.isLoggable(Level.FINE)) {
                    logger.log(
                            Level.FINE,
                            getClass().getSimpleName() + "(topics=" + this.topics + ").consumer (mq.count = " + count
                                    + ", mq.cost-slower = " + e + " ms), msgs=" + map);
                } else if (e > 100 && logger.isLoggable(Level.FINER)) {
                    logger.log(
                            Level.FINER,
                            getClass().getSimpleName() + "(topics=" + this.topics + ").consumer (mq.count = " + count
                                    + ", mq.cost-slowly = " + e + " ms), msgs=" + map);
                } else if (e > 10 && logger.isLoggable(Level.FINEST)) {
                    logger.log(
                            Level.FINEST,
                            getClass().getSimpleName() + "(topics=" + this.topics + ").consumer (mq.count = " + count
                                    + ", mq.cost-normal = " + e + " ms)");
                }
                map.clear();
            }
            if (this.consumer != null) {
                this.consumer.close();
            }
        } catch (Throwable t) {
            logger.log(
                    Level.SEVERE,
                    getClass().getSimpleName() + "(topics=" + this.topics + ", group=" + group + ") occur error",
                    t);
        } finally {
            if (this.closeFuture != null) {
                this.closeFuture.complete(null);
            }
        }
    }

    public void start() {
        startCloseLock.lock();
        try {
            if (this.startFuture != null) {
                this.startFuture.join();
                return;
            }
            this.thread = new Thread(this);
            this.startFuture = new CompletableFuture<>();
            this.thread.setName(MessageConsumer.class.getSimpleName() + "-[" + group + "]-Thread");
            logger.log(
                    Level.INFO,
                    getClass().getSimpleName() + "(topics=" + this.topics + ", group=" + group + ") starting");
            this.thread.start();
            this.startFuture.join();
            logger.log(
                    Level.INFO,
                    getClass().getSimpleName() + "(topics=" + this.topics + ", group=" + group + ") started");
        } finally {
            startCloseLock.unlock();
        }
    }

    public void stop() {
        startCloseLock.lock();
        try {
            if (this.closeFuture != null) {
                this.closeFuture.join();
                return;
            }
            if (this.consumer == null) {
                return;
            }
            if (this.closed) {
                return;
            }
            logger.log(
                    Level.INFO,
                    getClass().getSimpleName() + "(topics=" + this.topics + ", group=" + group + ") stoping");
            this.closeFuture = new CompletableFuture<>();
            this.closed = true;
            this.thread.interrupt();
            this.closeFuture.join();
            logger.log(
                    Level.INFO,
                    getClass().getSimpleName() + "(topics=" + this.topics + ", group=" + group + ") stoped");
        } finally {
            startCloseLock.unlock();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy