本文将探讨如下几个问题:

  • Event-Driven架构风格的约束
  • EDA风格对架构属性的影响
  • Reactor架构模式
  • Reactor所解决的问题
  • redis中的EventDriven

从观察者模式到EDA风格

GOF的23种设计模式中,有一个观察者模式!个人觉得叫「监听模式」更合理!

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。

它和EDA风格有些类似!不过一个应用在代码关系层面;一个应用在架构上!

在EDA中,有三个必要组件:

  • 事件生产组件:对应到观察者模式中,就是引起Subject状态变化的对象。它引起Subject状态变化的动作就是事件。
  • 事件队列:对应到观察者模式中,就是Subject对象。事件生产者产生的事件,会到事件队列中。
  • 事件处理组件:处理事件生产组件所产生的事件。对应到观察者模式中,就是Observer对象。

EDA风格的约束

  • 「事件生产组件」产生事件,将其添加到「事件队列」中
  • 「事件处理组件」监听「事件队列」
  • 当「事件队列」中有自己能处理的事件时,「事件处理组件」将对该事件进行处理
  • 「事件处理组件」处理完事件后,可以将处理结果返回给「事件生产组件」,也可以不返回
  • 「事件生产组件」可以接收「事件处理组件」处理结果,也可以不接收

EDA风格可以细分为Mediator结构和Broker结构!

  • Mediator结构通过一个Mediator组件协调多个步骤间的关系和执行顺序
  • Broker结构则是通过松散的方式来组织多个步骤之间的关系和执行顺序

Mediator结构如下:

在Mediator结构中主要有四个组件:

  • 事件队列(event queue)
  • 事件中介(event mediator)
  • 事件通道(event channel)
  • 事件处理器(event processor)。

执行流程如下:

  • 「事件生产组件」产生事件,将其添加到「事件队列」中
  • 「事件中介」监听「事件队列」
  • 当「事件队列」中有事件时,「事件中介」根据具体的事件,将其拆分/组合为一步步的具体步骤
  • 将具体的步骤添加到对应的「事件通道」中
  • 「事件处理器」从「事件通道」中获取到事件,进行处理

Broker结构如下:

Broker结构中主要包括两个组件:

  • Broker:Broker可被集中或相互关联在一起使用,此外,Broker中还可以包含所有事件流中使用的事件通道。
  • 事件处理器

执行流程如下:

  • 「事件生产组件」产生事件,将其添加到「Broker」中
  • 「事件处理组件」监听「Broker」
  • 当「Broker」中有自己能处理的事件时,「事件处理组件」将对该事件进行处理
  • 「事件处理组件」处理完事件后,发送后续事件到「Broker」中
  • 其它「事件处理组件」接收到自己能处理的事件后,对该事件进行处理,处理完后,可能继续发送事件到「Broker」中
  • 当不再有后续事件时,所有步骤完成

EDA风格对架构属性的影响

  • 性能:由于EDA是个异步架构,对于不需要返回值的请求,它能明显的提高用户可感知的性能。
  • 扩展性:事件生产者和事件消费者松耦合,可以独立进化,可以方便的进行功能扩展。同时由于,可以方便的添加事件消费者,故而进一步提高了扩展性。
  • 伸缩性:事件生产者和事件消费者松耦合及可以方便的添加事件消费者,使得EDA有很好的伸缩性
  • 可维护性:事件生产者和事件消费者松耦合,且和一般的调用方式相比,在理解上有一定的难度,使得开发难度增加,但单元测试较方便。而由于EDA的异步性,使得集成测试比较麻烦。可维护性一般
  • 可运维性:事件生产者和事件消费者松耦合,可独立部署,可运维性相对较容易
  • 灵活性:高度可扩展,易于伸缩使得EDA有较高的灵活性

Reactor架构模式

Reactor架构模式,是EDA风格的一种实现!它是为了处理:

  • 高并发IO请求
  • 其中请求所包含的数据量不大
  • 每个请求处理耗时不长

Reactor模型,有四个组件:

  • Acceptor:Acceptor接受Client连接。属于EDA「事件队列组件」。
  • Channel:接收IO事件。属于EDA「事件队列组件」。
  • Reactor:监听Channel和Acceptor,当发生连接事件或IO事件时,将其派发给对应的Handler。属于EDA「事件队列组件」。
  • Handler:和一个Client通讯的实体,进行实际的业务处理。对应EDA的「事件处理组件」。

Client属于EDA的「事件生产组件」!

Reactor可以分为:单线程模型,多线程模型和主从Reactor模型

  • 单线程模型

Reactor轮询到消息后,就直接委托给Handler去处理,处理完后,再进行后面的轮询,即一个Handler处理完之后,才能进行下一个Handler的处理!如果某个handler耗时较长,就会阻塞后续的handler的执行!

  • 多线程模型

Reactor多线程模型就是将Handler中的IO操作和非IO操作分开,操作IO的线程称为IO线程,非IO操作的线程称为工作线程!这样的话,客户端的请求会直接被丢到线程池中,客户端发送请求就不会堵塞!

但是当用户进一步增加的时候,Reactor会出现瓶颈!因为Reactor既要处理IO操作请求,又要响应连接请求!为了分担Reactor的负担,所以引入了主从Reactor模型!

  • 主从Reactor模型

主Reactor用于响应连接请求,从Reactor用于处理IO操作请求!

详细内容可参考之前的博文《高性能Server---Reactor模型》

这里简单说下,为什么Reactor中,请求的数据量不能太大,请求处理时间为什么不能太长!

其实很好理解,如果请求数据量太大、处理时间很长!即使是主从Reactor模型,线程池也会被耗尽!耗尽后,导致后续的请求积压。

Reactor解决的是传统BIO模型下,Server并发处理请求的能力受限于系统所能创建的线程数的问题!

redis中的EventDriven

redis使用了「Event-driven programming」来处理指令,「Event-driven programming」是一种编程范式,也可以说是EDA的代码实现。redis中的实现,类似Reactor单线程模型!

事件驱动程序设计是一种计算机程序设计模型。与传统上一次等待一个完整的指令然后再做运行的方式不同,事件驱动程序模型下的系统,基本上的架构是预先设计一个事件循环所形成的程序,这个事件循环程序不断地检查目前要处理的信息,根据要处理的信息运行一个触发函数进行必要的处理。其中这个外部信息可能来自一个目录夹中的文件,可能来自键盘或鼠标的动作,或者是一个时间事件。

简单的看一下代码:

  1. // server.c
  2. int main(int argc, char **argv) {
  3. ......
  4. aeMain(server.el); // 进入事件监听轮询,轮询注册上来的文件句柄
  5. ......
  6. }
  7. // ae.c
  8. void aeMain(aeEventLoop *eventLoop) {
  9. eventLoop->stop = 0; // eventLoop相当于Reactor模式中的Reactor组件
  10. while (!eventLoop->stop) {
  11. if (eventLoop->beforesleep != NULL)
  12. eventLoop->beforesleep(eventLoop);
  13. aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
  14. }
  15. }
  16. int aeProcessEvents(aeEventLoop *eventLoop, int flags)
  17. {
  18. ......
  19. numevents = aeApiPoll(eventLoop, tvp);// select,epoll,evport,kqueue底层实现,获取事件
  20. ......
  21. // 将事件委托给具体的Handler去处理
  22. for (j = 0; j < numevents; j++) {
  23. aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
  24. int mask = eventLoop->fired[j].mask;
  25. int fd = eventLoop->fired[j].fd;
  26. int fired = 0;
  27. int invert = fe->mask & AE_BARRIER;
  28. if (!invert && fe->mask & mask & AE_READABLE) {
  29. fe->rfileProc(eventLoop,fd,fe->clientData,mask);
  30. fired++;
  31. }
  32. if (fe->mask & mask & AE_WRITABLE) {
  33. if (!fired || fe->wfileProc != fe->rfileProc) {
  34. fe->wfileProc(eventLoop,fd,fe->clientData,mask);
  35. fired++;
  36. }
  37. }
  38. if (invert && fe->mask & mask & AE_READABLE) {
  39. if (!fired || fe->wfileProc != fe->rfileProc) {
  40. fe->rfileProc(eventLoop,fd,fe->clientData,mask);
  41. fired++;
  42. }
  43. }
  44. processed++;
  45. }
  46. ......
  47. }
  48. // ae.h
  49. // aeFileEvent的结构如下,在构建时,就将处理程序赋给了其中的aeFileProc
  50. // 在上面的轮询中,直接取出来执行即可
  51. typedef struct aeFileEvent {
  52. int mask; /* one of AE_(READABLE|WRITABLE|BARRIER) */
  53. aeFileProc *rfileProc;
  54. aeFileProc *wfileProc;
  55. void *clientData;
  56. } aeFileEvent;

下面以我们启动redis-server和redis-cli这个流程,来简单的说明下redis的执行流程:

  1. //redis-server启动后,进入轮询状态,等待事件
  2. //启动时构建了一个tcpHandler,用于处理客户端连接
  3. // server.c
  4. for (j = 0; j < server.ipfd_count; j++) {
  5. if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
  6. acceptTcpHandler,NULL) == AE_ERR)
  7. {
  8. serverPanic(
  9. "Unrecoverable error creating server.ipfd file event.");
  10. }
  11. }
  12. //redis-cli启动后,eventLoop轮询到连接事件,触发tcpHandler,根据获取到的文件句柄,构建aeFileEvent,同时设置readQueryFromClient Handler,用于处理后续的客户端的指令
  13. client *createClient(int fd) {
  14. ......
  15. if (aeCreateFileEvent(server.el,fd,AE_READABLE,
  16. readQueryFromClient, c) == AE_ERR)
  17. {
  18. close(fd);
  19. zfree(c);
  20. return NULL;
  21. }
  22. ......
  23. }
  24. // 客户端发送指令后,触发readQueryFromClient Handler,来处理指令
  25. // 处理完后,等待下一个指令

参考资料

EDA风格与Reactor模式的更多相关文章

  1. Reactor模式解析——muduo网络库

    最近一段时间阅读了muduo源码,读完的感受有一个感受就是有点乱.当然不是说代码乱,是我可能还没有完全消化和理解.为了更好的学习这个库,还是要来写一些东西促进一下. 我一边读一边尝试在一些地方改用c+ ...

  2. Java进阶知识点5:服务端高并发的基石 - NIO与Reactor模式以及AIO与Proactor模式

    一.背景 要提升服务器的并发处理能力,通常有两大方向的思路. 1.系统架构层面.比如负载均衡.多级缓存.单元化部署等等. 2.单节点优化层面.比如修复代码级别的性能Bug.JVM参数调优.IO优化等等 ...

  3. Reactor 模式的简单实现

    Reactor 模式简单实现 在网上有部分文章在描述Netty时,会提到Reactor.这个Reactor到底是什么呢?为了搞清楚Reactor到底是什么鬼,我写了一个简单的Demo,来帮助大家理解他 ...

  4. NIO及Reactor模式

    关于Nio Java NIO即Java Non-blocking IO(Java非阻塞I/O),是Jdk1.4之后增加的一套操作I/O工具包,又被叫做Java New IO. Nio要去解决的问题 N ...

  5. Java进阶(五)Java I/O模型从BIO到NIO和Reactor模式

    原创文章,同步发自作者个人博客,http://www.jasongj.com/java/nio_reactor/ Java I/O模型 同步 vs. 异步 同步I/O 每个请求必须逐个地被处理,一个请 ...

  6. 什么是Reactor模式,或者叫反应器模式

    Reactor这个词译成汉语还真没有什么合适的,很多地方叫反应器模式,但更多好像就直接叫reactor模式了,其实我觉着叫应答者模式更好理解一些.通过了解,这个模式更像一个侍卫,一直在等待你的召唤,或 ...

  7. 知识联结梳理 : I/O多路复用、EPOLL(SELECT/POLL)、NIO、Event-driven、Reactor模式

    为了形成一个完整清晰的认识,将概念和关系梳理出来,把坑填平. I/O多路复用 I/O多路复用主要解决传统I/O单线程阻塞的问题.它通过单线程管理多个FD,当监听的FD有状态变化的时候的,调用回调函数, ...

  8. Reactor模式通俗解释

    Reactor这个词译成汉语还真没有什么合适的,很多地方叫反应器模式,但更多好像就直接叫reactor模式了,其实我觉着叫应答者模式更好理解一些.通过了解,这个模式更像一个侍卫,一直在等待你的召唤,或 ...

  9. ACE - Reactor模式源码剖析及具体实现(大量源码慎入)

    原文出自http://www.cnblogs.com/binchen-china,禁止转载. 在之前的文章中提到过Reactor模式和Preactor模式,现在利用ACE的Reactor来实现一个基于 ...

随机推荐

  1. SPOJ:COT2 Count on a tree II

    题意 给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数. n=40000,m=100000 Sol 树上莫队模板题 # include <bits/stdc++.h ...

  2. BZOJ2882: 工艺(后缀数组)

    题意 题目链接 Sol 直接把序列复制一遍 后缀数组即可 在前\(N\)个位置中取\(rak\)最小的输出 #include<bits/stdc++.h> using namespace ...

  3. 关于 class 的命名

    class名称中只出现小写字符和破折号 使用有组织或目的明确的名称,不使用表现形式 基于最近的的父class 作为新class的前缀 使用 .js-* 来标识行为,并且不要将这些class包含到css ...

  4. 【Android】修改包名

    工程写的差不多了才发现原来用的包名还是自己尝试性的进行写代码的时候用到的.但apk的发布,google map api的申请等等方面都需要用到一个比较规范的包名.这就涉及到修改包名的问题. 包名一开始 ...

  5. 使用qt帮助 查看样式表stylesheet的帮助文档

    QCreactor帮助文档中搜索的关键字 Qt Style Sheets Examples        有所有控件的样式例子 Qt Style Sheets Reference      控件的所有 ...

  6. centos7主机名的修改

    在CentOS中,有三种定义的主机名:静态的(static),瞬态的(transient),和灵活的(pretty).“静态”主机名也称为内核主机名,是系统在启动时从/etc/hostname自动初始 ...

  7. typeof操作符和instanceof操作符的区别 标签: JavaScript 2016-08-01 14:21 113人阅读 评论(

    typeof主要用于检测变量是不是基本数据类型 typeof操作符是确定一个变量是字符串.数值.布尔类型,还是undefined的最佳工具.此外,使用typeof操作符检测函数时,会返回"f ...

  8. 解决yii2.0里url重写引用js路径问题(@web/的用法)

    在实际项目中,为了seo优化,使用了伪静态,开启了url重写

  9. Oracle Fusion Middleware Supported System check,jdk,java .etc requirements

    http://www.oracle.com/technetwork/middleware/ias/downloads/fusion-certification-100350.html 在oracle官 ...

  10. June 19th 2017 Week 25th Monday

    Everyone is dissatisfied with his own fortune. 人对自己的命运总是感到不满足. We always want more, even when we hav ...