mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-05-24 21:36:49 +00:00
perf[event]:Optimize event exception handling and unsubscribed event handling
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package com.zfoo.event;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 订阅者抛出异常上下文
|
||||
*
|
||||
* @param event 事件对象
|
||||
* @param subscriber 订阅者
|
||||
* @param subscriberMethod 订阅方法
|
||||
*/
|
||||
public record EventExceptionContext(Object event, Object subscriber, Method subscriberMethod) {
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.zfoo.event;
|
||||
|
||||
import com.zfoo.event.manager.EventBus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* 处理事件订阅者抛出异常
|
||||
*
|
||||
* @author veione
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface EventExceptionHandler {
|
||||
/**
|
||||
* 处理订阅者抛出的异常
|
||||
*
|
||||
* @param exception 异常对象
|
||||
* @param context 异常上下文
|
||||
*/
|
||||
void handleException(Throwable exception, EventExceptionContext context);
|
||||
|
||||
final class LoggingEventExceptionHandler implements EventExceptionHandler {
|
||||
public static final LoggingEventExceptionHandler INSTANCE = new LoggingEventExceptionHandler();
|
||||
private final Logger logger = LoggerFactory.getLogger(EventBus.class.getName());
|
||||
|
||||
@Override
|
||||
public void handleException(Throwable exception, EventExceptionContext context) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error(message(context), exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static String message(EventExceptionContext context) {
|
||||
Method method = context.subscriberMethod();
|
||||
return "Exception thrown by subscriber method "
|
||||
+ method.getName()
|
||||
+ '('
|
||||
+ method.getParameterTypes()[0].getName()
|
||||
+ ')'
|
||||
+ " on subscriber "
|
||||
+ context.subscriber()
|
||||
+ " when dispatching event: "
|
||||
+ context.event();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.zfoo.event.anno;
|
||||
|
||||
import org.springframework.aot.hint.annotation.Reflective;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 用于标记处理事件接口
|
||||
*
|
||||
* @author veione
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Reflective
|
||||
@Service
|
||||
public @interface EventService {
|
||||
}
|
||||
@@ -57,14 +57,23 @@ public abstract class EnhanceUtils {
|
||||
CtClass enhanceClazz = classPool.makeClass(EnhanceUtils.class.getName() + StringUtils.capitalize(NamespaceHandler.EVENT) + UuidUtils.getLocalIntId());
|
||||
enhanceClazz.addInterface(classPool.get(IEventReceiver.class.getName()));
|
||||
|
||||
// 定义类中的一个成员
|
||||
CtField field = new CtField(classPool.get(bean.getClass().getName()), "bean", enhanceClazz);
|
||||
field.setModifiers(Modifier.PRIVATE);
|
||||
// 定义类中的一个成员bean
|
||||
CtClass beanClass = classPool.get(bean.getClass().getName());
|
||||
CtField field = new CtField(beanClass, "bean", enhanceClazz);
|
||||
field.setModifiers(Modifier.PRIVATE + Modifier.FINAL);
|
||||
enhanceClazz.addField(field);
|
||||
|
||||
// 定义类中的一个成员method
|
||||
CtClass methodClass = classPool.get(method.getClass().getName());
|
||||
CtField methodField = new CtField(methodClass, "method", enhanceClazz);
|
||||
methodField.setModifiers(Modifier.PRIVATE + Modifier.FINAL);
|
||||
enhanceClazz.addField(methodField);
|
||||
|
||||
// 定义类的构造器
|
||||
CtConstructor constructor = new CtConstructor(classPool.get(new String[]{bean.getClass().getName()}), enhanceClazz);
|
||||
constructor.setBody("{this.bean=$1;}");
|
||||
// 创建构造函数参数数组
|
||||
CtClass[] parameterTypes = {beanClass, methodClass};
|
||||
CtConstructor constructor = new CtConstructor(parameterTypes, enhanceClazz);
|
||||
constructor.setBody("{this.bean=$1; this.method=$2;}");
|
||||
constructor.setModifiers(Modifier.PUBLIC);
|
||||
enhanceClazz.addConstructor(constructor);
|
||||
|
||||
@@ -82,12 +91,25 @@ public abstract class EnhanceUtils {
|
||||
busMethod.setBody(busMethodBody);
|
||||
enhanceClazz.addMethod(busMethod);
|
||||
|
||||
// 定义类实现的接口方法getBean
|
||||
CtMethod beanMethod = new CtMethod(classPool.get(Object.class.getName()), "getBean", null, enhanceClazz);
|
||||
beanMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL);
|
||||
String beanMethodBody = "{ return this.bean; }";
|
||||
beanMethod.setBody(beanMethodBody);
|
||||
enhanceClazz.addMethod(beanMethod);
|
||||
|
||||
// 定义类实现的接口方法getMethod
|
||||
CtMethod getMethod = new CtMethod(classPool.get(Method.class.getName()), "getMethod", null, enhanceClazz);
|
||||
getMethod.setModifiers(Modifier.PUBLIC + Modifier.FINAL);
|
||||
String getMethodBody = "{ return this.method; }";
|
||||
getMethod.setBody(getMethodBody);
|
||||
enhanceClazz.addMethod(getMethod);
|
||||
|
||||
// 释放缓存
|
||||
enhanceClazz.detach();
|
||||
|
||||
Class<?> resultClazz = enhanceClazz.toClass(IEventReceiver.class);
|
||||
Constructor<?> resultConstructor = resultClazz.getConstructor(bean.getClass());
|
||||
IEventReceiver receiver = (IEventReceiver) resultConstructor.newInstance(bean);
|
||||
return receiver;
|
||||
Constructor<?> resultConstructor = resultClazz.getConstructor(bean.getClass(), method.getClass());
|
||||
return (IEventReceiver) resultConstructor.newInstance(bean, method);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ package com.zfoo.event.enhance;
|
||||
import com.zfoo.event.anno.Bus;
|
||||
import com.zfoo.event.model.IEvent;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author godotg
|
||||
*/
|
||||
@@ -22,4 +24,8 @@ public interface IEventReceiver {
|
||||
Bus bus();
|
||||
|
||||
void invoke(IEvent event);
|
||||
|
||||
Object getBean();
|
||||
|
||||
Method getMethod();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
*/
|
||||
package com.zfoo.event.manager;
|
||||
|
||||
import com.zfoo.event.EventExceptionContext;
|
||||
import com.zfoo.event.EventExceptionHandler;
|
||||
import com.zfoo.event.enhance.IEventReceiver;
|
||||
import com.zfoo.event.model.IEvent;
|
||||
import com.zfoo.protocol.collection.CollectionUtils;
|
||||
@@ -54,6 +56,10 @@ public abstract class EventBus {
|
||||
* event mapping
|
||||
*/
|
||||
private static final Map<Class<? extends IEvent>, List<IEventReceiver>> receiverMap = new HashMap<>();
|
||||
/**
|
||||
* event exception handler
|
||||
*/
|
||||
private static EventExceptionHandler exceptionHandler = EventExceptionHandler.LoggingEventExceptionHandler.INSTANCE;
|
||||
|
||||
static {
|
||||
for (int i = 0; i < executors.length; i++) {
|
||||
@@ -87,8 +93,19 @@ public abstract class EventBus {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置异常处理器
|
||||
*
|
||||
* @param handler 异常处理器
|
||||
*/
|
||||
public static void setExceptionHandler(EventExceptionHandler handler) {
|
||||
exceptionHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish the event
|
||||
*
|
||||
* @param event Event object
|
||||
*/
|
||||
public static void post(IEvent event) {
|
||||
if (event == null) {
|
||||
@@ -97,6 +114,7 @@ public abstract class EventBus {
|
||||
var clazz = event.getClass();
|
||||
var receivers = receiverMap.get(clazz);
|
||||
if (CollectionUtils.isEmpty(receivers)) {
|
||||
logger.warn("This event [" + clazz.getName() + "] has not registered any valid event handler");
|
||||
return;
|
||||
}
|
||||
for (var receiver : receivers) {
|
||||
@@ -111,14 +129,11 @@ public abstract class EventBus {
|
||||
private static void doReceiver(IEventReceiver receiver, IEvent event) {
|
||||
try {
|
||||
receiver.invoke(event);
|
||||
} catch (Exception e) {
|
||||
logger.error("eventBus {} [{}] unknown exception", receiver.bus(), event.getClass().getSimpleName(), e);
|
||||
} catch (Throwable t) {
|
||||
logger.error("eventBus {} [{}] unknown error", receiver.bus(), event.getClass().getSimpleName(), t);
|
||||
exceptionHandler.handleException(t.getCause(), new EventExceptionContext(event, receiver.getBean(), receiver.getMethod()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void asyncExecute(Runnable runnable) {
|
||||
execute(RandomUtils.randomInt(), runnable);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.zfoo.event.model;
|
||||
|
||||
/**
|
||||
* 玩家事件接口
|
||||
* <p>
|
||||
* 主要用于让同一个玩家ID在同一个线程里面进行事件的业务处理,防止玩家事件在不同的线程中处理造成的玩家数据冲突或者线程竞争导致的性能下降.
|
||||
* </p>
|
||||
*
|
||||
* @author veione
|
||||
*/
|
||||
public interface IPlayerEvent extends IEvent {
|
||||
|
||||
@Override
|
||||
default int executorHash() {
|
||||
return (int) executePlayerId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行玩家ID
|
||||
*
|
||||
* @return 玩家ID
|
||||
*/
|
||||
long executePlayerId();
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ public class ApplicationTest {
|
||||
// see receiver method of MyController1 and MyController2
|
||||
EventBus.post(MyNoticeEvent.valueOf("我的事件"));
|
||||
|
||||
EventBus.post(new UnhandledEvent());
|
||||
|
||||
ThreadUtils.sleep(1000);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
package com.zfoo.event;
|
||||
|
||||
import com.zfoo.event.anno.EventReceiver;
|
||||
import com.zfoo.event.anno.EventService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author godotg
|
||||
*/
|
||||
@Component
|
||||
@EventService
|
||||
public class MyController1 {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MyController1.class);
|
||||
@@ -30,6 +30,7 @@ public class MyController1 {
|
||||
@EventReceiver
|
||||
public void onMyNoticeEvent(MyNoticeEvent event) {
|
||||
logger.info("方法1同步执行事件:" + event.getMessage());
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,14 +15,14 @@ package com.zfoo.event;
|
||||
|
||||
import com.zfoo.event.anno.Bus;
|
||||
import com.zfoo.event.anno.EventReceiver;
|
||||
import com.zfoo.event.anno.EventService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author godotg
|
||||
*/
|
||||
@Component
|
||||
@EventService
|
||||
public class MyController2 {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MyController2.class);
|
||||
|
||||
@@ -15,14 +15,14 @@ package com.zfoo.event;
|
||||
|
||||
import com.zfoo.event.anno.Bus;
|
||||
import com.zfoo.event.anno.EventReceiver;
|
||||
import com.zfoo.event.anno.EventService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author godotg
|
||||
*/
|
||||
@Component
|
||||
@EventService
|
||||
public class MyController3 {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(MyController3.class);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.zfoo.event;
|
||||
|
||||
import com.zfoo.event.model.IEvent;
|
||||
|
||||
public class UnhandledEvent implements IEvent {
|
||||
}
|
||||
Reference in New Issue
Block a user