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

org.nofdev.topic.AbstractTopicConsumerHandle.groovy Maven / Gradle / Ivy

There is a newer version: 1.7.6
Show newest version
package org.nofdev.topic

import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.ObjectMapper
import groovy.transform.CompileStatic
import org.nofdev.logging.CustomLogger
import org.springframework.aop.support.AopUtils

import java.lang.reflect.Method
import java.lang.reflect.Type
import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import java.util.concurrent.ThreadPoolExecutor

/**
 * Created by Liutengfei on 2017/5/3 0003.
 */
@CompileStatic
abstract class AbstractTopicConsumerHandle {
    private static final CustomLogger logger = CustomLogger.getLogger(AbstractTopicConsumerHandle.class)

    public void registConsumer(Object obj) {
        registConsumer(obj, null)
    }

    public void registConsumer(Object obj, TopicErrorHandler topicErrorHandler) {
        Class targetInterface = AopUtils.getTargetClass(obj).interfaces[0]
        String interfaceClazz = targetInterface.name
        Method[] methods = targetInterface.getDeclaredMethods();
        for (Method method : methods) {
            Method implMethod = obj.getClass().getMethod(method.getName(), method.getParameterTypes())
            TopicConsumer topicConsumerAnnotation = implMethod.getAnnotation(TopicConsumer.class)
            if (topicConsumerAnnotation?.ignore()) {
                continue
            }
            TopicConsumerSetting topicConsumerSetting = new TopicConsumerSetting()
            topicConsumerSetting.topicName=interfaceClazz.replaceAll("\\.", "-") + "-" + method.getName()
            topicConsumerSetting.nThreads = topicConsumerAnnotation ? topicConsumerAnnotation.nThreads() : 5
            topicConsumerSetting.queueSize = topicConsumerAnnotation ? topicConsumerAnnotation.queueSize() : 5
            topicConsumerSetting.maxRetries = topicConsumerAnnotation ? topicConsumerAnnotation.maxRetries() : 16

            if (topicConsumerSetting.nThreads <= 0) {throw new TopicException(" @TopicConsumer.nThreads 必须大于0")}
            if (topicConsumerSetting.queueSize <= 0) {throw new TopicException(" @TopicConsumer.queueSize 必须大于0")}
            if (topicConsumerSetting.maxRetries <= 0) {throw new TopicException(" @TopicConsumer.maxRetries 必须大于0")}

            //注意这里必须传入 method 而不能传入 implMethod 否则会造成参数列表中的泛型丢失
            asyncListen(obj, method, topicConsumerSetting, topicErrorHandler)
        }
    }

    private void asyncListen(obj, Method method, TopicConsumerSetting topicConsumerSetting,TopicErrorHandler topicErrorHandler) {
        Executor executor
        Thread thread = new Thread({
            try {
                executor = Executors.newFixedThreadPool(topicConsumerSetting.nThreads) as ThreadPoolExecutor
                pollMsg(method, obj, executor, topicConsumerSetting, topicErrorHandler)
            } catch (Exception e) {
                executor?.shutdown()
                logger.error("message listener thread handling exception", e)
                throw e
            }
        })
        thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                if (!e instanceof TopicException) {
                    asyncListen(obj, method,topicConsumerSetting, topicErrorHandler)
                }
            }
        })
        thread.start()
    }

    abstract void pollMsg(Method method, Object obj, ThreadPoolExecutor executor, TopicConsumerSetting topicConsumerSetting, TopicErrorHandler topicErrorHandler)

    /**
     *  TODO 处理 header
     */
    protected CompletableFuture onSubmitAsync(Object obj, TopicMessage topicMessage, Method method, Executor executor) {
        new CompletableFuture().runAsync({
            method.invoke(obj, (topicMessage?.payload) as Object[])
        }, executor)
    }

    protected TopicMessage getResult(Method method, String result) throws Throwable {
        ObjectMapper objectMapper = ObjectMapperFactory.createObjectMapper()
        JsonNode jsonNode = objectMapper.readTree(result)
        TopicMessage topicMessage = new TopicMessage()
        topicMessage.setHeaders(objectMapper.treeToValue(jsonNode.get('headers'), Map))
        def payload = []
        method.genericParameterTypes?.eachWithIndex() { Type it, Integer index ->
            JavaType javaType = objectMapper.getTypeFactory().constructType(it)
            payload.add(objectMapper.convertValue(jsonNode.get('payload').get(index), javaType))
        }
        topicMessage.setPayload(payload.toArray())
        topicMessage
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy