网络编程学习-面向工资编程-读书笔记
2017-01-22 19:24
459 查看
接上一篇 http://www.cnblogs.com/charlesblc/p/6241926.html
来源:
https://zhuanlan.zhihu.com/p/20204159
Nginx在并发性能上比Apache强很多,如果是纯静态资源(图片、JS、CSS)那么Nginx是不二之选。
Apache有mod_php、在PHP类的应用场景下比Nginx部署起来简单很多。一些老的PHP项目用Apache 来配置运行非常的简单,例如Wordpress。
对于初学者来说Apache配置起来非常复杂冗长的类XML语法,甚至支持在子目录放置.htaccess 文件来配置子目录的属性。Nginx的配置文件相对简单一点。
Nginx的模块比较容易写,可以通过写C的mod实现接口性质的服务,并且拥有惊人的性能。 分支OpenResty,可以配合lua来实现很多自定义功能,兼顾扩展性和性能。
这里我们要着重讨论的是为什么Nginx在并发性能上比Apache要好很多。
非阻塞&事件驱动这么好,为什么大家没有一开始就采用这种方式呢? 原因有二:
非阻塞&事件驱动需要系统的支持,提供non-blocking版的整套 系统调用。
非阻塞&事件驱动编程难度较大,需要很高的抽象思维能力, 把整个任务拆解;采用有限状态机编程才能实现。
第二篇:
https://zhuanlan.zhihu.com/p/20311080
下面这段摘自wikipedia:
epoll是Linux内核的可扩展I/O事件通知机制。它设计目的只在取代既有POSIX select(2)与poll(2)系统函数,让需要大量操作文件描述符的程序得以发挥更优异的性能 (举例来说:旧有的系统函数所花费的时间复杂度为O(n),epoll则耗时O(1))。 epoll与FreeBSD的kqueue类似,底层都是由可配置的操作系统内核对象建构而成, 并以文件描述符(file descriptor)的形式呈现于用户空间。
epoll由下面几个系统调用组成:
为了解决高并发问题,大约在2000年,Jonathan Lemon在FreeBSD内核中实现了第一个版本 的kqueue,并在FreeBSD 4.1版本发布。之后FreeBSD在处理高并发的问题上一直领先于Linux。
在2002年,Linux的2.5.44版本(测试版本)首次加入了epoll机制。 直到2004年,在Linux的2.6.9版本epoll相关的API才稳定下来, Linux的高并发机制这才跟赶上FreeBSD。
各种操作系统在解决这个问题的办法上也是百花齐放:
技术操作系统kqueueUNIX (FreeBSD、MacOS)epollLinux 2.5.44/2.6.9IOCP (IO Completion Port)Windows NT 3.5, AIX, Solaris 10
第三篇
https://zhuanlan.zhihu.com/p/20315482?refer=auxten
由于POSIX标准的滞后性,事件通知API的混乱一直保持到现在, 所有就有libevent、libev甚至后面的libuv的出现为跨平台编程扫清障碍。
下面是WikiPedia对于libevent的介绍:
libevent是一个异步事件处理软件函式库,以BSD许可证发布。 libevent提供了一组应用程序编程接口(API),让程序员可以设定某些事件发生时所执行的函式,也就是说,libevent可以用来取代网络服务器所使用的事件循环检查框架。
由于可以省去对网络的处理,且拥有不错的效能, 有些软件使用libevent作为网络底层的函式库,如:Chromium(Chrome的开源版)、 memcached、Tor。
按照libevent的官方网站,libevent库提供了以下功能:当一个文件描述符的特定事件 (如可读,可写或出错)发生了,或一个定时事件发生了, libevent就会自动执行用户指定的回调函数,来处理事件。
libevent的高明之处还在于,它把fd读写、信号、DNS、定时器甚至idle(空闲) 都抽象化成了event(事件)。
ET/LT区别:
二者的差异在于Level Triggered模式下只要某个socket处于readable/writable状态, 无论什么时候进行epoll_wait都会返回该socket;
而Edge Triggered模式下只有某个socket从unreadable变为readable或 从unwritable变为writable时,epoll_wait才会返回该socket。
第四篇 https://zhuanlan.zhihu.com/p/20336461?refer=auxten 看专门的笔记:http://www.cnblogs.com/charlesblc/p/6341363.html
第五篇
TCP的“连接”仅仅是连接的两端对于四元组和sequence号的一种约定而已。
在有些文章里总会提到这名词、或者五元组,甚至七元组。 虽然我很反对摆弄名词秀专业,但我们也要防止被“秀”。 其实很容易理解:
四元组: 源IP地址、目的IP地址、源端口、目的端口
五元组: 源IP地址、目的IP地址、协议、源端口、目的端口
七元组: 源IP地址、目的IP地址、协议、源端口、目的端口,服务类型,接口索引
在 HTTP 1.0 中, 没有官方的 keepalive 的操作。通常是在现有协议上添加一个指数。 如果浏览器支持 keep-alive,它会在请求的包头中添加:
然后当服务器收到请求,作出回应的时候,它也添加一个头在响应中:
这样做,连接就不会中断,而是保持连接。
在 HTTP 1.1 中 所有的连接默认都是持续连接,除非特殊声明不支持。
然而,Apache 2.0 httpd 的默认连接过期时间是仅仅15秒,对于 Apache 2.2 只有5秒。
动静分离同时还会有一个额外的好处:
对于静态资源的请求,HTTP请求头里的Cookie等信息是没有用处的, 反而占用了宝贵的上行网络资源。用独立的域名存放静态资源后, 请求静态资源域名就不会默认带上主站域的Cookie,从而解决了这个问题。
如下表:
在IANA官方的标准里0号端口是保留端口。
然而,标准归标准,在UNIX/Linux网络编程中0号端口被赋予了特殊的涵义:
NAT技术的广泛应用也给很多应用带来了极大的麻烦: 处于NAT网络环境内的服务器很难被外部的网络程序主动连接,受这一点伤害最大的莫过于: 点对点视频、语音、文件传输类的程序。
当然我们聪明的工程师经过长时间的努力,发明了“NAT打洞”技术,一定程度上解决了此类问题。
如果一个端口正在被使用,无论是TIME_WAIT、CLOSE_WAIT、还是ESTABLISHED状态。 这个端口都不能被复用,这里面自然也是包括不能被用来LISTEN(监听)。
但这件事也不是绝对的,之前跟大家讲进程的创建过程提到过一件事: 当进程调用fork(2)系统调用的时候,会发生一系列资源的复制,其中就包括句柄。 所以,在调用fork(2)之前,打开任何文件,监听端口产生的句柄也将会被复制。
通过这种方式,我们就可以达成"多进程端口监听"。
我们大名鼎鼎的Nginx就是通过这种手法让多个进程同时监听在HTTP的服务端口上的, 这么做的好处就在于,当外部请求到达,Linux内核会保证多个进程只会有一个accept(2) 成功,这种情况下此端口的服务可用性就和单个进程存在与否无关。 Nginx正是利用这一点达成“不停服务reload、restart”的。
当时答得不太好,不太明白这个问题的关键点在哪里,后来逐渐明白了。
TCP的原理会导致这样的一个结果:
主动close socket的一方会进入TIME_WAIT,这个状况持续的时间取决于三件事:
TCP关闭连接的五次挥手包什么时候到达
SO_LINGER的设置
/proc/sys/net/ipv4/tcp_tw_recycle 和 /proc/sys/net/ipv4/tcp_tw_reuse 的设置
总之默认情况下,处于TIME_WAIT状态的端口是不能用来LISTEN的。 这就导致,Apache重启时产生80端口TIME_WAIT,进而导致Apache再次尝试LISTEN失败。
在很多开源代码里我们会看到如下代码:
有了上面这段神奇的代码,就不会出现上面的惨剧。但SO_REUSEADDR的作用不仅限于上述。
Linux 的 SO_REUSEADDR 设置为 1 有四种效果:
当端口处在TIME_WAIT时候,可以复用监听。
可以允许多个进程监听同一端口,但是必须不同IP。
这里说的比较隐晦,如果进程A监听0.0.0.0:80,B进程可以成功监听127.0.0.1:80, 顺序反过来也是可以的。
允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的IP地址不同。
使用UDP时候,可以允许多个实例或者单进程同时监听同个端口同个IP。
笔记见这篇: http://www.cnblogs.com/charlesblc/p/6341505.html
来源:
https://zhuanlan.zhihu.com/p/20204159
(一):演进——从Apache到Nginx
网上关于Apache和Nginx性能比较的文章非常多,基本上有如下的定论:Nginx在并发性能上比Apache强很多,如果是纯静态资源(图片、JS、CSS)那么Nginx是不二之选。
Apache有mod_php、在PHP类的应用场景下比Nginx部署起来简单很多。一些老的PHP项目用Apache 来配置运行非常的简单,例如Wordpress。
对于初学者来说Apache配置起来非常复杂冗长的类XML语法,甚至支持在子目录放置.htaccess 文件来配置子目录的属性。Nginx的配置文件相对简单一点。
Nginx的模块比较容易写,可以通过写C的mod实现接口性质的服务,并且拥有惊人的性能。 分支OpenResty,可以配合lua来实现很多自定义功能,兼顾扩展性和性能。
这里我们要着重讨论的是为什么Nginx在并发性能上比Apache要好很多。
非阻塞&事件驱动这么好,为什么大家没有一开始就采用这种方式呢? 原因有二:
非阻塞&事件驱动需要系统的支持,提供non-blocking版的整套 系统调用。
非阻塞&事件驱动编程难度较大,需要很高的抽象思维能力, 把整个任务拆解;采用有限状态机编程才能实现。
第二篇:
https://zhuanlan.zhihu.com/p/20311080
下面这段摘自wikipedia:
epoll是Linux内核的可扩展I/O事件通知机制。它设计目的只在取代既有POSIX select(2)与poll(2)系统函数,让需要大量操作文件描述符的程序得以发挥更优异的性能 (举例来说:旧有的系统函数所花费的时间复杂度为O(n),epoll则耗时O(1))。 epoll与FreeBSD的kqueue类似,底层都是由可配置的操作系统内核对象建构而成, 并以文件描述符(file descriptor)的形式呈现于用户空间。
epoll由下面几个系统调用组成:
int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event); int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
为了解决高并发问题,大约在2000年,Jonathan Lemon在FreeBSD内核中实现了第一个版本 的kqueue,并在FreeBSD 4.1版本发布。之后FreeBSD在处理高并发的问题上一直领先于Linux。
在2002年,Linux的2.5.44版本(测试版本)首次加入了epoll机制。 直到2004年,在Linux的2.6.9版本epoll相关的API才稳定下来, Linux的高并发机制这才跟赶上FreeBSD。
各种操作系统在解决这个问题的办法上也是百花齐放:
技术操作系统kqueueUNIX (FreeBSD、MacOS)epollLinux 2.5.44/2.6.9IOCP (IO Completion Port)Windows NT 3.5, AIX, Solaris 10
第三篇
https://zhuanlan.zhihu.com/p/20315482?refer=auxten
Libevent
由于POSIX标准的滞后性,事件通知API的混乱一直保持到现在, 所有就有libevent、libev甚至后面的libuv的出现为跨平台编程扫清障碍。
下面是WikiPedia对于libevent的介绍:
libevent是一个异步事件处理软件函式库,以BSD许可证发布。 libevent提供了一组应用程序编程接口(API),让程序员可以设定某些事件发生时所执行的函式,也就是说,libevent可以用来取代网络服务器所使用的事件循环检查框架。
由于可以省去对网络的处理,且拥有不错的效能, 有些软件使用libevent作为网络底层的函式库,如:Chromium(Chrome的开源版)、 memcached、Tor。
按照libevent的官方网站,libevent库提供了以下功能:当一个文件描述符的特定事件 (如可读,可写或出错)发生了,或一个定时事件发生了, libevent就会自动执行用户指定的回调函数,来处理事件。
libevent的高明之处还在于,它把fd读写、信号、DNS、定时器甚至idle(空闲) 都抽象化成了event(事件)。
ET/LT区别:
二者的差异在于Level Triggered模式下只要某个socket处于readable/writable状态, 无论什么时候进行epoll_wait都会返回该socket;
而Edge Triggered模式下只有某个socket从unreadable变为readable或 从unwritable变为writable时,epoll_wait才会返回该socket。
第四篇 https://zhuanlan.zhihu.com/p/20336461?refer=auxten 看专门的笔记:http://www.cnblogs.com/charlesblc/p/6341363.html
第五篇
TCP的“连接”仅仅是连接的两端对于四元组和sequence号的一种约定而已。
在有些文章里总会提到这名词、或者五元组,甚至七元组。 虽然我很反对摆弄名词秀专业,但我们也要防止被“秀”。 其实很容易理解:
四元组: 源IP地址、目的IP地址、源端口、目的端口
五元组: 源IP地址、目的IP地址、协议、源端口、目的端口
七元组: 源IP地址、目的IP地址、协议、源端口、目的端口,服务类型,接口索引
在 HTTP 1.0 中, 没有官方的 keepalive 的操作。通常是在现有协议上添加一个指数。 如果浏览器支持 keep-alive,它会在请求的包头中添加:
Connection: Keep-Alive
然后当服务器收到请求,作出回应的时候,它也添加一个头在响应中:
Connection: Keep-Alive
这样做,连接就不会中断,而是保持连接。
在 HTTP 1.1 中 所有的连接默认都是持续连接,除非特殊声明不支持。
然而,Apache 2.0 httpd 的默认连接过期时间是仅仅15秒,对于 Apache 2.2 只有5秒。
动静分离
为了规避上面说的对图片等静态资源的影响,大多数商业网站会启用独立的静态资源域名。 从而保证主站的动态资源请求和静态资源的请求不会互相挤占连接。动静分离同时还会有一个额外的好处:
对于静态资源的请求,HTTP请求头里的Cookie等信息是没有用处的, 反而占用了宝贵的上行网络资源。用独立的域名存放静态资源后, 请求静态资源域名就不会默认带上主站域的Cookie,从而解决了这个问题。
如下表:
第六篇 端口
https://zhuanlan.zhihu.com/p/20365900?refer=auxten0号端口
端口号里有一个极为特殊的端口,各种文档书籍中都鲜有记载,就是0号端口。在IANA官方的标准里0号端口是保留端口。
然而,标准归标准,在UNIX/Linux网络编程中0号端口被赋予了特殊的涵义:
如果在bind绑定的时候指定端口0,意味着由系统随机选择一个可用端口来绑定。
网络地址转换NAT
NAT是"Network Address Translation"的缩写,直译就是网络地址转换。 1990年代中期,为了应对IPv4地址短缺,NAT技术流行起来。NAT技术的广泛应用也给很多应用带来了极大的麻烦: 处于NAT网络环境内的服务器很难被外部的网络程序主动连接,受这一点伤害最大的莫过于: 点对点视频、语音、文件传输类的程序。
当然我们聪明的工程师经过长时间的努力,发明了“NAT打洞”技术,一定程度上解决了此类问题。
多进程端口监听
我们都有一个计算机网络的常识:不同的进程不能使用同一端口。如果一个端口正在被使用,无论是TIME_WAIT、CLOSE_WAIT、还是ESTABLISHED状态。 这个端口都不能被复用,这里面自然也是包括不能被用来LISTEN(监听)。
但这件事也不是绝对的,之前跟大家讲进程的创建过程提到过一件事: 当进程调用fork(2)系统调用的时候,会发生一系列资源的复制,其中就包括句柄。 所以,在调用fork(2)之前,打开任何文件,监听端口产生的句柄也将会被复制。
通过这种方式,我们就可以达成"多进程端口监听"。
我们大名鼎鼎的Nginx就是通过这种手法让多个进程同时监听在HTTP的服务端口上的, 这么做的好处就在于,当外部请求到达,Linux内核会保证多个进程只会有一个accept(2) 成功,这种情况下此端口的服务可用性就和单个进程存在与否无关。 Nginx正是利用这一点达成“不停服务reload、restart”的。
SO_REUSEADDR
有一个问题就是为什么有时候重启Apache会失败,报“Address already in use”?
当时答得不太好,不太明白这个问题的关键点在哪里,后来逐渐明白了。
TCP的原理会导致这样的一个结果:
主动close socket的一方会进入TIME_WAIT,这个状况持续的时间取决于三件事:
TCP关闭连接的五次挥手包什么时候到达
SO_LINGER的设置
/proc/sys/net/ipv4/tcp_tw_recycle 和 /proc/sys/net/ipv4/tcp_tw_reuse 的设置
总之默认情况下,处于TIME_WAIT状态的端口是不能用来LISTEN的。 这就导致,Apache重启时产生80端口TIME_WAIT,进而导致Apache再次尝试LISTEN失败。
在很多开源代码里我们会看到如下代码:
int reuseaddr = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int));
有了上面这段神奇的代码,就不会出现上面的惨剧。但SO_REUSEADDR的作用不仅限于上述。
Linux 的 SO_REUSEADDR 设置为 1 有四种效果:
当端口处在TIME_WAIT时候,可以复用监听。
可以允许多个进程监听同一端口,但是必须不同IP。
这里说的比较隐晦,如果进程A监听0.0.0.0:80,B进程可以成功监听127.0.0.1:80, 顺序反过来也是可以的。
允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的IP地址不同。
使用UDP时候,可以允许多个实例或者单进程同时监听同个端口同个IP。
第七篇 CAP
https://zhuanlan.zhihu.com/p/20399316?refer=auxten笔记见这篇: http://www.cnblogs.com/charlesblc/p/6341505.html
第八篇 sendfile
http://www.cnblogs.com/charlesblc/p/6341605.html相关文章推荐
- 网络编程学习-面向工资编程
- 《Web Service 编程 --用C#.NET 开发网络服务》北京希望出版社 我的学习笔记(第二章)(也就是书上抄了一写东西而已)
- 该学习网络编程了
- 初步学习C#网络编程,可惜不太顺利
- 学习VC++深入浅出——网络通讯编程
- c++网络编程学习笔记(3)
- 对前一段时间学习网络和多线程编程的总结
- 我的面向网络编程
- 网络编程C#篇(二):Socket面向连接简单实例
- 如何学习C++(面向对象和windows编程第一节课讲稿)
- 将继续深入学习网络编程
- Proxy源代码分析--谈谈如何学习linux网络编程 [转]
- Proxy源代码分析--谈谈如何学习linux网络编程
- 今日网络编程学习总结
- Java网络编程入门学习
- 孙鑫VC学习笔记:第十四讲 网络编程
- perl网络编程学习系列:Net:FTP
- 孙鑫VC学习笔记:第十四讲 (二) 网络编程
- Java 网络编程 学习
- Java 网络编程 TCP vs UDP -Java学习笔记(31)