org.nofdev.topic.AbstractTopicConsumerHandle.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of service-topic-consumer Show documentation
Show all versions of service-topic-consumer Show documentation
The basic componet of Nofdev Topic framework
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
}
}