eventhandler java_简单的Java Event-事件框架

自己写的一个简单的Java事件框架。目前具备以下功能:

1.通过继承Event类,用户可自定义事件。

2.通过EventService 的fireEvent(Event e) 发出一个事件。

3.通过实现IEventHandler接口,监听某类事件。EventHandler可以动态注册到EventServer,也可以在配置文件中配置。

4.通过实现IEventConsumedCallback接口,在事件被处理后,通知事件发出者。IEventConsumedCallback可在fireEvent时指定,也可不指定。

5.fireEvent  和 Consume Event都是异步进行,Consume Event时采用线程池处理。

类图如下:

1b22372258aee44b894c44272d533bf7.png

测试:

1.自定义Event

import com.lenovo.commonevent.Event;

/**

* Project: CommonEvent

* FileName: TestEvent.java

* @Description: TODO

* @author: jmpp

* Createdate: 2015年3月16日 下午5:57:12

* Copyright: Copyright(C) 2014-2015

* Company Lenovo LTD.

* All rights Reserved, Designed By Lenovo CIC.

*/

/**

* 类 TestEvent 的实现描述:TODO 类实现描述

*

* @author jmpp 2015年3月16日下午5:57:12

*/

public class TestEvent extends Event {

public TestEvent() {

super(TestEvent.class.getSimpleName());

}

}

2.定义一个EventHandler

import java.util.Date;

import com.lenovo.commonevent.Event;

import com.lenovo.commonevent.IEventHandler;

/**

* Project: CommonEvent

* FileName: TestEventHandler.java

* @Description: TODO

* @author: jmpp

* @version V1.0

* Createdate: 2015年3月16日 下午6:00:34

* Copyright: Copyright(C) 2014-2015

* Company Lenovo LTD.

* All rights Reserved, Designed By Lenovo CIC.

*/

/**

* 类 TestEventHandler 的实现描述:TODO 类实现描述

*

* @author jmpp 2015年3月16日下午6:00:34

*/

public class TestEventHandler implements IEventHandler {

/**

* @author jmpp 2015年3月16日下午6:00:48

*/

@Override

public Object onEvent(Event event) {

System.out.println("On event " + event.getId() + " Type:" + event.getType());

return new Date();

}

}

3.定义EventConsumedCallback

import java.util.Date;

import com.lenovo.commonevent.Event;

import com.lenovo.commonevent.EventService;

import com.lenovo.commonevent.IEventConsumedCallback;

/**

* Project: CommonEvent

* FileName: TestEventInvoker.java

* @Description: TODO

* @author: jmpp

* @version V1.0

* Createdate: 2015年3月16日 下午6:03:47

* Copyright: Copyright(C) 2014-2015

* Company Lenovo LTD.

* All rights Reserved, Designed By Lenovo CIC.

*/

/**

* 类 TestEventInvoker 的实现描述:TODO 类实现描述

*

* @author jmpp 2015年3月16日下午6:03:47

*/

public class TestEventInvoker implements IEventConsumedCallback {

/**

* @author jmpp 2015年3月16日下午6:04:02

*/

@SuppressWarnings("deprecation")

@Override

public void onEventFinished(Event event, Object result) {

System.out.println("Event callback " + event.getId() + " at "

+ ((Date) result).toLocaleString());

}

}

4.测试调用  触发事件-〉处理事件(Handle) ->回调Callbackimport java.util.Date;

import com.lenovo.commonevent.Event;

import com.lenovo.commonevent.EventService;

import com.lenovo.commonevent.IEventConsumedCallback;

/**

* Project: CommonEvent

* FileName: TestEventInvoker2.java

* @Description: TODO

* @author: jmpp

* @version V1.0

* Createdate: 2015年3月16日 下午6:03:47

* Copyright: Copyright(C) 2014-2015

* Company Lenovo LTD.

* All rights Reserved, Designed By Lenovo CIC.

*/

/**

* 类 TestEventInvoker2 的实现描述:TODO 类实现描述

*

* @author jmpp 2015年3月16日下午6:03:47

*/

public class TestEventInvoker2 {

public static void main(String args[]) throws Exception {

EventService.init(null);

EventService.registerEventHandler(TestEvent.class.getSimpleName(), new TestEventHandler());

for (int i = 0; i < 10; i++) {

TestEvent event = new TestEvent();

EventService.fireEvent(event, new TestEventInvoker());

}

Thread.sleep(5000);

EventService.stop();

}

}

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class}) public class KafkaDemoApp implements CommandLineRunner { @Autowired private KafkaEventCenter eventCenter; @Autowired private DeadLetterConfig deadLetterConfig; # 自动注入死信配置 public static void main(String[] args) { SpringApplication.run(KafkaDemoApp.class, args); } @Override public void run(String... args) throws Exception { // 注册带死信的消费者(模拟消费失败) registerDLQConsumer(); // 注册死信队列的监听(验证死信是否发送成功) registerDLQListener(); // 发送测试消息(触发消费失败) sendHelloMessage(); } /** * 注册带死信的消费者(模拟消费失败) */ private void registerDLQConsumer() { // 定义一个会抛出异常的事件处理器(模拟消费失败) EventHandler failingHandler = event -> { System.out.println("尝试处理消息: " + event.getMessage()); throw new RuntimeException("模拟业务处理失败"); // 主动抛异常 }; // 调用带死信的registerUnicast方法 eventCenter.registerUnicast( "hello-topic", // 主Topic "demo-group", // 消费者组Id failingHandler, // 会失败的处理器 ForkJoinPool.commonPool(), // 线程池 PartitionAssignorMode.COOPERATIVE_STICKY, // 分区分配策略 deadLetterConfig // 死信配置(自动注入) ); } /** * 注册死信队列的监听(验证死信是否发送成功) */ private void registerDLQListener() { eventCenter.registerUnicast( deadLetterConfig.getDlqTopic(), // 死信Topic "dlq-demo-group", // 消费者组Id(对应配置中的dlq-consumer.group-id) event -> System.out.println("收到死信消息: " + event.getMessage()), // 死信处理器 ForkJoinPool.commonPool() // 线程池 ); } /** * 发送测试消息(触发消费流程) */ private void sendHelloMessage() { Event event = new Event("key1", "Hello Kafka! 这是一条会触发死信的消息"); eventCenter.send("hello-topic", event); // 发送至主Topic } } 2025-09-23 15:53:09.256 INFO 25232 --- [ main] o.a.kafka.common.utils.AppInfoParser : Kafka version: 2.8.0 2025-09-23 15:53:09.256 INFO 25232 --- [ main] o.a.kafka.common.utils.AppInfoParser : Kafka commitId: ebb1d6e21cc92130 2025-09-23 15:53:09.256 INFO 25232 --- [ main] o.a.kafka.common.utils.AppInfoParser : Kafka startTimeMs: 1758613989255 2025-09-23 15:53:09.267 INFO 25232 --- [opic_demo-group] o.a.kafka.common.utils.AppInfoParser : Kafka version: 2.8.0 2025-09-23 15:53:09.267 INFO 25232 --- [opic_demo-group] o.a.kafka.common.utils.AppInfoParser : Kafka commitId: ebb1d6e21cc92130 2025-09-23 15:53:09.267 INFO 25232 --- [opic_demo-group] o.a.kafka.common.utils.AppInfoParser : Kafka startTimeMs: 1758613989267 2025-09-23 15:53:09.267 INFO 25232 --- [opic_demo-group] c.t.s.e.p.k.consumer.KafkaConsumerTask : start to consumer kafka topic: hello-topic 2025-09-23 15:53:09.267 INFO 25232 --- [opic_demo-group] c.t.s.e.p.k.c.AbstractTaskService : KafkaConsumerTask is running! topic:hello-topic 2025-09-23 15:53:09.267 INFO 25232 --- [_dlq-demo-group] o.a.kafka.common.utils.AppInfoParser : Kafka version: 2.8.0 2025-09-23 15:53:09.267 INFO 25232 --- [_dlq-demo-group] o.a.kafka.common.utils.AppInfoParser : Kafka commitId: ebb1d6e21cc92130 2025-09-23 15:53:09.267 INFO 25232 --- [_dlq-demo-group] o.a.kafka.common.utils.AppInfoParser : Kafka startTimeMs: 1758613989267 2025-09-23 15:53:09.267 INFO 25232 --- [_dlq-demo-group] c.t.s.e.p.k.consumer.KafkaConsumerTask : start to consumer kafka topic: vms_dlq_hello-topic 2025-09-23 15:53:09.267 INFO 25232 --- [_dlq-demo-group] c.t.s.e.p.k.c.AbstractTaskService : KafkaConsumerTask is running! topic:vms_dlq_hello-topic 2025-09-23 15:53:09.268 INFO 25232 --- [_dlq-demo-group] o.a.k.clients.consumer.KafkaConsumer : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] Subscribed to topic(s): vms_dlq_hello-topic 2025-09-23 15:53:09.268 INFO 25232 --- [opic_demo-group] o.a.k.clients.consumer.KafkaConsumer : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] Subscribed to topic(s): hello-topic 2025-09-23 15:53:09.427 INFO 25232 --- [_dlq-demo-group] org.apache.kafka.clients.Metadata : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] Cluster ID: YjO1tbcTTlK2KxmL_Vu2yw 2025-09-23 15:53:09.427 INFO 25232 --- [ad | producer-1] org.apache.kafka.clients.Metadata : [Producer clientId=producer-1] Cluster ID: YjO1tbcTTlK2KxmL_Vu2yw 2025-09-23 15:53:09.427 INFO 25232 --- [opic_demo-group] org.apache.kafka.clients.Metadata : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] Cluster ID: YjO1tbcTTlK2KxmL_Vu2yw 2025-09-23 15:53:09.428 INFO 25232 --- [opic_demo-group] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] Discovered group coordinator admin1-virtual-machine:9092 (id: 2147483647 rack: null) 2025-09-23 15:53:09.428 INFO 25232 --- [_dlq-demo-group] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] Discovered group coordinator admin1-virtual-machine:9092 (id: 2147483647 rack: null) 2025-09-23 15:53:09.853 INFO 25232 --- [_dlq-demo-group] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] (Re-)joining group 2025-09-23 15:53:09.853 INFO 25232 --- [opic_demo-group] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] (Re-)joining group 2025-09-23 15:53:09.864 INFO 25232 --- [_dlq-demo-group] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] (Re-)joining group 2025-09-23 15:53:09.864 INFO 25232 --- [opic_demo-group] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] (Re-)joining group 2025-09-23 15:53:09.866 INFO 25232 --- [_dlq-demo-group] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] Successfully joined group with generation Generation{generationId=5, memberId='consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36-1e8889d7-12e5-4c3b-82b5-c0e53161962e', protocol='cooperative-sticky'} 2025-09-23 15:53:09.867 INFO 25232 --- [opic_demo-group] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] Successfully joined group with generation Generation{generationId=21, memberId='consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58-79cbe46a-3964-4605-8135-a1cedac9ee71', protocol='cooperative-sticky'} 2025-09-23 15:53:09.868 INFO 25232 --- [opic_demo-group] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] Finished assignment for group at generation 21: {consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58-79cbe46a-3964-4605-8135-a1cedac9ee71=Assignment(partitions=[hello-topic-0])} 2025-09-23 15:53:09.868 INFO 25232 --- [_dlq-demo-group] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] Finished assignment for group at generation 5: {consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36-1e8889d7-12e5-4c3b-82b5-c0e53161962e=Assignment(partitions=[vms_dlq_hello-topic-0])} 2025-09-23 15:53:09.871 INFO 25232 --- [opic_demo-group] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] Successfully synced group in generation Generation{generationId=21, memberId='consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58-79cbe46a-3964-4605-8135-a1cedac9ee71', protocol='cooperative-sticky'} 2025-09-23 15:53:09.871 INFO 25232 --- [_dlq-demo-group] o.a.k.c.c.internals.AbstractCoordinator : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] Successfully synced group in generation Generation{generationId=5, memberId='consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36-1e8889d7-12e5-4c3b-82b5-c0e53161962e', protocol='cooperative-sticky'} 2025-09-23 15:53:09.871 INFO 25232 --- [opic_demo-group] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] Updating assignment with Assigned partitions: [hello-topic-0] Current owned partitions: [] Added partitions (assigned - owned): [hello-topic-0] Revoked partitions (owned - assigned): [] 2025-09-23 15:53:09.871 INFO 25232 --- [_dlq-demo-group] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] Updating assignment with Assigned partitions: [vms_dlq_hello-topic-0] Current owned partitions: [] Added partitions (assigned - owned): [vms_dlq_hello-topic-0] Revoked partitions (owned - assigned): [] 2025-09-23 15:53:09.871 INFO 25232 --- [opic_demo-group] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] Notifying assignor about the new Assignment(partitions=[hello-topic-0]) 2025-09-23 15:53:09.871 INFO 25232 --- [_dlq-demo-group] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] Notifying assignor about the new Assignment(partitions=[vms_dlq_hello-topic-0]) 2025-09-23 15:53:09.872 INFO 25232 --- [opic_demo-group] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] Adding newly assigned partitions: hello-topic-0 2025-09-23 15:53:09.872 INFO 25232 --- [_dlq-demo-group] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] Adding newly assigned partitions: vms_dlq_hello-topic-0 2025-09-23 15:53:09.872 INFO 25232 --- [_dlq-demo-group] com.tplink.smb.eventcenter.api.Handler : ending rebalance! 2025-09-23 15:53:09.872 INFO 25232 --- [opic_demo-group] com.tplink.smb.eventcenter.api.Handler : ending rebalance! 2025-09-23 15:53:09.878 INFO 25232 --- [opic_demo-group] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer_10.13.35.30_36b0164c-09fb-4960-a621-27bae26d4a58, groupId=demo-group] Setting offset for partition hello-topic-0 to the committed offset FetchPosition{offset=10, offsetEpoch=Optional.empty, currentLeader=LeaderAndEpoch{leader=Optional[admin1-virtual-machine:9092 (id: 0 rack: null)], epoch=absent}} 2025-09-23 15:53:09.878 INFO 25232 --- [_dlq-demo-group] o.a.k.c.c.internals.ConsumerCoordinator : [Consumer clientId=consumer_10.13.35.30_19bfe369-61c8-469a-90bd-6de2b9d7dd36, groupId=dlq-demo-group] Setting offset for partition vms_dlq_hello-topic-0 to the committed offset FetchPosition{offset=0, offsetEpoch=Optional.empty, currentLeader=LeaderAndEpoch{leader=Optional[admin1-virtual-machine:9092 (id: 0 rack: null)], epoch=absent}} 尝试处理消息: Hello Kafka! 这是一条会触发死信的消息 2025-09-23 15:53:09.915 WARN 25232 --- [nPool-worker-22] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event handle failed (attempt 1/2): 模拟业务处理失败 2025-09-23 15:53:09.915 INFO 25232 --- [nPool-worker-22] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Scheduled delay of 1000ms for event 尝试处理消息: Hello Kafka! 这是一条会触发死信的消息 2025-09-23 15:53:10.928 WARN 25232 --- [nPool-worker-22] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event handle failed (attempt 2/2): 模拟业务处理失败 2025-09-23 15:53:10.928 INFO 25232 --- [nPool-worker-22] c.t.s.e.port.kafka.KafkaEventCenter : not implement yet 2025-09-23 15:53:10.928 INFO 25232 --- [nPool-worker-22] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event sent to DLQ topic: vms_dlq_hello-topic 2025-09-23 15:53:10.928 ERROR 25232 --- [nPool-worker-22] c.t.s.e.p.k.d.DLQEventHandlerWrapper : Event failed after 2 retries, sent to DLQ观察日志中没有死信消息的日志,请分析原因,死信处理器代码如下@Slf4j @RequiredArgsConstructor public class DLQEventHandlerWrapper implements EventHandler { // 原事件处理器 private final EventHandler delegate; // DLQ配置 private final DeadLetterConfig deadLetterConfig; // 事件中心(用于发送死信) private final EventCenter eventCenter; @Override public void handleEvent(Event event) { if (!deadLetterConfig.isEnabled()) { delegate.handleEvent(event); // 未启用DLQ,直接执行原逻辑 return; } int retryCount = 0; boolean success = false; while (retryCount < deadLetterConfig.getMaxRetries()) { try { delegate.handleEvent(event); // 执行原事件处理 success = true; break; } catch (Exception e) { retryCount++; log.warn("Event handle failed (attempt {}/{}): {}", retryCount, deadLetterConfig.getMaxRetries(), e.getMessage()); if (retryCount >= deadLetterConfig.getMaxRetries()) { sendToDLQ(event); // 达到最大重试次数,发送死信 break; } // 计算重试延时(指数退避) long delay = deadLetterConfig.getRetryStrategy().getNextDelay(retryCount); try { sendDelay(event, delay); // 重试延时占位(后续实现) Thread.sleep(delay); // 临时用sleep模拟,后续替换为异步延时 } catch (InterruptedException ie) { Thread.currentThread().interrupt(); log.error("Retry interrupted for event", ie); break; } } } if (!success) { log.error("Event failed after {} retries, sent to DLQ", deadLetterConfig.getMaxRetries()); } } /** * 将事件发送至死信队列 */ private void sendToDLQ(BaseEvent event) { String dlqTopic = deadLetterConfig.getDlqTopic(); try { // 根据事件类型自动选择序列化方式(对齐原发送逻辑) SerializeEnum serializeEnum = (event instanceof EventV2) ? SerializeEnum.KRYO : SerializeEnum.JSON; eventCenter.send(dlqTopic, event, serializeEnum); // 调用现有send方法 log.info("Event sent to DLQ topic: {}", dlqTopic); } catch (Exception e) { log.error("Failed to send event to DLQ", e); } } /** * 重试延时占位方法(后续实现) * 例如:用Redis/ZooKeeper实现延时队列,或用定时任务调度 */ private void sendDelay(BaseEvent event, long delay) { log.info("Scheduled delay of {}ms for event", delay); // TODO: 后续实现真实延时逻辑(如:将事件存入延时队列,到期后重试) } }
09-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值