消息驱动 流 基础概念解析 事件驱动 (消息驱动模型)
本文目录导航:
“消息驱动、事件驱动、流 ”基础概念解析
消息驱动、事件驱动、流这些概念在消息领域中颇为流行,但它们之间的具体区别和应用场景往往让人一头雾水。
本文将对这三个概念进行深入剖析,旨在为读者提供清晰、直观的理解。
首先,消息驱动、事件驱动、流三种模式都涉及异步通信,其核心在于发送消息的服务无需等待消费者响应,从而实现服务之间的解耦。
它们的共同特点是通过中间件缓存消息,直到消费者准备接收。
在消息驱动模式下,消息通常由生产者向消费者发送,中间件如RocketMQ、Kafka或RabbitMQ等确保消息传递,其格式由消费者需求决定,支持一对多、多对一等传递方式。
这种模式下,消息驱动通讯用于场景如商品订单推送,上游生成组件专注于高效生成订单,而不必关注整个订单流程。
事件驱动模式是对消息驱动的优化,通过限制消息大小和格式,将消息封装为事件。
生产者发布事件表示系统变更,感兴趣且有权接入的服务订阅事件,触发特定逻辑或任务。
事件驱动支持一对多、多对一等传递方式,事件格式由生产者制定,但无需关心消费系统。
事件不具命令性质,仅告知发生事件,支持过滤、转换等操作,提供最小耦合性,允许生产服务和消费服务按需扩展。
流则是一个有序的无界事件序列,通常通过过滤、组合、拆分等操作进行处理,适用于时间敏感的应用场景。
流可以是无状态或有状态的,例如物流系统中的物品流转状态。
Kafka是典型的流式中间件,位置信息由消费者托管。
事件规范是明确生产者和消费者关系的关键,包含AsyncAPI和CloudEvents两部分。
AsyncAPI提供与事件API对应的OpenAPI和Swagger等规范,而CloudEvents侧重处理事件元数据。
CloudEvents的核心定义了事件传输的元数据及其在消息体中的表现形式,强调事件体的准确性,确保稳定的系统架构。
综上所述,消息驱动、事件驱动、流在异步通信中各有特点,适用于不同场景。
理解这些概念的基础,对于构建高效、灵活的消息系统至关重要。

SpringBoot核心原理:自动配置、事件驱动、Condition
SpringBoot是Spring的包装,通过自动配置使得SpringBoot可以做到开箱即用,上手成本非常低,但是学习其实现原理的成本大大增加,需要先了解熟悉Spring原理。
如果还不清楚Spring原理的,可以先查看博主之前的文章,本篇主要分析SpringBoot的启动、自动配置、Condition、事件驱动原理。
SpringBoot启动非常简单,因其内置了Tomcat,所以只需要通过下面几种方式启动即可: 可以看到第一种是最简单的,也是最常用的方式,需要注意类上面需要标注@SpringBootApplication 注解,这是自动配置的核心实现,稍后分析,先来看看SpringBoot启动做了些什么? 在往下之前,不妨先猜测一下,run方法中需要做什么?对比Spring源码,我们知道,Spring的启动都会创建一个ApplicationContext 的应用上下文对象,并调用其refresh方法启动容器,SpringBoot只是Spring的一层壳,肯定也避免不了这样的操作。
另一方面,以前通过Spring搭建的项目,都需要打成War包发布到Tomcat才行,而现在SpringBoot已经内置了Tomcat,只需要打成Jar包启动即可,所以在run方法中肯定也会创建对应的Tomcat对象并启动。
以上只是我们的猜想,下面就来验证,进入run方法: SpringBoot的启动流程就是这个方法,先看getRunListeners 方法,这个方法就是去拿到所有的 SpringApplicationRunListener 实现类,这些类是用于SpringBoot事件发布的,关于事件驱动稍后分析,这里主要看这个方法的实现原理: 一步步追踪下去可以看到最终就是通过SPI机制根据接口类型从META-INF/ 文件中加载对应的实现类并实例化,SpringBoot的自动配置也是这样实现的。
为什么要这样做呢?通过注解扫描不可以么?当然不行,这些类都在第三方jar包中,注解扫描实现是很麻烦的,当然你也可以通过@Import 注解导入,但是这种方式不适合扩展类特别多的情况,所以这里采用SPI的优点就显而易见了。
回到run方法中,可以看到调用了createApplicationContext 方法,见名知意,这个就是去创建应用上下文对象: 注意这里通过反射实例化了一个新的没见过的上下文对象AnnotationConfigServletWebServerApplicationContext ,这个是SpringBoot扩展的,看看其构造方法: 如果你有看过Spring注解驱动的实现原理,这两个对象肯定不会陌生,一个实支持注解解析的,另外一个是扫描包用的。
上下文创建好了,下一步自然就是调用refresh方法启动容器: 这里首先会调用到其父类中ServletWebServerApplicationContext : 可以看到是直接委托给了父类: 这个方法不会陌生吧,之前已经分析过了,这里不再赘述,至此SpringBoot的容器就启动了,但是Tomcat启动是在哪里呢?run方法中也没有看到。
实际上Tomcat的启动也是在refresh流程中,这个方法其中一步是调用了onRefresh方法,在Spring中这是一个没有实现的模板方法,而SpringBoot就通过这个方法完成了Tomcat的启动: 这里首先拿到TomcatServletWebServerFactory 对象,通过该对象再去创建和启动Tomcat: 上面的每一步都可以对比Tomcat的配置文件,需要注意默认只支持了http协议: 如果想要扩展的话则可以对additionalTomcatConnectors 属性设置值,需要注意这个属性没有对应的setter方法,只有 addAdditionalTomcatConnectors 方法,也就是说我们只能通过实现 BeanFactoryPostProcessor 接口的 postProcessBeanFactory 方法,而不能通过 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法,因为前者可以通过传入的BeanFactory对象提前获取到 TomcatServletWebServerFactory 对象调用 addAdditionalTomcatConnectors 即可;而后者只能拿到BeanDefinition对象,该对象只能通过setter方法设置值。
这段代码会在控制台打印所有的事件名称,按照顺序如下: 以上是正常启动关闭,如果发生异常还有发布ApplicationFailedEvent 事件。
事件的发布遍布在整个容器的启动关闭周期中,事件发布对象刚刚我们也看到了是通过SPI加载的 SpringApplicationRunListener 实现类 EventPublishingRunListener ,同样事件监听器也是在 文件中配置的,默认实现了以下监听器: 可以看到有用于文件编码的(FileEncodingApplicationListener ),有加载日志框架的( LoggingApplicationListener ),还有加载配置的( ConfigFileApplicationListener )等等一系列监听器,SpringBoot也就是通过这系列监听器将必要的配置和组件加载到容器中来,这里不再详细分析,感兴趣的读者可以通过其实现的 onApplicationEvent 方法看到每个监听器究竟是监听的哪一个事件,当然事件发布和监听我们自己也是可以扩展的。
SpringBoot最核心的还是自动配置,为什么它能做到开箱即用,不再需要我们手动使用@EnableXXX 等注解来开启?这一切的答案就在 @SpringBootApplication 注解中: 这里重要的注解有三个:@SpringBootConfiguration 、 @EnableAutoConfiguration 、 @ComponentScan 。
@ComponentScan 就不用再说了, @SpringBootConfiguration 等同于 @Configuration ,而 @EnableAutoConfiguration 就是开启自动配置: @AutoConfigurationPackage 注解的作用就是将该注解所标记类所在的包作为自动配置的包,简单看看就行,主要看 AutoConfigurationImportSelector ,这个就是实现自动配置的核心类,注意这个类是实现的 DeferredImportSelector 接口。
在这个类中有一个selectImports 方法。
这个方法在我之前的文章这一次搞懂Spring事务注解的解析也有分析过,只是实现类不同,它同样会被 ConfigurationClassPostProcessor 类调用,先来看这个方法做了些什么: 追踪源码最终可以看到也是从META-INF/ 文件中拿到所有 EnableAutoConfiguration 对应的值(在 spring-boot-autoconfigure 中)并通过反射实例化,过滤后包装成 AutoConfigurationEntry 对象返回。
看到这里你应该会觉得自动配置的实现就是通过这个selectImports 方法,但实际上这个方法通常并不会被调用到,而是会调用该类的内部类 AutoConfigurationGroup 的process和selectImports方法,前者同样是通过 getAutoConfigurationEntry 拿到所有的自动配置类,而后者这是过滤排序并包装后返回。
下面就来分析ConfigurationClassPostProcessor 是怎么调用到这里的,直接进入 processConfigBeanDefinitions 方法: 前面一大段主要是拿到合格的Configuration 配置类,主要逻辑是在 方法中,该方法完成了对 @Component 、 @Bean 、 @Import 、 @ComponentScans 等注解的解析,这里主要看对 @Import 的解析,其它的读者可自行分析。
一步步追踪,最终会进入到 processConfigurationClass 方法: 这里需要注意 方法的调用,这个方法就是进行Bean加载过滤的,即根据 @Condition 注解的匹配值判断是否加载该Bean,具体实现稍后分析,继续跟踪主流程 doProcessConfigurationClass : 这里就是完成对一系列注解的支撑,我省略掉了,主要看processImports 方法,这个方法就是处理 @Import 注解的: 刚刚我提醒过AutoConfigurationImportSelector 是实现 DeferredImportSelector 接口的,如果不是该接口的实现类则是直接调用 selectImports 方法,反之则是调用 方法: 首先创建了一个DeferredImportSelectorHolder 对象,如果是第一次执行则是添加到 deferredImportSelectors 属性中,等到 的最后调用process方法: 反之则是直接执行,首先通过register拿到AutoConfigurationGroup 对象: 然后在processGroupImports 方法中进行真正的处理: 在getImports 方法中就完成了对process和 selectImports 方法的调用,拿到自动配置类后再递归调用调用 processImports 方法完成对自动配置类的加载。
至此,自动配置的加载过程就分析完了,下面是时序图: 在自动配置类中有很多Condition相关的注解,以AOP为例: 这里就能看到@ConditionalOnProperty 、 @ConditionalOnClass 、 @ConditionalOnMissingClass ,另外还有 @ConditionalOnBean 、 @ConditionalOnMissingBean 等等很多条件匹配注解。
这些注解表示条件匹配才会加载该Bean,以@ConditionalOnProperty 为例,表明配置文件中符合条件才会加载对应的Bean,prefix表示在配置文件中的前缀,name表示配置的名称, havingValue 表示配置为该值时才匹配, matchIfMissing 则是表示没有该配置是否默认加载对应的Bean。
其它注解可类比理解记忆,下面主要来分析该注解的实现原理。
这里注解点进去看会发现每个注解上都标注了@Conditional 注解,并且value值都对应一个类,比如 OnBeanCondition ,而这些类都实现了 Condition 接口,看看其继承体系: 上面只展示了几个实现类,但实际上Condition的实现类是非常多的,我们还可以自己实现该接口来扩展@Condition 注解。
Condition接口中有一个matches方法,这个方法返回true则表示匹配。
该方法在 ConfigurationClassParser 中多处都有调用,也就是刚刚我提醒过的shouldSkip方法,具体实现是在 ConditionEvaluator 类中: 再来看看matches的实现,但OnBeanCondition 类中没有实现该方法,而是在其父类 SpringBootCondition 中: getMatchOutcome 方法也是一个模板方法,具体的匹配逻辑就在这个方法中实现,该方法返回的 ConditionOutcome 对象就包含了是否匹配和日志消息两个字段。
进入到 OnBeanCondition 类中: 可以看到该类支持了@ConditionalOnBean 、 @ConditionalOnSingleCandidate 、 @ConditionalOnMissingBean 注解,主要的匹配逻辑在 getMatchingBeans 方法中: 这里逻辑看起来比较复杂,但实际上就做了两件事,首先通过getNamesOfBeansIgnoredByType 方法调用 拿到容器中对应的Bean实例,然后根据返回的结果判断哪些Bean存在,哪些Bean不存在(Condition注解中是可以配置多个值的)并返回MatchResult对象,而MatchResult中只要有一个Bean没有匹配上就返回false,也就决定了当前Bean是否需要实例化。
本篇分析了SpringBoot核心原理的实现,通过本篇相信读者也将能更加熟练地使用和扩展SpringBoot。
另外还有一些常用的组件我没有展开分析,如事务、MVC、监听器的自动配置,这些我们有了Spring源码基础的话下来看一下就明白了,这里就不赘述了。
最后读者可以思考一下我们应该如何自定义starter启动器,相信看完本篇应该难不倒你。
典型的事件驱动型应用有哪些
事件驱动型应用是一种软件架构模式,其中应用程序的执行由外部事件触发和驱动。
以下是一些典型的事件驱动型应用的示例:1. 消息队列系统:消息队列系统通过将消息发送到队列中,实现不同组件之间的异步通信。
当有新的消息到达时,系统会触发相应的事件并处理消息。
2. 实时数据处理系统:实时数据处理系统用于处理大量的实时数据流。
数据的到达会触发事件,系统会对数据进行处理和分析,例如实时监控系统、实时推荐系统等。
3. GUI 应用程序:图形用户界面(GUI)应用程序通常使用事件驱动的方式处理用户的输入和操作。
例如,点击按钮、输入文本等操作会触发相应的事件,应用程序会根据事件进行响应。
4. Web 应用程序:Web 应用程序通常使用事件驱动的方式处理用户的请求和响应。
例如,当用户发起一个 HTTP 请求时,Web 服务器会触发相应的事件并返回相应的内容。
5. 多线程应用程序:多线程应用程序中的线程可以通过事件进行通信和同步。
当一个线程完成某个任务时,可以触发事件通知其他线程进行相应的操作。
6. IoT 应用程序:物联网(IoT)应用程序通常涉及多个设备和传感器之间的通信。
当设备状态发生变化或传感器检测到某个事件时,会触发相应的事件进行处理。
这些只是一些典型的事件驱动型应用的示例,实际上,事件驱动型应用可以适用于各种不同的领域和场景。