perf[event]:Optimize event exception handling and unsubscribed event handling

This commit is contained in:
weixiaoqiang
2024-03-23 10:13:57 +08:00
parent 8ade37bbe3
commit b51fd4e0ed
12 changed files with 175 additions and 18 deletions
@@ -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 {
}