前言

 

1. nginx是一个优秀的事件驱动框架,nginx非常适合开发在传输层以TCP对外提供服务的服务器程序。基于nginx框架开发程序有5个优势:

* nginx将网络、磁盘及定时器等异步事件的驱动都做了非常好的封装,基于它开发将可以忽略这些事情处理的细节

* nginx封装了许多平台无关的接口、容器,适用于跨平台开发

* 优秀的模块化设计,使得开发者可以轻易的复用各种已有的模块,其中既包括基本的读取配置、记录日志等模块,也包括处理请求的诸如http、mail等高级功能模块

* nginx是作为服务器来设计其框架的,因此,它在服务器进程的管理上相当出色,基于它开发服务器程序可以轻松地实现程序的动态升级,子进程的监控、管理,配置项的动态修改生效等。

* nginx充分考虑到各操作系统所擅长的“绝活”,能够使用特殊的系统调用更高效地完成任务时,绝不会去使用低效的通用接口。尤其对于Linux操作系统,nginx不遗余力地做了大量优化。

 

第一部分

nginx能帮我们做什么

 

第一章 

研究nginx前的准备工作

 

1. 2012年,nginx荣获年度云计算开发奖,并成为世界第二大web服务器

 

1.1 nginx是什么?

1. web服务器的基本功能:基于REST架构风格,以统一资源描述符(uniform resource identifier, URI)或统一资源定位符(uniform resource locator, URL)作为沟通依据,通过http为浏览器等客户端程序提供各种网络服务。

2. nginx的竞争对手---Apache,Lighttpd, tomcat,jetty, IIS。他们都是web服务器,或者叫做WWW服务器。、

3. nginx是一个跨平台的web服务器,可运行在Linux,FreeBSD,Solaris,AIX,MacOS,Windows等操作系统上,并且它还可以使用当前操作系统特有的一些高效API来提高自己的性能

 

1.2 为什么选择nginx

1. nginx具备以下特点

* 更快

* 高扩展性

* 高可靠性

* 低内存消耗

* 单机支持10万以上的并发连接

* 热部署

* 最自由的BSD许可协议

 

nginx的核心优点还是他能够在支持高并发请求的同时保持高效的服务

 

1.3.1 Linux操作系统

1. 系统必须是内核为Linux 2.6 及以上版本的操作系统,因为Linux 2.6及以上内核才支持epoll,而在Linux上使用select或poll来解决事件的多路复用,是无法解决高并发压力问题的。

2. uname -a 查询Linux内核版本

 

1.3.3 磁盘目录

1. nginx编译阶段产生的中间文件存放目录

该目录用于放置configure命令执行后所产生的源文件及目录,以及make命令执行后生成的目标文件和最终连接成功的二进制文件。默认情况下,configure命令会将该目录命名为objs

 

 

 

 

 

第十四章

进程间的通信机制

 

1.  nginx由一个master进程和多个worker进程组成,但master进程或者worker进程中并不会再创建线程(nginx的多线程机制一直停留在测试状态,虽然不排除未来nginx可能发布支持多线程版本的可能性)

 

14.1

1. nginx框架使用了3种传递消息传递方式:共享内存、套接字、信号

2. nginx主要使用了3种同步方式:原子操作、信号量、文件锁

 

14.2 共享内存

1. 共享内存是Linux下提供的最基本的进程间通信方法,它通过mmap或者shmget系统调用在内存中创建了一块连续的线性地址空间,而通过munmap或者shmdt系统调用可以释放这块内存

使用共享内存的好处是当多个进程使用同一块共享内存时,在任何一个进程修改了共享内存中的内容后,其他进程通过访问这段共享内存都能够得到修改后的内容

2. nginx各进程间共享数据的主要方式就是使用共享内存,在使用共享内存时,nginx一般是由master进程创建,在master进程fork出worker子进程后,所有的进程开始使用这块内存中的数据

 

14.3 原子操作

1. 能够执行原子操作的原子变量只有整型,包括无符号整型ngx_atomic_uint_t和有符号整型ngx_atomic_t。这两种类型都使用了volatile关键字告诉C编译器不要做优化。

 

14.3.1 不支持原子库下的原子操作

 

14.3.3 自旋锁

1. 基于原子操作,nginx实现了一个自旋锁。

2. 自旋锁是一种非睡眠锁,也就是说,某进程如果试图获得自旋锁,当发现锁已经被其他进程获得时,那么不会使得当前进程进入睡眠状态,而是始终保持进程在可执行状态,每当内核调度到这个进程执行时就持续检查是否可以获取到锁。在拿不到锁时,这个进程的代码将会一直在自旋锁代码处执行,直到其他进程释放了锁且当前进程获取了锁后,代码才会继续向下执行。

3. 自旋锁主要是为多处理器操作系统而设置的,它要解决的共享资源保护场景就是进程使用锁的时间非常短,如果锁的使用时间很久,自旋锁会不太合适,那么它会占用大量的CPU资源

4. ngx_spinlock方法是非常高效的自旋锁,它充分考虑了单处理器和多处理器的系统,对于持有锁时间非常短的场景很有效率

 

14.4 nginx频道

1. ngx_channel_t频道是nginx master进程与worker进程之间通信的常用工具,它是使用本机套接字实现的。

int socketpair( int d, int type, int portocol, int sv[2]); 创建父子进程间使用的套接字

2. 当socketpair执行成功时,sv[2]这两个套接字具备下列关系:向sv[0]套接字写入数据,将可以从sv[1]套接字中读取到刚写入的数据;同样,向sv[1]套接字写入数据,也可以从sv[0]中读取到写入的数据。

通常,在父子进程通信前,会先调用socketpair方法创建这样一组套接字,在调用fork方法创建出子进程后,将会在父进程中关闭sv[1]套接字,仅使用sv[0]套接字用于向子进程发送数据以及接受子进程发送来的数据;而在子进程中则关闭sv[0]套接字,仅使用sv[1]套接字既可以接受父进程发来的数据,也可以向父进程发送数据

3. master进程如何监控、管理worker子进程?

每次派生一个子进程之前,也就是fork之前,都会先调用socketpair方法,在nginx派生子进程ngx_spawn_process方法中,会首先派生基于TCP的套接字,master正是通过socketpair产生的套接字发送命令的。

 

14.5 信号

1. Linux提供了以信号传递进程间消息的机制,nginx在管理master进程和worker进程时大量使用了信号。

2. 信号和信号量是完全不同的概念,信号量仅用于同步代码段,而信号则用于传递消息。一个进程可以向另外一个进程或者另外一组进程发送信号消息,通知目标进程执行特定的代码

./nginx -s reload

 

14.6 信号量

1. 信号量是用来保证两个或多个代码段不被并发访问,是一种保证共享资源有序访问的工具,使用信号量作为互斥锁有可能导致进程睡眠,因此,要谨慎使用。

2. int sem_init( sem_t* sem, int pshared, unsigned int value);

初始化信号量, pshared为0时表示线程间同步,为1时表示进程间同步。

3. int sem_destroy(sem_t *sem)

销毁信号量

4. int sem_post(sem_t *sem);

信号量的值加一

5. int sem_wait(sem_t * sem);

信号量的值减一

 

14.7 文件锁

1. Linux内核提供了基于文件的互斥锁

int fcntl(int fd, int cmd, struct flock* lock);

fd 是打开的文件句柄,cmd 表示执行的锁操作,lock描述了这个锁的信息。

2. nginx.conf文件中的lock_file配置项指定的文件路径,就是用于文件互斥锁的。

3. 对于文件锁,nginx封装了3个方法:ngx_trylock_fd实现了非阻塞进程,不会使进程进入睡眠状态的互斥锁

ngx_lock_fd 阻塞互斥锁

ngx_unlock_fd 释放互斥锁

 

14.8 互斥锁

总结:nginx是一个能够并发处理几十万甚至几百万个TCP连接的高性能服务器,因此,在进行进程间通信时,必须充分考虑到不能过分影响正常请求的处理