@EventListener注解详细使用(IT枫斗者)
@EventListener注解详细使用
简介
- @EventListener是一种事件驱动编程在spring4.2的时候开始有的,早期可以实现ApplicationListener接口, 为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式;为的就是业务系统逻辑的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。
- 比如我们做一个电商系统,用户下单支付成功后,我们一般要发短信或者邮箱给用户提示什么的,这时候就可以把这个通知业务做成一个单独事件监听,等待通知就可以了;把它解耦处理。
使用@EventListener注解
-
建立事件对象,当调用publishEvent方法是会通过这个bean对象找对应事件的监听。AddDataEvent.java
-
package com.rw.article.pay.event.bean;import org.springframework.context.ApplicationEvent;/*** 新增mongodb数据事件*/ public class AddDataEvent extends ApplicationEvent {public AddDataEvent(Object source) {super(source);}public AddDataEvent(Object source, Class clz, Object data) {super(source);this.clz = clz;this.data = data;}public AddDataEvent(Object source, Class clz, Object data, String modelName, String userAgent) {super(source);this.clz = clz;this.data = data;this.modelName = modelName;this.userAgent = userAgent;}/** 要更新的表对象 **/private Class clz;/** 操作的数据**/private Object data;/** 模块名称**/private String modelName;/** 浏览器标识 **/private String userAgent;public Class getClz() {return clz;}public void setClz(Class clz) {this.clz = clz;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}public String getModelName() {return modelName;}public void setModelName(String modelName) {this.modelName = modelName;}public String getUserAgent() {return userAgent;}public void setUserAgent(String userAgent) {this.userAgent = userAgent;} }
-
对应的监听AddDataEventListener .java
-
package com.rw.article.pay.event.listener; import com.alibaba.fastjson.JSON; import com.rw.article.pay.event.bean.AddDataEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory;import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component;/*** 新增数据的事件监听*/ @Component public class AddDataEventListener {private static Logger log = LoggerFactory.getLogger(AddDataEventListener.class);/** 在AnnotationConfigUtils#registerAnnotationConfigProcessors注册了BeanDefinition 对应的是EventListenerMethodProcessor对象 , AnnotationConfigUtils在AnnotationConfigServletWebServerApplicationContext构造方法里被加载* *//*** DefaultListableBeanFactory#中preInstantiateSingletons -> (beanName为org.springframework.context.event.internalEventListenerProcessor时得到EventListenerMethodProcessor)EventListenerMethodProcessor#afterSingletonsInstantiated this.processBean(factories, beanName, type)* 然后把要执行的方法封装为ApplicationListenerMethodAdapter -> 添加到listener中 AbstractApplicationEventMulticaster#addApplicationListener* */// 该方法在 ApplicationListenerMethodAdapter 利用反射执行/*** 处理新增数据的事件**/@EventListenerpublic void handleAddEvent(AddDataEvent event) {log.info("发布的data为:{} ", JSON.toJSONString(event));} }
-
建立测试类
-
package com.rw.article.pay.action;import com.rw.article.pay.event.bean.AddDataEvent; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;import javax.annotation.Resource;/*** 测试的controller*/ @Controller @RequestMapping("/test") public class TestController {@Resourceprivate ApplicationContext applicationContext;@ResponseBody@RequestMapping("/testListener")public String testListener(){applicationContext.publishEvent(new AddDataEvent(this,TestController.class,"test"));return "success";} }
-
结果是能够监听到的
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pHjkZeUm-1680567653256)(C:%5CUsers%5Cquyanliang%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1680567079331.png)]
-
如果要使用异步加上
@EnableAsync
注解,方法上加@Async
注解,如下spring boot项目配置 -
@SpringBootApplication @EnableAsync public class XApplication{public static void main(String[] args) {ConfigurableApplicationContext run = new SpringApplicationBuilder(XApplication.class).web(true).run(args);run.publishEvent("test");} }
-
@Async@EventListenerpublic void test(String wrapped){System.out.println("当前线程 "+Thread.currentThread().getName());System.out.println(wrapped);}
-
还可以配置线程池
taskExecutor
-
@Configuration public class GenericConfiguration {@Beanpublic Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//核心线程数:线程池创建时候初始化的线程数//最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程//缓冲队列:用来缓冲执行任务的队列//允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁//线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池//线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(20);executor.setKeepAliveSeconds(60);executor.setThreadNamePrefix("taskExecutor-");executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());return executor;} }
源码解析
-
原理还得从org.springframework.context.event.internalEventListenerProcessor
说起。 -
在AnnotationConfigUtils#registerAnnotationConfigProcessors注册了BeanDefinition 对应的是EventListenerMethodProcessor对象 , 而AnnotationConfigUtils是在AnnotationConfigServletWebServerApplicationContext构造方法里被加载。这里要提一下AnnotationConfigServletWebServerApplicationContext,他是spring boot启动入口的重要类(我这里用的是spring boot所以是这个类),可以相当于以前用xml的ClassPathXmlApplicationContext。
-
public static final String EVENT_LISTENER_PROCESSOR_BEAN_NAME ="org.springframework.context.event.internalEventListenerProcessor";public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {................... // 注册EventListenerMethodProcessor对象if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));}...........................return beanDefs;}
-
注册的EventListenerMethodProcessor对象会在初始化非懒加载对象的时候运行它的afterSingletonsInstantiated方法。
AbstractApplicationContext#finishBeanFactoryInitialization -
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {............. // 初始化非懒加载对象beanFactory.preInstantiateSingletons(); }
-
DefaultListableBeanFactory#preInstantiateSingletons
-
@Override public void preInstantiateSingletons() throws BeansException {..................// 触发所有适用bean的初始化后回调 主要是afterSingletonsInstantiated方法for (String beanName : beanNames) { //如果beanName传入org.springframework.context.event.internalEventListenerProcessor 因为已经上面代码已经初始化,将从缓存中得到一个EventListenerMethodProcessor对象Object singletonInstance = getSingleton(beanName);if (singletonInstance instanceof SmartInitializingSingleton) {final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;if (System.getSecurityManager() != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {smartSingleton.afterSingletonsInstantiated();return null;}, getAccessControlContext());}else { // 调用其afterSingletonsInstantiated方法smartSingleton.afterSingletonsInstantiated();}}} }
-
EventListenerMethodProcessor#afterSingletonsInstantiated
-
@Override public void afterSingletonsInstantiated() {List<EventListenerFactory> factories = getEventListenerFactories();ConfigurableApplicationContext context = getApplicationContext();String[] beanNames = context.getBeanNamesForType(Object.class);for (String beanName : beanNames) {if (!ScopedProxyUtils.isScopedTarget(beanName)) {Class<?> type = null;try {type = AutoProxyUtils.determineTargetClass(context.getBeanFactory(), beanName);}catch (Throwable ex) {// An unresolvable bean type, probably from a lazy bean - let's ignore it.if (logger.isDebugEnabled()) {logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);}}if (type != null) {if (ScopedObject.class.isAssignableFrom(type)) {try {Class<?> targetClass = AutoProxyUtils.determineTargetClass(context.getBeanFactory(), ScopedProxyUtils.getTargetBeanName(beanName));if (targetClass != null) {type = targetClass;}}catch (Throwable ex) {// An invalid scoped proxy arrangement - let's ignore it.if (logger.isDebugEnabled()) {logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);}}}try {// 重点是这个方法 处理beanprocessBean(factories, beanName, type);}catch (Throwable ex) {throw new BeanInitializationException("Failed to process @EventListener " +"annotation on bean with name '" + beanName + "'", ex);}}}} }
-
EventListenerMethodProcessor#processBean;这里有一个重要的类就是ApplicationListenerMethodAdapter,spring把加入了@EventListener注解的方法封装进ApplicationListenerMethodAdapter对象里,然后我们publishEvent方法是,其实是调用的对应的ApplicationListenerMethodAdapter,然后里面是执行这个方法,这里可以看下ApplicationListenerMethodAdapter类的属性。
-
public class ApplicationListenerMethodAdapter implements GenericApplicationListener {protected final Log logger = LogFactory.getLog(getClass());private final String beanName;private final Method method;private final Method targetMethod;private final AnnotatedElementKey methodKey;private final List<ResolvableType> declaredEventTypes;@Nullableprivate final String condition;private final int order;@Nullableprivate ApplicationContext applicationContext;@Nullableprivate EventExpressionEvaluator evaluator;.................................. }
-
protected void processBean(final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {if (!this.nonAnnotatedClasses.contains(targetType)) {Map<Method, EventListener> annotatedMethods = null;try {// 拿到使用了@EventListener注解的方法annotatedMethods = MethodIntrospector.selectMethods(targetType,(MethodIntrospector.MetadataLookup<EventListener>) method ->AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));}catch (Throwable ex) {// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.if (logger.isDebugEnabled()) {logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);}}if (CollectionUtils.isEmpty(annotatedMethods)) {this.nonAnnotatedClasses.add(targetType);if (logger.isTraceEnabled()) {logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());}}else {// Non-empty set of methodsConfigurableApplicationContext context = getApplicationContext();for (Method method : annotatedMethods.keySet()) {for (EventListenerFactory factory : factories) {// 判断是否支持该方法 这里用的DefaultEventListenerFactory spring5.0.8 写死的返回trueif (factory.supportsMethod(method)) {//选择方法 beanName 这里是AddDataEventListener的beanName 默认是addDataEventListenerMethod methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));// 这里是创建一个ApplicationListenerMethodAdapter对象ApplicationListener<?> applicationListener =factory.createApplicationListener(beanName, targetType, methodToUse);if (applicationListener instanceof ApplicationListenerMethodAdapter) {// 如果是ApplicationListenerMethodAdapter对象 就把context和evaluator传进去((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);}// 添加到ApplicationListener事件Set集合中去context.addApplicationListener(applicationListener);break;}}}if (logger.isDebugEnabled()) {logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +beanName + "': " + annotatedMethods);}}} }
-
后面就是触发事件监听了AbstractApplicationContext#publishEvent
-
@Override public void publishEvent(ApplicationEvent event) {publishEvent(event, null); }protected void publishEvent(Object event, @Nullable ResolvableType eventType) {..............................// Multicast right now if possible - or lazily once the multicaster is initializedif (this.earlyApplicationEvents != null) {this.earlyApplicationEvents.add(applicationEvent);}else {// 进入multicastEventgetApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}// Publish event via parent context as well...if (this.parent != null) {if (this.parent instanceof AbstractApplicationContext) {((AbstractApplicationContext) this.parent).publishEvent(event, eventType);}else {this.parent.publishEvent(event);}}}
-
SimpleApplicationEventMulticaster#multicastEvent->invokeListener->doInvokeListener
-
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {listener.onApplicationEvent(event);}catch (ClassCastException ex) {String msg = ex.getMessage();if (msg == null || matchesClassCastMessage(msg, event.getClass().getName())) {// Possibly a lambda-defined listener which we could not resolve the generic event type for// -> let's suppress the exception and just log a debug message.Log logger = LogFactory.getLog(getClass());if (logger.isDebugEnabled()) {logger.debug("Non-matching event type for listener: " + listener, ex);}}else {throw ex;}} }
-
ApplicationListenerMethodAdapter#onApplicationEvent]
-
@Override public void onApplicationEvent(ApplicationEvent event) {processEvent(event); } ApplicationListenerMethodAdapter#processEventpublic void processEvent(ApplicationEvent event) {Object[] args = resolveArguments(event);if (shouldHandle(event, args)) {// 执行真正的方法Object result = doInvoke(args);if (result != null) {handleResult(result);}else {logger.trace("No result object given - no result to handle");}} }
-
ApplicationListenerMethodAdapter#doInvoke
-
protected Object doInvoke(Object... args) {Object bean = getTargetBean();ReflectionUtils.makeAccessible(this.method);try {return this.method.invoke(bean, args);}catch (IllegalArgumentException ex) {assertTargetBean(this.method, bean, args);throw new IllegalStateException(getInvocationErrorMessage(bean, ex.getMessage(), args), ex);}catch (IllegalAccessException ex) {throw new IllegalStateException(getInvocationErrorMessage(bean, ex.getMessage(), args), ex);}catch (InvocationTargetException ex) {// Throw underlying exceptionThrowable targetException = ex.getTargetException();if (targetException instanceof RuntimeException) {throw (RuntimeException) targetException;}else {String msg = getInvocationErrorMessage(bean, "Failed to invoke event listener method", args);throw new UndeclaredThrowableException(targetException, msg);}} }
-
ApplicationListenerMethodAdapter#getTargetBean
-
protected Object getTargetBean() {Assert.notNull(this.applicationContext, "ApplicationContext must no be null");return this.applicationContext.getBean(this.beanName); }
相关文章:

@EventListener注解详细使用(IT枫斗者)
EventListener注解详细使用 简介 EventListener是一种事件驱动编程在spring4.2的时候开始有的,早期可以实现ApplicationListener接口, 为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式;为的就是业务系统逻辑的解耦,提高…...

[c++17新增语言特性] --- [[nodiscard]]和[[maybe_unused]]
1 [[nodiscard]] 介绍和应用示例 [[nodiscard]] 是C++17引入的一个属性(Attribute),它用于向编译器提示一个函数的返回值应该被检查,避免其被忽略或误用。它可以被用于函数、结构体、类、枚举和 typedef 等声明上,表示如果函数返回值未被使用,或者结构体、类、枚举和 type…...

Centos7安装和使用docker的笔记
最近项目要求用容器部署,所以需要将docker的用法搞清楚,在操作过程中,积累了一些操作方法和技巧,作为笔记,为后面使用做个参考。 首先安装docker需要给centos增加源(参考https://www.runoob.com/docker/cen…...

结构像与功能像
导读现代神经成像技术使我们能够研究活体大脑的结构和功能。活体神经成像的益处是显而易见的,而且在基础和临床神经科学中,神经成像已经取得了巨大进展。本文概述了利用活体神经成像研究大脑结构和功能的工作和成就。介绍了几种不同类型的结构MRI成像方法…...

【IAR工程】STM8S基于ST标准库读取DS1302数据
【IAR工程】STM8S基于ST标准库读取DS1302数据✨申明:本文章仅发表在CSDN网站,任何其他网站,未注明来源,见此内容均为盗链和爬取,请多多尊重和支持原创!🍁对于文中所提供的相关资源链接将作不定期更换。&…...

【SpringBoot】实现后端服务器发送QQ邮件验证码的功能
步骤一、添加邮件相关依赖二、配置邮件服务器三、发送邮件PS:SMTP 发送失败的解决方案一、添加邮件相关依赖 在 pom.xml 文件中添加 JavaMail 和 Spring Mail 相关的依赖。示例代码如下: <dependency><groupId>com.sun.mail</groupId&g…...

vue在input中输入后,按回车,提交数据
vue在input中输入后,按回车,提交数据 1.展示效果如下: 2.代码展示: <div><el-input v-model"toAddNameText" keyup.enter.native"toAddName()" placeholder"回车,即新增该竖杆名称…...

【YOLOX】用YOLOv5框架YOLOX
【YOLOX】用YOLOv5框架YOLOX一、新建common_x.py二、修改yolo.py三、新建yolox.yaml四、训练最近在跑YOLO主流框架的对比实验,发现了一个很奇怪的问题,就是同一个数据集,在不同YOLO框架下训练出的结果差距竟然大的离谱。我使用ultralytics公司…...

【python机器学习实验】——逻辑回归与感知机进行线性分类,附可视化结果!
【python机器学习实验】——逻辑回归与感知机进行线性分类,附可视化结果! 下载链接 下载链接 下载链接 可视化效果图: 感知机模型结果为例: 首先,我们有训练数据和测试数据,其每一行为(x,y,label)的形式…...

wps删除的文件怎么恢复
在办公中,几乎每个人都会用到WPS办公软件。它可以帮助我们快速有效地处理各种Word文档、ppt幻灯片、excel表格等。但有文件就会有清理,如果我们不小心删除了wps文件呢?那些wps删除的文件怎么恢复?针对这种情况,小编为大家带来一些恢复WPS文…...

NIO消息黏包和半包处理
1、前言 我们在进行NIO编程时,通常会使用缓冲区进行消息的通信(ByteBuffer),而缓冲区的大小是固定的。那么除非你进行自动扩容(Netty就是这么处理的),否则的话,当你的消息存进该缓冲…...

day018 第六章 二叉树 part05
一、513.找树左下角的值 这个题目的主要思路是使用广度优先搜索(BFS)遍历整棵树,最后返回最后一层的最左边的节点的值。具体的实现可以使用队列来存储每一层的节点,并且在遍历每一层节点时,不断更新最左边的节点的值。…...

如何下载ChatGPT-ChatGPT如何写作
CHATGPT能否改一下文章 ChatGPT 作为一种自然语言处理技术,生成的文章可能存在表达不够准确或文风不符合要求等问题。在这种情况下,可以使用编辑和修改来改变输出的文章,使其符合特定的要求和期望。 具体来说,可以采用以下步骤对…...

微策略再次买入
原创:刘教链* * *隔夜,比特币再次小幅回升至28k上方。微策略(Microstrategy)创始人Michael Saylor发推表示,微策略再次出手,买入1045枚比特币。此次买入大概花费2930万美元,平均加仓成本28016美…...

express框架
Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架. 创建一个基本的express web服务器 // 1.导入express const express require(express); // 2.创建web服务器 const app express(); // 3.启动web服务器 app.listen(80, ()>{console.log(expres…...

完蛋的goals
...

Javase学习文档------面象对象初探
引入面向对象 面向对象的由来: 面向对象编程(Object-Oriented Programming, OOP)是一种编程范型,其由来可以追溯到20世纪60年代。在此之前,主流编程语言采用的是“过程化编程”模式,即面向过程编程模式。在这种模式下&…...

ChatGPT能够干翻谷歌吗?
目前大多数人对于ChatGPT的喜爱,主要源自于其强大的沟通能力,当我们向ChatGPT提出问题时,它不仅能够为我们提供结论,而且还能够与我们建立沟通,向ChatGPT提出任何问题,感觉都像是在与一个真实的人类进行交谈…...

PCL 使用点云创建数字高程模型DEM
目录 一、DEM1、数字高程模型二、代码实现三、结果展示1、点云2、DEM四、相关链接一、DEM 1、数字高程模型 数字高程模型(Digital Elevation Model),简称DEM,是通过有限的地形高程数据实现对地面地形的数字化模拟(即地形表面形态的数字化表达),它是用一组有序数值阵列形…...

我体验了首个接入GPT-4的代码编辑器,太炸裂了
最近一款名为Cursor的代码编辑器已经传遍了圈内,受到众多编程爱好者的追捧。 它主打的亮点就是,通过 GPT-4 来辅助你编程,完成 AI 智能生成代码、修改 Bug、生成测试等操作。 确实很吸引人,而且貌似也能大大节省人为的重复工作&…...

互联网数据挖掘与分析讲解
一、定义 数据挖掘(英语:Data mining),又译为资料探勘、数据采矿。它是数据库知识发现(英语:Knowledge-Discovery in Databases,简称:KDD)中的一个步骤。数据挖掘一般是指从大量的数…...

linux之cut的使用
cut是一个选取命令,就是将一段数据经过分析,取出我们想要的。一般来说,选取信息通常是针对“行”来进行分析的,并不是整篇信息分析的 其语法格式为: cut [-bn] [file] 或 cut [-c][file] 或 cut [-df] [file]使用说明:…...

Redis第十讲 Redis之Hash数据结构Dict-rehash扩容操作
Rehash 执行过程 字典的 rehash 操作实际上就是执行以下任务: 创建一个比 ht[0]->table 更大的 ht[1]->table ;将 ht[0]->table 中的所有键值对迁移到 ht[1]->table ;将原有 ht[0] 的数据清空,并将 ht[1] 替换为新的 ht[0] ; 经过以上步骤之后, 程序就在不改…...

电动力学问题中的Matlab可视化
电磁场的经典描述 小说一则 电磁场的经典描述就是没有啥玩意量子力学的经典电动力学下对电磁场的描述,以后有空写个科幻小说,写啥呢,就写有天张三遇见了一个外星人,外星人来自这样一个星球,星球上的物质密度特别低,导致外星人的测量会明显的影响物质的运动,外星人不能同时得到…...

云原生周刊:编程即将终结?
近日哈佛大学计算机科学的前教授 Matt Welsh,分享了他对计算机科学、分布式计算的未来以及 ChatGPT 和 GitHub Copilot 是否代表编程结束的开始的看法。 威尔士说,编程语言仍然很复杂。再多的工作也无法让它变得简单。 “在我看来,任何改进…...

C++ STL,resize 和 reserve 的区别
结论放前边:resize和reserve都可以给容器扩容,区别在于resize会进行填充,使容器处于满员的状态,即sizecapacity,而reserve不会填充,有size<capacity. 1. size 和 capacity 的区别 size和capacity是容器…...

Java——详解ReentrantLock与AQS的关联以及AQS的数据结构和同步状态State
前言 Java中大部分同步类(Lock、Semaphore、ReentrantLock等)都是基于AbstractQueuedSynchronizer(简称为 AQS)实现的。 AQS 是一种提供了原子式管理同步状态、阻塞和唤醒线程功能以及队列模型的简单框架。 本文会先介绍应用层&a…...

vue3+vite+ts 接入QQ登录
说明 前提资料准备 在QQ互联中心注册成为开发者 站点:https://connect.qq.com/创建应用,如图 js sdk方式 下载对应的sdk包 sdk下载:https://wiki.connect.qq.com/sdk%e4%b8%8b%e8%bd%bd 使用 下载离线js sdk 打开:https:…...

消息队列kafka及zookeeper机制
目录 一、zookeeper 1、zookeeper简介 2、zookeeper特点 3、zookeeper工作模式及机制 4、zookeeper应用场景及选举机制 5、zookeeper集群部署 ①实验环境 ②安装zookeeper 二、消息队列kafka 1、为什么要有消息队列 2、使用消息队列的好处 3、kafka简介 4、kafka…...

分布式 - 分布式体系架构:IT架构的演进过程
文章目录01. 应用与数据一体模式02. 应用服务和数据服务的分离03. 缓存与性能的提升04. 服务器集群处理并发05. 数据库读写分离06. 反向代理和 CDN07. 分布式文件系统和分布式数据库系统08. NoSQL和搜索引擎09. 业务拆分10. Redis缓存在应用服务器上是进程内缓存还是进程外缓存…...