Redis源码学习-6-事件驱动1

事件驱动编程

1 回顾

这里的回顾主要涉及到我自己的学习。如果是初学者的话,可以跳过,直接到2.

1.1 阻塞编程

一般情况下,我们在最开始学习网络编程的时候,这里主要指socket编程的时候。当我们调用read和write的时候,一遍都是使用阻塞模式下的read和write。

对于read来说,当stream中没有数据时,read就会发生阻塞。
对于write来说,当stream满了的时候,write就会发生阻塞。

那么问题来了,就以简单的聊天服务器举例。我的收端是不知道发端在什么时候发送信息的,当收端持有发端的socket后,难道要一直在这里无限等待么?那么其它的连接该什么办?

有人肯能会说多线程啊。
是的。
正如tinyhttpd上的做法那样。

socket 
bind 
listen
while(1)
{
    newConn = accept
    pthread_process(newConn);
}

每当我们收到一个客户端连接的时候,我们就启动一个线程来负责它。这被称作one-thread one-connection模式。但是

  1. 对于C或者C++来说,线程的启动和销毁开销对于我们补鞥呢接收。
  2. 线程对于C语言来说,虽然比进程轻量级,但是相对于goroutine等语言方面支持并发的来说,还是太重了。不可能去让一个线程只负责一个connection。
  3. 假设一个线程只负责一个connection,那么在A端给B端发送的内容没有发送完毕之前。B端是没有办法给A端回消息的。因为你需要一直在那里阻塞接收A端的内容,直到收够。

1.2 Tips:read和write的局限

看过《linux系统编程》的人都知道,read和write是不会保证你一定收或者发送多少个字节的。

《unix网络编程卷一》提供给了我们readN和writeN来保证我们一定收到/发送N个字节。

而在没有收够的这个期间,我们会一直停留在这里的while循环中。简单的伪代码如下。

int toRecv = N;
int nread = 0;
while(toRecv != 0)
{
    int n = read(dst, src + nread, toRecv);
    nread += n;
    toRecv -= n;
}

2. 事件驱动编程

什么是事件驱动编程?这里理解比较难点,可以先放着。
事件驱动编程简单来说,就是为对应的事件事先编好事件处理程序,当事件发生时,就会直接调用事件处理程序。
理解这里的关键是理解非事件编程。非事件编程实际上就是需要一个条件判断来不断判断事件有没有发送。就比如查看的你的工资有俩种方法。1. 不断在app上查看余额,来判断你的工资是否到账。这就是非事件编程。2. 短信通知,当工资到账时,直接会有短信通知,这就是事件编程。如果将人的精力比作CPU的话,显然非事件编程就比较耗费CPU。

简而言之,就是阻塞编程不好。很简单,那么就使用非阻塞编程+IO复用就可以了。

对于非阻塞编程来说,就是我read一次,不论stream中有无数据,不论我读到多少,我读完一次,我就结束了,将控制权交还给程序,让程序接着运行后面的事情。

那么问题来了?我怎么知道什么时候去读?
难道是

while(isRead)
{
    read_nonblock();
}// 典型的非事件编程

2.1 问题1:什么时候读

我们希望当有读事件到来的时候,有人能告诉我一下,对于程序员来说,只能祈求操作系统来告诉我们了。这就涉及到了IO复用。一般指的是select,epoll。
这里我就不谈如何使用这个函数了。总之,当有读事件/写事件到来的时候,即read-stream有数据,或者write-stream有空位的时候,我们就会被通知到。此时,我们就需要编写对应的事件处理程序。

2.2 问题2:读多少

这个问题实际上已经不是和事件驱动相关了,主要是TCP的分包问题了。

这里我们假设我们的协议为如下格式

字段功能
len(4字节)表示数据长度
data2进制数据

我们知道,我们可以通过len来判断我们接下来要收多少字节。问题是,如果发端在发送len字段的时候,就发送了1+3字节,或者2+2字节。怎么办?


很明显,解决这个问题肯定需要有缓冲区的设置,将我们第一次收到的1或者2字节收取下来。因此对于每个TcpConnection来说,其最起码要有一个接收缓冲区。

我们可以这样做,当读事件触发后,我们先read_nonblock,然后判断是否收到4个字节?,如果没有,就将其存入接收端缓冲区。如果收到4个字节,就读出len长度,然后为这个connection注册一个新的读事件。

实际上,数据endecode(encode/decode)层一般干的就是这事。

3. Reactor模式伪代码

reactor模式讲解

3.1 结构

3.2 简单运行

伪代码如下。

reactor = new Reactor;

Acceptor::void on_process_accept()
{
    new_fd = this.accept();
    new_handler = new Handler(new_fd);
    new_handler.register_handler(on_process_read());
    new_handler.register_handler(on_process_write());
    reactor.register_handler(new_handler);
}

void main()
{
    acceptor = new Acceptor;
    acceptor.register_handle(on_process_accept())
    reactor.register_handler(acceptor);
    reactor.loop(milliseconds(500));
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。、可私 6信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。、可 6私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。、可私 6信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值