2.2.2 redis,memcached,nginx网络组件

文章目录

  • 一、网络编程关注的问题
    • 1.连接建立
    • 2.连接断开
    • 3.消息到达
    • 4.消息发送
  • 二、网络IO的职责
    • 1.检测IO
    • 2.操作IO——阻塞IO与非阻塞IO
    • 3.IO多路复用——EPOLL
    • 4.reactor
    • 5.redis
    • 6.reactor的应用
    • 7.memcached
    • 8.nginx

一、网络编程关注的问题

1.连接建立

分为两种:
1.服务端处理接收客户端的连接;
2.服务端作为客户端连接第三方服务;
int clientfd = accept(listenfd, addr, sz);
// 举例为非阻塞io,阻塞io成功直接返回0;
int connectfd = socket(AF_INET, SOCK_STREAM, 0);
int ret = connect(connectfd, (struct sockaddr*)&addr, sizeof(addr));
connect检测功能:检测建立连接的状态
connect操作功能:具体连接成功
connect第一次调用返回 ENIPROGRESS(正在建立)

accept主要对三次握手时的全连接队列操作
accept检测功能:检测全连接队列里是否有未处理的连接信息
accept操作功能:把该结点取出,生成客户端fd,客户端ip地址和端口(五元组)

listen监听:服务器接收客户端的连接
listen(fd,backlog):在没有调用accept把数据取出前,三次握手结束时最多能堆积backlog大小的数据

2.连接断开

分为两种:主动断开和被动断开;

主动断开
close:关闭两个端
shutdown:关闭一个端,例如调用shutdown关闭客户端的读端,那么服务端对应的写端就关闭了,对应write = -1 && errno = EPIPE状态

被动断开:
read = 0:服务端的读端关闭
write = -1 && errno = EPIPE :服务端的写端关闭
有的服务器需要支持半关闭状态,需要知道具体是读端还是写端关闭
close(fd);
shutdown(fd, SHUT_RDWR);
// 主动关闭本地读端,对端写段关闭
shutdown(fd, SHUT_RD);
// 主动关闭本地写端,对端读段关闭
shutdown(fd, SHUT_WR);
// 被动:读端关闭
// 有的网络编程需要支持半关闭状态
int n = read(fd, buf, sz);
if (n == 0) {
    close_read(fd);
    // write()
    // close(fd);
}
// 被动:写端关闭
int n = write(fd, buf, sz);
if (n == -1 && errno == EPIPE) {
    close_write(fd);
    // close(fd);
}

3.消息到达

从读缓冲区中读取数据
read:
read = -1 && EWOULDBLOCK :说明读缓冲区没有数据
read = -1 && EINTR:说明 read 操作被取消
int n = read(fd, buf, sz);
if (n < 0) { // n == -1
    if (errno == EINTR || errno == EWOULDBLOCK)
        break;
    close(fd);
} else if (n == 0) {
    close(fd);
} else {
    // 处理 buf
}

4.消息发送

往写缓冲区中写数据
write:
write= -1 && EWOULDBLOCK :说明读缓冲区没有数据
write= -1 && EINTR:说明 read 操作被取消
int n = write(fd, buf, dz);
if (n == -1) {
    if (errno == EINTR || errno == EWOULDBLOCK) {
        return;
   }
    close(fd);
}
IO函数只能检测一条连接的就绪状态,以及操作一条连接的IO数据

二、网络IO的职责

1.检测IO

io 函数本身可以检测 io 的状态;但是只能检测一个 fd 对应的状态;io 多路复用可以同时检测多个 io 的状态;
区别是:io函数可以检测具体状态;io 多路复用只能检测出可读、可写、错误、断开等笼统的事件;

IO多路复用的职责:操作检测多个IO的就绪状态,但是从不操作具体的IO


A.连接建立
	1.接收连接
	socket  -> bind & listen -> listenfd 监听读事件
	读事件发生后才会知道客户端与服务器产生连接了,
	产生一个结点告诉epoll,触发相应的epoll后续流程
	
	2.主动连接(connect)
	在第三次握手时,客户端向服务器发送ACK包的同时给Epoll 发送信号;
	触发写事件,说明连接成功了

B.连接断开
	EPOLLRDHUP:说明此时服务端的读端关闭了
	EPOLLHUP:说明服务端的读写端全都关闭了
	
C.消息到达
	客户端给服务器发送消息,如何监听(读缓冲区是否有数据)
	监听客户端的fd ,客户端发送数据(向读缓冲区插入数据时)会给epoll发送信号,
	触发读事件,说明读缓冲区有数据了,接下来就可以调用read,操作IO
	
D.消息发送
	一般监听客户端fd的写事件
	write buffer 如果满,Epoll 监听写事件,如果write buffer 存在剩余空间,可写状态时,
	就会给epoll发送一个信号,触发写事件,用户就可以调用write写数据了
注:只有主动连接和消息发送时检测的是写事件,其他都是读事件。

2.2.2 redis,memcached,nginx网络组件_第1张图片
2.2.2 redis,memcached,nginx网络组件_第2张图片

2.操作IO——阻塞IO与非阻塞IO

只能使用 io 函数来进行操作;分为两种操作方式:阻塞 io 和非阻塞 io;
1.阻塞在网络线程;
2.连接的 fd 阻塞属性决定了 io 函数是否阻塞;
3.具体差异在:io 函数在数据未到达时是否立刻返回;

2.2.2 redis,memcached,nginx网络组件_第3张图片

// 默认情况下,fd 是阻塞的,设置非阻塞的方法如下;
int flag = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flag | O_NONBLOCK);
int n = read(fd,buf,sz);
int n = write(fd,buf,sz);
在数据准备阶段
阻塞时,对于读缓冲区,如果为空,调用read阻塞就会等待填充数据;
阻塞时,对于写缓冲区,如果不能写入数据,调用write阻塞就会等待直到可以写入数据;
非阻塞时,不管缓冲区里有没有数据,会立即返回。

3.IO多路复用——EPOLL

int epoll_wait(int epfd, struct epoll_event*
events, int maxevents, int timeout);
OP操作:通过一个系统调用操作 epoll创建的rb_tree
op:
 EPOLL_CTL_ADD //添加
 EPOLL_CTL_MOD //修改
 EPOLL_CTL_DEL //删除

2.2.2 redis,memcached,nginx网络组件_第4张图片

1.调用epoll_ctl 添加到 epoll 中的事件与网卡驱动程序建立回调关系;
相应事件触发时会调用回调函数(ep_poll_callback);
将触发的事件拷贝到 rdlist 双向链表(就绪队列)中;

2.调用 epoll_wait 将会把 rdlist 中就绪事件拷贝到用户态中(epoll_event 填充一个用户态数组用来接收就绪队列中的数据)

3. maxevents 为预期取出的事件
   int epoll_wait 函数返回实际取出的事件
   
4.  timeout 调节阻塞或非阻塞
    timeout = -1 为阻塞特性
	timeout  = 0 为非阻塞的特性
	timeout  = 1000,阻塞最多1s

4.reactor

1.io多路复用 :检测
2.非阻塞io :检测事件触发后,操作io
3.异步事件:read -> fd  > callback
4.reactor为什么搭配非阻塞io?
	a.多线程环境  可以将一个 listenfd 放到多个 epoll 去处理 epoll惊群,但只要一个accept调用成功
	b.边缘触发下  读事件触发后,read在一次事件循环中把read buffer 读空
	c.select bug:当某个socket接收缓冲区有新数据分节到达,然后select报告这个socket描述符可读,
	但随后,协议栈检查到这个新分解检验和错误,然后丢弃这个分节,这时候调用read则无数据可读,
	如果socket没有被设置nonblocking,此read将阻塞当前线程

5.redis

1.环境
kv、数据结构、内存数据库
命令处理是单线程的

2.redis为什么要使用单reactor?
命令处理是单线程的 ->  单reactor
操作具体命令事件复杂度比较低

3.redis怎么处理reactor?
	1.创建listenfd
	2. bind listen
	3. listenfd注册读事件
	4. 读事件触发的回调 accept
	5. clientfd 注册读事件
	6. 读事件触发,将会回调readQueryFromClient
4.redis针对reactor做了哪些优化
	1.read,decode 		记录日志
	2.encode,write 	获取排行榜记录

6.reactor的应用

单线程
2.2.2 redis,memcached,nginx网络组件_第5张图片
多线程
2.2.2 redis,memcached,nginx网络组件_第6张图片
多进程
2.2.2 redis,memcached,nginx网络组件_第7张图片

7.memcached

…未完待续

8.nginx

…未完待续

你可能感兴趣的:(C/C++后台开发,网络,redis,memcached)