Redis、memcached、Nginx组件

  • 一、网络编程需要关注的问题
  • 二、网络io职责
    • 1、操作io
    • 2、检测io
    • 3、epoll结构
  • 三、reactor原理
  • 四、Redis、memcached、Nginx 组件介绍
    • 1、Redis
    • 2、memcached
    • 3、Nginx
  • 五、总结

一、网络编程需要关注的问题

这里主要以TCP连接实现server为例,主要介绍网络编程中需要关注的问题,以及在每个状态下,io函数的作用。下面也主要从以下四个问题讲述

1、连接建立
2、连接断开
3、消息到达
4、消息发送

二、网络io职责

1、操作io

操作方式:
我们知道,文件描述符是网络编程的基础,对io的操作都是通过文件描述符fd实现的,所以在开始之前我们需要好好了解一下fd的属性,接下来我们看看下面这张图

请注意途中红圈圈出来的部分,当阻塞io调用read/recv函数时,此时如果内核缓冲区的数据还没有准备好,也就是协议栈还未收到数据或者是协议栈收到数据,但数据还没copy到内核缓冲区中,那么此时就会阻塞等待,知道内核中的read buffer中有数据才会返回,返回值代表的是实际返回的字节数,而不是我们在调用函数时传入的参数。

非阻塞io中调用read/recv函数时,如果内核read buffer没有数据,此时函数会直接返回并设置errno,errno为EWOULDBLOCK,如果有数据就是返回实际读取到的字节数,所以我们可以看到,阻塞io和非阻塞io的区别就在于数据的准备阶段。

那非阻塞io是如何处理连接建立、断开、消息送达和消息发送四个问题的呢?

建立连接:
连接建立分为主动连接,就是server作为客户端调用connect函数去连接数据库获取数据;被动连接就是被动接受处理客户端的链接请求。首先是主动连接,当server调用connect函数之后,此时就会开始TCP三次握手,因为设置了fd是非阻塞,此时connect返回值是-1。
errno是
EINPROGRESS,表示套接字为非阻塞套接字,且连接请求没有立即完成。此时TCP正在进行连接,我们需要通过epoll去注册读事件就可以判断连接是否可以建立成功了,建立成功后,connect返回-1,errno为EISCONN表示已经连接套接字了。
所以通过这里我们可以看出我们可以通过判断设置的errno来判断io是否建立连接。

被动连接:
被动连接发生之前,server需要进行socket、bind、listen操作,并将listenfd放到epoll树上注册读事件,此时就等待客户端连接了。当连接来临时,server端需要等待三次握手结束后accept返回值才不为-1,此时accept返回的值就是客户端锁持有的fd。

连接断开

主动断开:
当server端调用shutdown函数时会关闭读端或写端,那对应就会关闭客户端的写端或读端。比如server端shutdown掉读端,那客户端的写端就会被关闭,也就是客户端无法将数据发送到server端了,但是服务端依旧可以将数据发送给客户端,因为TCP通信是全双工的,关闭了读端是不会影响到另一端的。还有就是调用close,此时是全部关闭,既不能读也不能写。

被动断开:
什么时候是被动断开呢?就是当server段调用read/recv函数时,返回值是0,此时表明客户端的写端,服务端的读端关闭了。此时是可以继续调用write将未发送完的数据继续发送出去,然后根据返回值,和设置的errno判断,如果是EPIPE,这说明断开了连接。

图片中红线所在的地方是调用的read返回0,但是在下一次发送FIN,也就是调用close之前,这段时间是科技继续发送数据的。但如果调用write返回-1,并且errno为EPIPE表示服务端写端关闭。

消息到达:

消息到达表明我们需要调用read从内核的read buffer中读取数据,那么此时如果read返回-1
且errno设置为EWOULDBLOCK 表示此时read buffer中没有数据,调用失败,如果errno设置为EINTR表示read调用被其他中断打断,调用失败。那如果返回值大于0,就表示read成功。

消息发送:

消息发送就是server端调用write函数,此时根据设置的errno判断,如果是EWOULDBLOCK表示表示客户端的read buffer无法读,原因就是客户端调用shutdown关闭了读端,如果返回EINTR表示write调用失败,被其他中断打断,如果返回大于0,表示写成功,返回值是指真正写入缓缓从去的字节数,而不是调用write时传入的参数

2、检测io

io多路复用(epoll)是如何做到io检测的呢?
首先我们要明白,io多路复用知识把io检测出来而不去操作io,也就是io检测和操作io是分开的。首先需要socket、bind、listen,然后epoll_create、epoll_wait函数,然后遍历epoll_wait的返回值,这时大致的流程,我们来看看这个过程具体发生了什么。

当程序执行到epoll_wait这个函数时,会根据timeout参数决定是否阻塞、阻塞多长时间,如果此时客户端调用connect函数发起三次握手,在三次握手结束之后,这个节点会被放进accept队列,此时通过注册的写事件就可以检测io了,那为什么需要注册写事件而不是读事件呢?这时因为,当客户端主动连接时,此时服务器关注最后一次握手也就是客户端最后一次发送的ack包,此时会同时发送一个信号触发epoll树注册的写事件,这时候就说明该连接成功建立了。此时调用accept从accept队列取出节点进行处理,最后再将节点添加到epoll树上(调用epoll_ctl函数)。此时在下一次返回时就可以根据具体的读写事件进行io到的操作了。这就是当服务端作为客户端主动连接时发生的情况。那如果是服务端被动连接呢,此时我们主要注册监听的就是读事件了,因为,此时服务器是需要拿到第三次握手的数据的,也就是需要客户端主动将数据发送到服务器的read buffer,此时我们只要检测到读事件就表示连接建立成功了,这就是被动建立。

被动断开:

我们根据epoll返回的errno, 如果errno是EPOLLRDHUP,说明服务端的读端已经关闭了,如果是EPOLLHUP说明读写段全部关闭,这样我们就可以通过判断epoll的返回值和errno来检测io了

消息到达:

消息到达需要服务端通过epoll监听客户端fd的读事件,如果read buffer有数据,此时就会发送信号给epoll触发EPOLLIN事件,接下来就可以调用read函数,此时read函数执行的就是操作io的功能了。

消息发送:

消息发送我们通常检测的是fd的写事件,因为如果write buffer有空间,epoll就会检测出来然后出发写事件,此时调用write就会将数据写write buffer。

3、epoll结构

epoll_create 函数首先会建立一个红黑树用来存储监听的fd节点事件,一个双端队列用来存储发生事件的fd节点

epoll_ctl函数主要工作就是将注册好事件的fd挂载到epoll_create创建的epoll树上去

epoll_wait函数主要是监听fd 是否发生变化,并将发生变化的fd放到双端队列中去

调用epoll_create会创建一个epoll对象;调用epoll_ctll添加到epoll中的事件都会与网卡驱动程序建立回调关系,相应事件触发时会调用回调函数( ep_epoll_callback),将触发的事件拷贝到rdlist双向链表中;调用epoll_wait将会把rdlist中就绪事件拷贝到用户态中;

三、reactor原理

reactor 是通过io多路复用和非阻塞io搭配实现的,所有的io都交由多路复用检测,然后调用具体的io函数去处理fd。reactorhi异步事件的。

那rector为什么使用非阻塞io呢,这里需要放到具体的环境中去解释。
多线程环境:
多线程环境下,我们可以将一个listenfd放到多个epoll中去检测,这是支持的。然后连接请求到来并放进accept队里中时,会发送信号给所有的epoll,这种现象叫做epoll惊群。这是假如,第一个epoll调用accept从队列中取出fd进行连接建立处理并成功执行,此时accept队列是没有fd需要处理了,那如果此时是阻塞io,其它的epoll就会阻塞在accept函数这里等待执行。如果是非阻塞io,accept会直接返回-1 并设置errno,这就避免了阻塞情况。

边缘触发下:
边缘触发下,一次读事件的触发会read一次,这里一般都会一次把read buffer读空,因为,如果读不完,剩下的数据就需要在下一次触发读事件才能继续读。如果io是阻塞的,读完之后read buffer内没有数据,此时调用read会阻塞直到read buffer有数据,如果是非阻塞io,这里就会直接返回,不会阻塞。

io多路复用选用select时:
当某个socket接收缓冲区有新数据分节到达,然后select报告这个socket描述符可读,但随后,协议栈检查到这个新分节检验和错误,然后丢弃这个分节,这时候调用read则无数据可读,如果socket没有被设置nonblocking,此read将阻塞当前线程。

优点:
1、rector将io检测和处理解耦出来,分离了io的职责。
2、epoll的惊群会在协议栈通过加锁处理,不需要关注。
3、reactor是一个线程执行一次事件循环。

四、Redis、memcached、Nginx 组件介绍

1、Redis

环境:redis是一种key-value的内置内存型数据库,value支持多种类型;然后redis的命令处理是单线程的。

redis为什么使用单线程实现?
首先就是value支持多种数据结构,多种数据结构涉及到的加锁问题尤其麻烦,而且,redis的命令处理是单线程的,更没必要使用多线程。但在6.0版本之后也支持了多线程,具体实现下面会说。

redis如何处理reactor?

redis 是主线程持有一个reactor,然后reactor的epoll去检测io,当io触发时会根据事件类型去执行对应的数据解码,编码的工作,这里是将read操作和数据的decode、write和encode放到多线程环境处理,compute命令操作还是由主线程处理的。这也是redis针对redis做的优化。

2、memcached

坏境:
memcached是一种key-value的内存型数据库,但是value只支持单一数据结构,命令处理是多行的。

memcached为什么实用多线程处理reactor?
因为value仅支持单一数据结构,加锁简单直接,而且命令处理也是多行的,多线程也更有利于提高业务的并发程度

memcached是如何处理reactor的?
主线程的reactor负责接收连接,然后通过管道PIPE和其他线程通信,负载均衡的将具体的clientfd交由不同的线程的reactor处理,在每一个线程里去处理具体的业务逻辑、命令。这里是没有将数据的读取,decode、写和encode交由多线程处理的。

3、Nginx


Nginx使用的是多进程版本的reactor去检测处理io,socket、bind、listen之后就会执行fork产生子进程,这里的惊群问题是在用户层通过加锁进行处理的,这里是存在一个共享资源的,我们将锁加在这里,然后进程去争夺这把锁,谁争夺到了就去处理listenfd。

五、总结

1、网络编程需要关注建立、断开、消息到达和发送四个问题。
2、主动连接、被动连接、主动断开、被动断开时,相应的函数具体做了什么。
3、io函数是具有检测和操作两种功能的。
4、reactor的实现由单线程、多线程、多进程版本的

redis、memcached、Nginx组件中的TCP相关推荐

  1. redis,memcached,nginx网络组件

    目录 1 梳理reactor网络编程 2 编程细节 (返回值以及错误码) 连接的建立 连接的断开 消息的到达 消息发送完毕 网络 IO 职责 epoll编程 reactor的应用 3 redis,ng ...

  2. Redis 集群规范(中文稿)(MOVED错误码及ASK错误码

    引言? 这个文档是正在开发中的 Redis 集群功能的规范(specification)文档,文档分为两个部分: 第一部分介绍目前已经在 unstable 分支中实现了的那些功能. 第二部分介绍目前仍 ...

  3. JavaWeb笔记:第07章 MVC |EL |JST |Filter |Listener |JQuery |AJAX |Maven |JSON |Redis |Linux |Nginx

    JavaWeb笔记:第07章 MVC |EL |JST |Filter |Listener |JQuery |AJAX |Maven |JSON |Redis |Linux |Nginx 1. MVC ...

  4. ELK+redis搭建nginx日志分析平台

    ELK+redis搭建nginx日志分析平台 发表于 2015-08-19   |   分类于 Linux/Unix   |   ELK简介 ELKStack即Elasticsearch + Logs ...

  5. 数据字典在sga的哪一个组件中缓存_非功能性约束之性能(1)-性能银弹:缓存...

    在<什么是架构属性>一文中提到提高「性能」的主要方式是优化,而优化的其中一个主要手段就是添加缓存! 在软件工程里有这么一句话:「没有银弹」!就是说由于软件工程的复杂性,没有任何一种技术或方 ...

  6. Linux安装jdk,mysql,tomcat,redis和nginx

    Linux安装jdk,mysql,tomcat,redis和nginx 1. jdk 2. mysql 3. tomcat 4. redis 5. nginx 首先安装lrzsz: yum -y in ...

  7. 常见NoSQL的对比及使用场景(Redis,memcached,mongodb)

    转自:https://my.oschina.net/liyurong/blog/1921898 1. NoSQL NoSQL(Not Only SQL),泛指非关系型的数据库(mysql.oracle ...

  8. Vue组件中使用canvas实现蜂巢效果的一些尝试

    Vue组件中使用canvas实现蜂巢效果的一些尝试   前段时间,看到D3.js的官方网站的蜂巢效果,感觉不错,不过一直没有时间去实际的实现下,借这次机会,算是填了前面的坑~~,先来看看d3.js的效 ...

  9. 从源码和内核角度分析redis和nginx以及java NIO可以支持多大的并发

    有人询问我网上一篇关于"redis为什么单线程这么快"的文章,我建议他不要看了,因为redis是单进程不是单线程,后面的意见不用看了,文章质量肯定不会很好,他也说了自己看了很久源码 ...

最新文章

  1. 重新开始 2011/11/25
  2. 问号和星号 php,星号和问号两个通配符
  3. bzoj2753: [SCOI2012]滑雪与时间胶囊
  4. USACO1.1.2|贪婪的送礼者
  5. 6大设计原则之接口隔离原则
  6. [转载] Java Formatter toString()方法与示例
  7. InstallShield中通过修改注册表关闭Vista/Windows 7的UAC
  8. Data Guard 三种模式解释
  9. python 回溯法 子集树模板 系列 —— 17、找零问题
  10. java 删除子文件夹_Java删除文件夹及文件夹下的子文件夹和子文件
  11. 1.59TB《全球 12.5 米高程DEM for WeServer》发布
  12. ThinkPHP(TP框架)的归纳与总结(一)----基于TP开发手册
  13. Photoshop插件-证件照-白红蓝底-PS插件-脚本开发
  14. VMware下安装Windows ME
  15. linux mpeg4ip 编译,利用Linux实现MPEG4流媒体技术
  16. 购买地铁车票的规定如下: 乘1-4站,3元/位;乘5-9站,4元/位; 乘9站以上,5元/位。 输入乘坐人数(per_num)和乘坐站数(sta_num), 计算购买地铁车票需要的总金额,并将计算结果
  17. 北大计算机博士毕业难度,北京大学博士毕业要求
  18. 东芝发布全系列消费级硬盘
  19. 怎么查询上网帐号和上网口令_宽带上网账号和密码忘记了该怎么查询
  20. 磁盘调度算法笔记和练习题

热门文章

  1. 小程序UI设计(9)-文字排版
  2. idea提示Your idea evaluation has expired. Your session will be limited to 30 minutes
  3. 坦然地接受你所面对的每一个人
  4. python-爬虫-58同城
  5. jquery ajax实现聊天室,用jquery写了一个超简陋的ajax无刷新聊天室
  6. 前端页面实现树形结构
  7. 从徐庶弃备投曹 说领导该如何对待组员
  8. WWDC2016 观后杂感
  9. 初级php工程师掌握技能,php工程师必须掌握的职业技能(上)
  10. Nowcoder专项练习:网络基础(一)