精华内容
下载资源
问答
  • 事件驱动

    2017-04-13 11:00:22
    计算机领域的事件驱动所谓事件驱动,简单地说就是你点什么按钮(即产生什么事件),电脑执行什么操作(即调用什么函数).当然事件不仅限于用户的操作. 事件驱动的核心自然是事件。从事件角度说,事件驱动程序的基本...

    1.概念

    在持续事务管理过程中,进行决策的一种策略,即跟随当前时间点上出现的事件,调动可用资源,执行相关任务,使不断出现的问题得以解决,防止事务堆积。在计算机编程、公共关系、经济活动等领域均有应用。

    计算机领域的事件驱动

    所谓事件驱动,简单地说就是你点什么按钮(即产生什么事件),电脑执行什么操作(即调用什么函数).当然事件不仅限于用户的操作. 事件驱动的核心自然是事件。从事件角度说,事件驱动程序的基本结构是由一个事件收集器、一个事件发送器和一个事件处理器组成。事件收集器专门负责收集所有事件,包括来自用户的(如鼠标、键盘事件等)、来自硬件的(如时钟事件等)和来自软件的(如操作系统、应用程序本身等)。事件发送器负责将收集器收集到的事件分发到目标对象中。事件处理器做具体的事件响应工作,它往往要到实现阶段才完全确定,因而需要运用虚函数机制(函数名往往取为类似于HandleMsg的一个名字)。对于框架的使用者来说,他们唯一能够看到的是事件处理器。这也是他们所关心的内容。

    视图(即我们通常所说的“窗口”)是“事件驱动”应用程序的另一个要元。它是我们所说的事件发送器的目标对象。视图接受事件并能够对其进行处理。当我们将事件发送到具体的视图时,实际上我们完成了一个根本性的变化:从传统的流线型程序结构到事件触发方式的转变。这样应用程序具备相当的柔性,可以应付种种离散的、随机的事件。

    由于Windows本身是基于“事件驱动”模型的。因而在Windows操作系统下实现应用程序框架有相当的便利。在事件驱动程序的基本单元中,事件收集器已经由Windows系统完成;事件发送器也已经由Windows完成了部分内容。之所以是部分而非完全是因为Windows是用C语言实现的,而不是C++。由于没有对象,Windows将事件发送到所谓的“窗口函数”中(尽管不是发送到具体的对象,但应该说这是面向对象方式实现的一个变体)。要感谢Windows做了这件事。确定事件的目标所要做的工作的复杂可能要超出我们的想象。

    wxWidgets的中所有可以处理事件的类都继承自wxEvtHandler,其中包含frames,buttons,menus,even documents,所有的窗体类(即从wxWindow继承的类)和程序类(application class).

    这些类可以有一个事件表,用来绑定事件和被调用的函数(handler functions).
    过程 3.2. 建立一个静态事件表(即编译时生成的事件表)的操作步骤
    建立一个新类(直接或间接从wxEvtHandler继承)
    为每个要处理的事件声明被调用的函数
    在被处理的事件所在的类的声明中加入宏DECLARE_EVENT_TABLE
    在宏BEGIN_EVENT_TABLE… END_EVENT_TABLE(就是事件表)中将函数与枚举的数字绑定(因为产生该类型的事件的按钮不唯一,要用枚举数来区分);有些事件不必与枚举数绑定,因为产生该类型的事件的对象可以确定(比如就是this).
    例 3.1. 一个事件表
    BEGIN_EVENT_TABLE(MyFrame,wxFrame)
    EVT_MENU (wxID_ABOUT,MyFrame::OnAbout)
    EVT_MENU (wxID_EⅪT,MyFrame::OnQuit)
    EVT_SIZE (MyFrame::OnSize)
    //不必与枚举数绑定,因为产生该类型的事件的对象是this
    EVT_BUTTON (wxID_OK,MyFrame::OnButtonOK)
    END_EVENT_TABLE()
    在事件中指定被绑定的数字,wxWidgets会将其映射到对应的函数,并调用函数
    所有在事件表中被绑定的函数有相似的形式:返回值都是void,不是virtual函数,参数为wxCommandEvent类型

    模型说明

    在UI编程中,常常要对鼠标点击进行相应,首先如何获得鼠标点击呢?

    方式一:创建一个线程,该线程一直循环检测是否有鼠标点击,那么这个方式有以下几个缺点:

    1. CPU资源浪费,可能鼠标点击的频率非常小,但是扫描线程还是会一直循环检测,这会造成很多的CPU资源浪费;如果扫描鼠标点击的接口是阻塞的呢?
    2. 如果是堵塞的,又会出现下面这样的问题,如果我们不但要扫描鼠标点击,还要扫描键盘是否按下,由于扫描鼠标时被堵塞了,那么可能永远不会去扫描键盘;
    3. 如果一个循环需要扫描的设备非常多,这又会引来响应时间的问题;
      所以,该方式是非常不好的。

    方式二:就是事件驱动模型

    目前大部分的UI编程都是事件驱动模型,如很多UI平台都会提供onClick()事件,这个事件就代表鼠标按下事件。事件驱动模型大体思路如下:
    1. 有一个事件(消息)队列;
    2. 鼠标按下时,往这个队列中增加一个点击事件(消息);
    3. 有个循环,不断从队列取出事件,根据不同的事件,调用不同的函数,如onClick()、onKeyDown()等;
    4. 事件(消息)一般都各自保存各自的处理函数指针,这样,每个消息都有独立的处理函数;
    如图:
    这里写图片描述

    展开全文
  • 事件驱动架构 事件驱动的体系结构是一种软件体系结构范例,可促进事件的产生,检测,使用和响应。 如何实现事件驱动的体系结构,以及何时使用它? 事件驱动的体系结构与微服务齐头并进。 发生操作时,将创建一个...

    事件驱动架构

    事件驱动的体系结构是一种软件体系结构范例,可促进事件的产生,检测,使用和响应。

    如何实现事件驱动的体系结构,以及何时使用它?

    事件驱动的体系结构与微服务齐头并进。 发生操作时,将创建一个事件,然后将该事件用于在所有等待该事件发生的事件上做出决策。

    服务不再绑定在一起,因为在发布订阅类型模型中,调用者不再同步调用被调用者。 相反,被呼叫者以最终一致的方式对事件进行操作。

    因此,事件驱动的体系结构可以更可靠,因为它们不必立即对服务调用进行操作(允许服务在成功之前都能够失败),但是很难预测何时执行操作(对于同样的原因)。

    事件驱动架构的一个简单示例是Amazon。 如果您曾经在忙碌的时间(例如黑色星期五)在亚马逊购物,则可以订购商品,但之后只能发送电子邮件说明您订购的商品实际上无货。

    如果您以事件驱动的体系结构来考虑此过程,则它可能以下列方式工作:

    客户订购该项目并发布OrderPlaced事件Stock服务订阅了该事件,但是在处理事件时,它检查了库存,现在为0 The Stock服务随后发布了OutOfStock事件电子邮件服务订阅了该事件并向客户发送电子邮件,说明该商品无库存。

    事件驱动的体系结构可以将队列与发布订阅模型结合使用作为支持模型。 这是为了保证在链中某些服务无法处理事件的情况下传递事件消息。

    事件驱动架构的优缺点

    有几个原因为什么使用事件驱动的体系结构比其他体系结构有优势。

    • 松散耦合 —服务不需要相互依赖。 这应用了不同的因素,例如传输协议,可用性(服务在线)和正在发送的数据。 消费者仍将需要知道如何解释事件或消息,因此在这两个服务之间仍应使用严格的合同,但是合同的实现细节无关紧要。
    • 可扩展性 —由于服务不再耦合,服务1的吞吐量不再需要满足服务2的吞吐量。这可以帮助降低成本,因为服务不再需要24/7全天候在线,并且可以利用无服务器计算的无限扩展。
    • 异步性 -由于服务不再依赖于同步返回的结果,因此可以使用即发即弃模型,这可以大大加快流程。 这可能会有一个缺点,下面将对此进行概述。
    • 时间点恢复 -如果事件由队列支持或维护某种历史记录,则可以重播事件,甚至可以及时返回并恢复状态。

    使用事件驱动的体系结构也有缺点。

    过度设计流程 -有时从一个服务到另一个服务的简单调用就足够了。 如果流程使用事件驱动的体系结构,则通常需要更多的基础结构来支持它,这将增加成本(因为它将需要一个排队系统)

    不一致 -由于流程现在依赖于最终的一致性,因此通常不支持ACID(原子性,一致性,隔离性,持久性)事务,因此重复处理或乱序事件的处理会使服务代码更加复杂,并且难以测试和调试所有情况。

    事件发生变化会怎样?

    想象一下一种情况,当我想添加或更改事件的合同时,例如在OrderPlaced事件中,将数量从整数更改为浮点数。

    如果不期望此更改,这种情况将使订户中断。

    您需要对事件的合同进行版本控制,以防止这种破坏。 一个好的经验法则通常是,对合同的任何增加都是可以的,但是要删除或更改某些内容,必须发布该事件的新版本,而订户也希望有新的事件合同。

    翻译自: https://hackernoon.com/understanding-event-driven-architecture-ub1k3umo

    事件驱动架构

    展开全文
  • 本篇是“事件驱动的微服务”系列的第二篇,主要讲述事件驱动设计。如果想要了解总体设计,请看第一篇"事件驱动的微服务-总体设计" 程序流程 我们通过一个具体的例子来讲解事件驱动设计。 本文中的程序有两个微服务,...

    本篇是“事件驱动的微服务”系列的第二篇,主要讲述事件驱动设计。如果想要了解总体设计,请看第一篇"事件驱动的微服务-总体设计"

    程序流程

    我们通过一个具体的例子来讲解事件驱动设计。 本文中的程序有两个微服务,一个是订单服务(Order Service), 另一个是支付服务(Payment Service)。用户调用订单服务的用例createOrder()来创建订单,创建之后的订单暂时还没有支付信息,订单服务然后发布命令(Command)给支付服务,支付服务完成支付,发送支付完成(Payment Created)消息。订单服务收到消息(Event),在Order表里增加Payment_Id并修改订单状态为“已付款”。
    下面就是组件图:

    事件处理

    事件分成内部事件和外部事件,内部事件是存在于一个微服务内部的事件,不与其他微服务共享。如果用DDD的语言来描述就是在有界上下文(Bounded Context)内的域事件(Domain Event)。外部事件是从一个微服务发布,而被其他微服务接收的事件。如果用DDD的语言来描述就是在不同有界上下文(Bounded Context)之间传送的域事件(Domain Event)。这两种事件的处理方式不同。

    内部事件:

    对于内部事件的处理早已有了成熟的方法,它的基本思路是创建一个事件总线(Event Bus),由它来监听事件。然后注册不同的事件处理器(Event Handler)来处理事件。这种思路被广泛地用于各种领域。

    下面就是事件总线(Event Bus)的接口,它有两个函数,一个是发布事件(Publish Event),另一个是添加事件处理器(Event Handler)。一个事件可以有一个或多个事件处理器。

    type EventBus interface {
    	PublishEvent(EventMessage)
    	AddHandler(EventHandler, ...interface{})
    }
    

    事件总线的代码的关键部分是加载事件处理器。我们以订单服务为例,下面就是加载事件处理器(Event Handler)的代码,它是初始化容器代码的一部分。在这段代码中,它只注册了一个事件,支付完成事件(PaymentCreateEvent),和与之相对应的事件处理器-支付完成事件处理器(PaymentCreatedEventHandler)。

    func loadEventHandler(c servicecontainer.ServiceContainer) error {
    	var value interface{}
    	var found bool
    
    	rluf, err := containerhelper.BuildModifyOrderUseCase(&c)
    	if err != nil {
    		return err
    	}
    	pceh := event.PaymentCreatedEventHandler{rluf}
    	if value, found = c.Get(container.EVENT_BUS); !found {
    		message := "can't find key=" + container.EVENT_BUS + " in container "
    		return errors.New(message)
    	}
    	eb := value.(ycq.EventBus)
    	eb.AddHandler(pceh,&event.PaymentCreatedEvent{})
    	return nil
    }
    

    由于在处理事件时要调用相应的用例,因此需要把用例注入到事件处理器中。在上段代码中,首先从容器中获得用例,然后创建事件处理器,最后把事件和与之对应的处理器加入到事件总线中。

    事件的发布是通过调用事件总线的PublishEvent()来实现的。下面的例子就是在订单服务中通过消息中间件来监听来自外部的支付完成事件(PaymentCreatedEvent),收到后,把它转化成内部事件,然后发送到事件总线上,这样已经注册的事件处理器就能处理它了。

    eb := value.(ycq.EventBus)
    	subject := config.SUBJECT_PAYMENT_CREATED
    	_, err := ms.Subscribe(subject, func(pce event.PaymentCreatedEvent) {
    		cpm := pce.NewPaymentCreatedDescriptor()
    		logger.Log.Debug("payload:",pce)
    		eb.PublishEvent(cpm)
    	})
    

    那么事件是怎样被处理的呢?关键就在PublishEvent函数。当一个事件发布时,事件总线会把所有注册到该事件的事件处理器的Handle()函数依次调用一遍, 下面就是PublishEvent()的代码。这样每个事件处理器只要实现Handle()函数就可以了。

    func (b *InternalEventBus) PublishEvent(event EventMessage) {
    	if handlers, ok := b.eventHandlers[event.EventType()]; ok {
    		for handler := range handlers {
    			handler.Handle(event)
    		}
    	}
    }
    

    下面就是PaymentCreatedEventHandler的代码。它的逻辑比较简单,就是从Event里获得需要的支付信息,然后调用相应的用例来完成UpdatePayment()功能。

    type PaymentCreatedEventHandler struct {
    	Mouc usecase.ModifyOrderUseCaseInterface
    }
    func(pc PaymentCreatedEventHandler) Handle (message ycq.EventMessage) {
    	switch event := message.Event().(type) {
    
    	case *PaymentCreatedEvent:
    		status := model.ORDER_STATUS_PAID
    		err := pc.Mouc.UpdatePayment(event.OrderNumber, event.Id,status)
    		if err != nil {
    			logger.Log.Errorf("error in PaymentCreatedEventHandler:", err)
    		}
    	default:
    		logger.Log.Errorf("event type mismatch in PaymentCreatedEventHandler:")
    	}
    }
    

    我在这里用到了一个第三方库"jetbasrawi/go.cqrs"来处理Eventbus。Jetbasrawi是一个事件溯源(Event Sourcing)的库。事件溯源与事件驱动很容易搞混,它们看起来有点像,但实际上是完全不同的两个东西。事件驱动是微服务之间的一种调用方式,存在于微服务之间,与RPC的调用方式相对应;而事件溯源是一种编程模式,你可以在微服务内部使用它或不使用它。但我一时找不到事件驱动的库,就先找一个事件溯源的库来用。其实自己写一个也很简单,但我不觉得能写的比jetbasrawi更好,那就还是先用它把。不过事件溯源要比事件驱动复杂,因此用Jetbasrawi可能有点大材小用了。

    外部事件:

    外部事件的不同之处是它要在微服务之间进行传送,因此需要消息中间件。我定义了一个通用接口,这样可以支持不同的消息中间件。它的最重要的两个函数是publish()和Subscribe()。

    package gmessaging
    
    type MessagingInterface interface {
    	Publish(subject string, i interface{}) error
    	Subscribe(subject string, cb interface{} ) (interface{}, error)
    	Flush() error
    	// Close will close the decorated connection (For example, it could be the coded connection)
    	Close()
    	// CloseConnection will close the connection to the messaging server. If the connection is not decorated, then it is
    	// the same with Close(), otherwise, it is different
    	CloseConnection()
    }
    

    由于定义了通用接口,它可以支持多种消息中间件,我这里选的是"NATS"消息中间件。当初选它是因为它是云原生计算基金会(“CNCF”)的项目,而且功能强大,速度也快。如果想了解云原生概念,请参见"云原生的不同解释及正确含义"

    下面的代码就是NATS的实现,如果你想换用别的消息中间件,可以参考下面的代码。

    type Nat struct {
    	Ec *nats.EncodedConn
    }
    
    func (n Nat) Publish(subject string, i interface{}) error {
    	return n.Ec.Publish(subject,i)
    }
    
    func (n Nat) Subscribe(subject string, i interface{} ) (interface{}, error) {
    	h := i.(nats.Handler)
    	subscription, err :=n.Ec.Subscribe(subject, h)
    	return subscription, err
    }
    
    func  (n Nat) Flush() error {
    	return n.Ec.Flush()
    }
    
    func  (n Nat) Close()  {
    	n.Ec.Close()
    }
    
    func  (n Nat) CloseConnection()  {
    	n.Ec.Conn.Close()
    }
    

    “Publish(subject string, i interface{})”有两个参数,“subject”是消息中间件的队列(Queue)或者是主题(Topic)。第二个参数是要发送的信息,它一般是JSON格式。使用消息中间件时需要一个链接(Connection),这里用的是“*nats.EncodedConn”, 它是一个封装之后的链接,它里面含有一个JSON解码器,可以支持在结构(struct)和JSON之间进行转换。当你调用发布函数时,发送的是结构(struct),解码器自动把它转换成JSON文本再发送出去。“Subscribe(subject string, i interface{} )”也有两个参数,第一个与Publish()的一样,第二个是事件驱动器。当接收到JSON文本后,解码器自动把它转换成结构(struct),然后调用事件处理器。

    我把与消息中间件有关的代码写成了一个单独的第三方库,这样不论你是否使用本框架都可以使用这个库。详细信息参见"jfeng45/gmessaging"

    命令

    命令(Command)在代码实现上和事件(Event)非常相似,但他们在概念上完全不同。例如支付申请(Make Payment)是命令,是你主动要求第三方(支付服务)去做一件事情,而且你知道这个第三方是谁。支付完成(Payment Created)是事件,是你在汇报一件事情已经做完,而其他第三方程序可能会根据它的结果来决定是否要做下一步的动作,例如订单服务当收到支付完成这个事件时,就可以更改自己的订单状态为“已支付”。这里,事件的发送方并不知道谁会对这条消息感兴趣,因此这个发送是广播式发送。而且这个动作(支付)已经完成,而命令是尚未完成的动作,因此接收方可以选择拒绝执行一条命令。我们平常经常讲的事件驱动是松耦合,而RPC是紧耦合,这里指的是事件方式,而不是命令方式。采用命令方式时,由于你已经知道了要发给谁,因此是紧耦合的。

    在实际应用中,我们所看到的大部分的命令都是在一个微服务内部使用,很少有在微服务之间发送命令的,微服务之间传递的主要是事件。但由于事件和命令很容易混淆,有不少在微服务之间传递的“事件”实际上是“命令”。因此并不是使用事件驱动方式就能把程序变成松耦合的,而要进一步检查你是否将“命令”错用成了“事件”。在本程序中会严格区分它们。

    下面就是命令总线(Dispatcher)的接口,除了函数名字不一样外,其他与事件总线几乎一模一样。

    type Dispatcher interface {
    	Dispatch(CommandMessage) error
    	RegisterHandler(CommandHandler, ...interface{}) error
    }
    

    我们完全可以把它定义成下面的样子,是不是就与事件总线很像了?下面的接口和上面的是等值的。

    type CommandBus interface {
    	PublishCommand(CommandMessage) error
    	AddHandler(CommandHandler, ...interface{}) error
    }
    

    事件和命令的其他方面,例如定义方式,处理流程,实现方式,传送方式也几乎一模一样。详细的我就不讲了,你可以自己看代码进行比较。那我们可不可以只用一个事件总线同时处理时间和命令呢?理论上来讲是没有问题的。我开始的时候也是这么想的,但由于现在的接口(“jetbasrawi/go.cqrs”)不支持,如果要改的话需要重新定义接口,因此就暂时放弃了。另外,他们两个在概念上还是很不同的,所以在实现上定义不同的接口也是有必要的。

    事件和命令设计

    下面来讲解在设计事件驱动时应注意的问题。

    结构设计

    事件驱动模式与RPC相比增加的部分是事件和命令。因此首先要考虑的是要对RPC的程序结构做哪些扩充和怎样扩充。“Event”和“command”从本质上来讲是业务逻辑的一部分,因此应属于领域层。因此在程序结构上也增加了两个目录“Event”和“command”分别用来存放事件和命令。结构如下图所示。

    发送和接收的不同处理方式

    现在的代码在处理外部事件时,在发送端和接收端的方式是不一样的。

    下面就是发送端的代码(代码在支付服务项目里),整个代码功能是创建支付,完成之后再发布“支付完成”消息。它直接通过消息中间件接口把事件发送出去。

    type MakePaymentUseCase struct {
    	PaymentDataInterface dataservice.PaymentDataInterface
    	Mi                   gmessaging.MessagingInterface
    }
    func (mpu *MakePaymentUseCase) MakePayment(payment *model.Payment) (*model.Payment, error) {
    	payment, err := mpu.PaymentDataInterface.Insert(payment)
    	if err!= nil {
    		return nil, errors.Wrap(err, "")
    	}
    	pce := event.NewPaymentCreatedEvent(*payment)
    	err = mpu.Mi.Publish(config.SUBJECT_PAYMENT_CREATED, pce)
    	if err != nil {
    		return nil, err
    	}
    	return payment, nil
    }
    

    下面就是接收端的代码例子。是它是先用消息接口接收时间,再把外部事件转化为内部事件,然后调用事件总线的接口在微服务内部发布事件。

    eb := value.(ycq.EventBus)
    	subject := config.SUBJECT_PAYMENT_CREATED
    	_, err := ms.Subscribe(subject, func(pce event.PaymentCreatedEvent) {
    		cpm := pce.NewPaymentCreatedDescriptor()
    		logger.Log.Debug("payload:",pce)
    		eb.PublishEvent(cpm)
    	})
    

    为什么会有这种不同?在接收时,可不可以不生成内部事件,而是直接调用用例来处理外部事件呢?在发送时,如果没有别的内部事件处理器,那么直接调用消息中间件来发送是最简单的方法(这个发送过程是轻量级的,耗时很短)。而接收时可能需要处理比较复杂的业务逻辑。因此你希望把这个过程分成接收和处理两个部分,让复杂的业务逻辑在另外一个过程里处理,这样可以尽量缩短接收时间,提高接收效率。

    是否需每个事件都要单独的事件处理器?

    现在的设计是每个事件和事件驱动器都有一个单独的文件。我见过有些人只用一个文件例如PaymentEvent来存放所有与Payment相关的事件,事件驱动器也是一样。这两种办法都是可行的。现在看来,生成单独的文件比较清晰,但如果以后事件非常多,也许一个文件存放多个事件会比较容易管理,不过到那时再改也不迟。

    事件处理逻辑放在哪?

    对于一个好的设计来讲,所有的业务逻辑都应该集中在一起,这样便于管理。在现在的架构里,业务逻辑是放在用例(Use Case)里的,但事件处理器里也需要有业务逻辑,应该怎么办?支付事件处理器的主要功能是修改订单中的付款信息,这部分的业务逻辑已经体现在修改订单(Modify Order)用例里,因此支付事件处理器只要调用修改订单的MakePayment()函数就可以了。实际上所有的事件处理器都应该这样设计,它们本身不应包含业务逻辑,而只是一个简单的封装,去调用用例里的业务逻辑。那么可不可以直接在用例里定义Handle()函数,这样用例就变成了事件处理器?这样的设计确实可行,但我觉得把事件处理器做成一个单独的文件,这样逻辑上更清晰。因为修改订单付款功能你是一定要有的,但事件处理器只有在事件驱动模式下才有,它们是属于两个不同层面的东西,只有分开放置才层次清晰。

    源程序:

    完整的源程序链接:

    索引:

    1 “事件驱动的微服务-总体设计”

    2 “jetbasrawi/go.cqrs”

    3 “CNCF”

    4 “云原生的不同解释及正确含义”

    5 “NATS”

    6 “jfeng45/gmessaging”

    展开全文
  • java基于事件驱动之spring事件驱动

    千次阅读 2017-10-08 17:08:24
    事件驱动4个要素: 事件、事件源、注册中心(事件通道)、侦听器。 事件驱动和观察者模式本质一样,事件驱动是观察者模式的经典实现。 事件驱动的好处: 1、 无耦合的关联,事件发布者和订阅者不需要预先知道彼此的存在...

    事件驱动4个要素:

    事件、事件源、注册中心(事件通道)、侦听器。

    事件驱动和观察者模式本质一样,事件驱动是观察者模式的经典实现。

    事件驱动的好处:

    1、 无耦合的关联,事件发布者和订阅者不需要预先知道彼此的存在。

    2、 异步消息传递,业务逻辑和事件可以同步发生。

    3、 多对多的交互,发布订阅模型。




    定义事件类:这个类需要继承ApplicationEvent类。

    注册事件的监听器:监听类需要实现ApplicationListener接口,并将泛型设置为具体的事件类。

    事件生产者:需要实现ApplicationContextAware接口,通过applicationContext.publishEvent()发布事件。

    注册中心:spring初始化的时候将所有的监听器放入集合中,当发布事件后spring会遍历集合将监听这个事件的所有监听器取出来依次执行监听代码。


    具体业务

    传统的业务流程,飞机票预定和发送短信、发送邮件耦合在一起,在同一个线程中,会出现如果短信或者邮件服务异常会引起主业务异常。所以需要通过事件驱动方式解耦。



    基于spring的事件驱动不止在代码级别解耦,一定要在线程级别解耦。配置事件异步支持(多线程方式),并通过线程池来实现不同的监听器用不同的线程。





    展开全文
  • 事件驱动程序

    2018-12-13 10:00:20
    事件驱动程序侧重于事件。 最终,程序的流程取决于事件。 到目前为止,我们正在处理顺序或并行执行模型,但具有事件驱动编程概念的模型称为异步模型。 事件驱动的编程依赖于一直监听新来的事件的事件循环。 事件驱动...
  • 事件驱动和消息驱动

    千次阅读 2020-04-17 10:26:15
    事件驱动和消息驱动 消息驱动和事件驱动很类似,都是先有一个事件,然后产生一个相应的消息,再把消息放入消息队列,由需要的项目获取。他们的区别是消息是谁产生的 消息驱动:鼠标管自己点击不需要和系统有过多的...
  • 事件驱动VS消息驱动

    千次阅读 2017-03-10 22:20:15
    事件:按下鼠标,按下键盘,按下游戏手柄,将U盘插入USB接口,都将产生事件。比如说按下鼠标左键,将产生鼠标左键被按下的事件。 消息:当鼠标被按下,产生...1.要理解事件驱动和程序,就需要与非事件驱动的程序进行
  • 事件驱动原理

    千次阅读 2020-07-08 19:55:31
    事件驱动原理 简单地说 ,就是由于js代码只在一个线程上运行,他容易被阻塞。例如:复杂的算法运算,js进行复杂的dom操作等等。我们试想下如果一打开页面,就有大量IO请求,都是同步执行的话,页面会有多卡,所以...
  • 事件驱动模型

    千次阅读 2018-08-15 14:01:07
    事件驱动模型是一种响应事件模型,事件驱动就是在持续事务管理的过程中,由当前时间点上出现的事件引发的调动可用资源执行相关任务,解决不断出现的问题,防止事务堆积的一种策略。 事件驱动程序设计:是一种程序...
  • NODE事件驱动

    2017-11-29 10:06:09
    基于事件驱动的回调:通过事件驱动方式实现的回调叫做基于事件驱动的回调。如果和io有关就可以叫做基于异步回调的io。 事件循环:Event loop有大量的异步操作完成时需要调用相应回调函数,需要一种机制来管理执行...
  • 浅谈前端的数据驱动和事件驱动

    千次阅读 2021-04-24 00:04:24
    js,jq 事件驱动->vue,react 数据驱动 前端生态越来越大 思想也从事件驱动转为数据驱动 事件驱动 操作UI => 触发事件 => 响应处理 => 更新UI 数据驱动 操作UI => 触发事件 => 响应处理 => ...
  • 事件驱动与事件处理

    2018-03-12 17:43:37
    基本概述JS是采用事件驱动的机制来响应用户操作的,也就是说当用户对某个html元素进行操作的时候, 会产生一个事件,该事件会驱动某些函数来处理。PS:这种方式和Java GUI中的事件监听机制很像,都是需要注册监听,...
  • 消息驱动与事件驱动比较

    万次阅读 2017-07-24 14:35:52
    简单记录自己对于 消息驱动 和 事件驱动的理解。关于这二者的具体区别,于实现上来说,二者都是 注册绑定,然后交付执行。消息驱动模型在注册的时候仅仅注册一个回调函数作为处理函数。 而事件驱动模型则需要注册...
  • 事件驱动与状态驱动

    2019-09-27 12:16:51
    假设有这么一个场景,是用键盘的方向键去控制一个物体前进的方向,其中有down、up、right和left,大家很容易想到的是事件驱动,大概模型如下: /* Alien screen coordinates */ int alien_x=0, alien_y=0; ...
  • JavaScript事件驱动

    2016-04-01 20:37:12
    一、事件驱动简介 在用户通过浏览器实行交互性体验中,事件驱动这一概念,就因此而来。要想弄明白事件驱动,首先得清楚什么是事件和什么是事件处理程序这两个问题。 浏览者通过鼠标或键盘执行的操作称为事件,对此...
  • 事件驱动的一般步骤: 1、确定响应事件的元素 2、为指定元素确定需要响应的事件类型 3、为指定元素的指定事件编写相应的事件处理程序 4、将事件处理程序绑定到指定元素的指定事件 对于事件驱动模式,...
  • 事件驱动 基本概念 窗口/组件 事件 消息(队列) 事件响应(服务处理程序) 调度算法 进程/线程 非阻塞I/O 程序的执行可以看成对CPU,内存,IO资源一次占用 现代操作系统支持...
  • 事件驱动模型事件监听机制观察者模式案例1.Spring事件机制(事件监听机制)案例2.基于Springboot事件监听机制整合EventBus应用案例3. 事件监听机制 熟悉Spring的同学,spring提供了一套完整的事件监听机制。要了解...
  • 什么是事件驱动

    千次阅读 2018-03-07 23:23:58
    实际上,现代的程序大多是事件驱动的,比如多线程的程序,肯定是事件驱动的。早期则存在许多非事件驱动的程序,这样的程序,在需要等待某个条件触发时,会不断地检查这个条件,直到条件满足,这是很浪费cpu时间的。...
  • 文章目录spring事件驱动组成定义事件发送事件:publishEvent()方法实现观察者有2种方式注意事项利用@TransactionalEventListener实现监听事件时的事务隔离 spring事件驱动组成 spring事件驱动由3个部分组成 1、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 35,613
精华内容 14,245
关键字:

事件驱动