Skip to content

Commit

Permalink
perf[event]:Optimize event exception handling and unsubscribed event …
Browse files Browse the repository at this point in the history
…handling
  • Loading branch information
weixiaoqiang committed Mar 23, 2024
1 parent 8ade37b commit b51fd4e
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 18 deletions.
13 changes: 13 additions & 0 deletions event/src/main/java/com/zfoo/event/EventExceptionContext.java
Original file line number Diff line number Diff line change
@@ -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) {
}
48 changes: 48 additions & 0 deletions event/src/main/java/com/zfoo/event/EventExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
19 changes: 19 additions & 0 deletions event/src/main/java/com/zfoo/event/anno/EventService.java
Original file line number Diff line number Diff line change
@@ -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 {
}
38 changes: 30 additions & 8 deletions event/src/main/java/com/zfoo/event/enhance/EnhanceUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,23 @@ public static IEventReceiver createEventReceiver(EventReceiverDefinition definit
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);

Expand All @@ -82,12 +91,25 @@ public static IEventReceiver createEventReceiver(EventReceiverDefinition definit
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@
import com.zfoo.event.anno.Bus;
import com.zfoo.event.model.IEvent;

import java.lang.reflect.Method;

/**
* @author godotg
*/
public interface IEventReceiver {
Bus bus();

void invoke(IEvent event);

Object getBean();

Method getMethod();
}
23 changes: 19 additions & 4 deletions event/src/main/java/com/zfoo/event/manager/EventBus.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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++) {
Expand Down Expand Up @@ -87,8 +93,19 @@ public Thread newThread(Runnable runnable) {
}
}

/**
* 设置异常处理器
*
* @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) {
Expand All @@ -97,6 +114,7 @@ public static void post(IEvent event) {
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) {
Expand All @@ -111,14 +129,11 @@ public static void post(IEvent event) {
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);
}
Expand Down
25 changes: 25 additions & 0 deletions event/src/main/java/com/zfoo/event/model/IPlayerEvent.java
Original file line number Diff line number Diff line change
@@ -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();
}

2 changes: 2 additions & 0 deletions event/src/test/java/com/zfoo/event/ApplicationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public void startEventTest() {
// see receiver method of MyController1 and MyController2
EventBus.post(MyNoticeEvent.valueOf("我的事件"));

EventBus.post(new UnhandledEvent());

ThreadUtils.sleep(1000);
}

Expand Down
5 changes: 3 additions & 2 deletions event/src/test/java/com/zfoo/event/MyController1.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -30,6 +30,7 @@ public class MyController1 {
@EventReceiver
public void onMyNoticeEvent(MyNoticeEvent event) {
logger.info("方法1同步执行事件:" + event.getMessage());
throw new NullPointerException();
}

}
4 changes: 2 additions & 2 deletions event/src/test/java/com/zfoo/event/MyController2.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@

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);
Expand Down
4 changes: 2 additions & 2 deletions event/src/test/java/com/zfoo/event/MyController3.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@

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);
Expand Down
6 changes: 6 additions & 0 deletions event/src/test/java/com/zfoo/event/UnhandledEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.zfoo.event;

import com.zfoo.event.model.IEvent;

public class UnhandledEvent implements IEvent {
}

0 comments on commit b51fd4e

Please sign in to comment.