# Electrons **Repository Path**: mirrors_gspandy/Electrons ## Basic Information - **Project Name**: Electrons - **Description**: 异步高性能事件总线,支持熔断、限流、监听器自由组合以及监听器的顺序执行 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2020-09-24 - **Last Updated**: 2025-11-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### Electrons使用指南 #### 前言
Electrons是一个异步高性能事件总线,结合了生产者消费者模型和SUB/PUB模式。传统的SUB/PUB模型,在publish事件的时候是直接同步执行所有的监听器,是**同步阻塞**的。现在结合RINGBUFFER的无锁的急速体验,设计了Electrons,意味电路,事件就相当于跑在电路中的电子,电子在电路中的传递速度非常之快,这也是这个插件的寓意。publish的事件会放到容器中等待消费者消费,消费者就是SUB方的listener,这里有个设计上的小缺陷,由于种种愿意,listener的注册是通过注解 + 接口的方式实现,后面会提到。下面进入主题
#### 使用
废话不说直接上代码:
``` EleCircuit eleCircuit = new EleCircuit(); eleCircuit.start(); boolean ok1 = eleCircuit.publish("tag1", new LongEvent(1)); boolean ok2 = eleCircuit.publish("tag1", new LongEvent(2)); System.out.println(ok1); System.out.println(ok2); eleCircuit.stop(); ``` 具体的限流熔断以及电路相关配置放在`Config`类中,创建实例的时候也可以使用:
``` Config config = new Config(); config.set... config.set... /*省略配置信息*/ EleCircuit eleCircuit = new EleCircuit(config); ``` Event需要继承`Electron`抽象类,举个例子:
``` public class LongEvent extends Electron { @Getter private String name; /** * Constructs a prototypical Event. * * @param source The object on which the Event initially occurred. * @throws IllegalArgumentException if source is null. */ public LongEvent(Object source) { super(source); this.name = "LongEvent" + source; } } ``` 其他例如事件的权重,可以在event中设置,方法是:
``` setWeight(int weight) { this.weight = weight; } ``` 这个属性仅仅在限流开启的时候有效,否则设置了也不生效,可以根据事件的大小来划定权重。
再看看监听器,这个比较重要,先举个例子:
``` Listener(subscribe = "tag1") public class LongEventListener implements ElectronsListener { @Override public void onEvent(LongEvent electrons) throws Exception { System.out.println("ele name : " + electrons.getName() + "Source : " + electrons.getSource()); } } ``` `@Listener`注解用来标注监听器,类需要实现ElectronsListener接口,并且***切记加上泛型的限制***,否则会出问题!在OnEvent方法中实现处理事件的方法即可。
再看一下`@Listener`注解,这个注解主要限定了一些Listener的属性值,先看一下`@Listener`:
``` @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Listener { /** * 标识一个唯一的listener * * @return 这个listener的id */ String id() default ""; /** * after标识这个listener在某个listener之后执行,返回一个string,是id的集合,用【,】分割 * * @return 要在该listener之前执行的listener的id的集合 */ String after() default ""; /** * 优先级 * * @return listener的优先级 */ int priority() default Byte.MIN_VALUE; /** * 订阅的类型 * * @return 订阅的事件类型 */ String subscribe() default ""; } ``` 1. 先看一下`subscribe`属性,这个属性规定了订阅的事件的`tag`,**最好不好为默认值**。
2. 再看一下`priority`属性,这个属性规定了监听器的**优先级**,**优先级高的监听器优先执行,但不保证优先完成!**而且在`after`属性存在的情况下,priority可能会失效!
3. 下面一起来说`id`属性和`after`属性,这两个属性大部分情况下要配合使用。我们来先看一种场景:事件A需要被监听器L1和L2订阅,但是*L2的执行依赖L1的结果或者L2需要L1执行之后再执行*。这是就需要使用我们的这两个属性类配合使用,我们先把L1的`id`设置为**L1**,再在L2的`after`属性中,写入`L1`,即规定L2监听器需要在id = “L1”的执行器执行之后执行。
***注意:如果L2为尾节点,则L2不能存在`id`属性!***
这里举一个复杂的例子:公有监听器A、B、C、D、E:
* B、C必须在A之后执行
* D必须在B之后执行
* E必须在D、C之后执行
* (**执行之后的意思就是执行完了之后**)
这种情况下的`id`和`after`属性:
* A的`id` -> 1,**没有`after`属性**
* B的`id` -> 2,`after`属性为 -> "1"
* C的`id` -> 3,`after`属性为 -> "1"
* D的`id` -> 4,`after`属性为 -> "2"
* **E的`id` 不能存在**!`after`属性为 -> "3,4"(如果在多个监听器执行之后执行,`after`中的id用 **[ , ]** 隔开)
模式图应该为下面这种情况:
![Alt text](./监听器模型图.png)
#### 其他使用注意事项
1. 代理类的使用:提供了两种代理发布器,生产者消费者模型也存在容器满了的情况,这种情况如果直接使用`publish`方法需要用户自己处理异常,比较麻烦,现在提供了:**重发代理类**和**丢弃代理类**。作用:
* 重发:先发布一次事件,如果容器满了,休息500ms再发一次,如果继续满,使用同步发送。
* 丢弃:只发布一次事件,如果容器满了,直接记录日志并返回,事件丢弃(不推荐,只在小部分场景使用)。
2. 同步发布事件的说明:提供了同步发送的传统方式,这种方式会循环所有的监听器,*同步阻塞,直到所有的监听器执行完成*。由于上述那种A监听器执行完再执行监听器B的逻辑依赖disruptor的实现,所以***同步发布不支持存在after逻辑的监听器!***
#### 架构设计
![Alt text](./electrons架构图.png)
##### 关于限流
令牌桶算法的实现,针对***全部通道***,每个通道都有一个限流器(***包括NormalChannel***)。在Config中配置之后即可开启,现在没有针对某一个通道的单独配置,后期考虑加入。
##### 关于熔断
上文提到过,只有特殊通道支持熔断,这里阐述一下设计理念:
**为什么对普通通道不设计熔断**:普通通道存放的事件的监听器是没有关联的,即只要监听器链中存在`after`逻辑的监听器,就不会放到普通通道中。如果因为某个监听器持续出错,把整个通道熔断,其实是非常不合理的,因为其他监听器跟持续出错的监听器之间没有任何联系,不应该一起被熔断,其他监听器应该允许继续执行。
**为什么对特殊通道设计熔断**:原理类似,由于特殊通道是存在after逻辑的通道,即监听器之间存在关联,如果中间或前置监听器出问题,很可能导致后继监听器处理事件出错,或者无法处理事件,这时候熔断整个通道,提醒用户去解决问题。
#### 特别鸣谢
**释迦**:帮我理顺思路以及链的遍历算法的完善。
**排骨**:第一版本的开发者,看了代码之后决定重写一个更加完善、立体的插件。提供了工具类,参考了相关概念,是在1.0版本基础上的2.0版本。
#### 关于
[源码地址](https://github.com/carryxyh/Electrons)
相关问题,二维火内部联系***紫苑***,非二维火同学联系我的邮箱:*ziyuan@2dfire.com*