nginx是一个非常好的技术点,javaWeb可以用到。php更是与nginx有lnmp的组合说法。网站开发可以说nginx是炙手可热的一个技术点了。本篇博客把我对nginx的了解记载下来。后续对nginx的了解也会补充到这篇文章。文章会从如下几方面进行记载。
一.nginx是干什么用的?
Nginx是一款轻量级高性能的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器
(1)上述说的反向代理服务器是什么?
反向代理(Reverse Proxy)方式是指以代理服务器(nginx)来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器(真实处理请求的服务器),并将从内部网络上的服务器上得到的结果返回给Internet上请求连接的客户端。
比如你想访问一个PHP网站。此时你只需要访问nginx上配置的域名www.hello.com→(172.30.22.173)并监听listen一个port 80。 而你真实请求的资源来源是 192.3.4.5:9000这个端口给的。
而你打开网站访问的是www.hello.com你根本不知道192.3.4.5:9000 的存在。
正向代理指的是,一个位于客户端和原始服务器之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。
比如你想访问Google 被墙了。这时你可以访问谷歌在中国的代理服务器,然后代理服务器根据你的请求去Google服务器获取相应并返回给你。
二. nginx的优势是什么?
Nginx可以轻松处理高并发。而且占用的资源相对其他服务器较少,CPU消耗时间少。
性价比高。没有那么多线程之间切换的花销。
其他优势:延迟处理,SSL(安全套接字层),静态内容,压缩和缓存,连接和请求限制所需的关键功能,甚至可以从应用程序中传输HTTP媒体流层到更有效的Web服务器层。
三.Nginx的优势是怎么做到的?
Nginx 采用的是多进程(单线程) & 多路IO复用模型(epoll)。使用了 I/O 多路复用技术是”并发事件驱动“的服务器。
1.进程&线程&协程之间的关系是什么?
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,也是基本的执行单元,是操作系统结构的基础。
每个进程都有自己的独立内存空间,不同进程通过(管道,信号量,共享内存,socket,消息队列等)来通信。由于进程比较重量,占据独立的内存,所以进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
线程是操作系统能够进行运算调度的最小单位!!意味着一定是通过多线程来实现多核CPU的利用的。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
关于协程的一些额外描述:
一个线程可以多个协程,即一个内核线程对应多个用户协程(用户进程)。
进程、线程,都是有内核进行调度,有CPU时间片的概念,进行抢占式调度(有多种调度算法)
协程的调度与内核无关,完全有程序进行控制。只能进行非抢占式调度。
线程进程都是同步机制,而协程则是异步。
协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态
在实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。
操作系统为了程序运行的高效性,每个线程都有自己缓存Cache等数据,操作系统会自动实现数据的恢复操作,所以线程的切换非常耗性能。
但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。
以上:如果是单线程所述的异步都是由协程完成的。但是还是由一个CPU调度的,线程是CPU调度的最小单位。即使我们看到的现象是一个异步的现象。但底层实际上是一个线程不断切换协程来实现的。所以单核的切换速度并不变快反而更慢。
一个线程运行多个协程就像一个人推箱子,先推箱子A,推一会推箱子B,箱子AB的位置是被保留现场的。但实际上就一个人。多个人推多个箱子就像多线程多协程工作。
因此如果想利用多核CPU的优势,要么开启多线程,使多协程多线程一起跑。 要么开多进程,使多进程多协程一起跑,来达到异步的效果。
而Nginx就是采用的多进程多协程的事件驱动运行方式来实现的。
golang python 都是有协程的这个概念的。 golang的 goroutines Python的gevent 查了一下发现java居然也有,但是我没用过。。 看网上说java的Quasar也是可以的。
2.什么叫多路IO复用?
IO多路复用,将IO过程分为等待内核数据准备好和读取/写入内核两部分。一个IO函数监控多个IO可读/可写事件,任意1个IO设备准备好时返回(需要代码中轮询查看是哪个IO文件描述符,什么事件),再调用对应的read/write函数操作,减少不必要的等待时间,高效了很多。具体的实现有select、poll和epoll三种。
nginx采用的是epoll ,线程主函数不断接收请求。epoll的实现是通过事件驱动。
通过3个函数来实现,更加高效,当前使用也最多。在epoll_ctl中注册事件到epoll文件描述符中,把fd全部拷贝进内核,而不是在epoll_wait中重复拷贝。实现中内核通过为每个fd指定一个回调函数,当fd就绪时调用回调函数把就绪fd加入一个就绪链表,epoll_wait只需要查看这个就绪链表是否有就绪fd就可。可监控文件描述符数是系统可同时打开文件数
原型:
int epoll_create(int size); //返回epoll文件描述符,size表示要监听的数目 (这个返回的fd要记得close)
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); //epoll事件注册函数
epfd是epoll_create返回的值
op是动作:EPOLL_CTL_ADD/EPOLL_CTL_MOD/EPOLL_CTL_DEL分别表示:注册fd到epfd,修改已注册的fd,从epfd删除1个fd
fd是要监听的fd
event是告诉内核要监听什么事件
struct epoll_event{
__uint32_t events; //epoll events
epoll_data_t data; //user data variable
}
event是宏的集合:EPOLLIN可读;EPOLLOUT可写;EPOLLPRI紧急数据可读;EPOLLERR发生错误;EPOLLHUP被挂断;EPOLLET将EPOLL设置为边缘触发模式;EPOLLONESHOT只监听1次事件
int epoll_wait(int epfd, struct epoll_event *events, int maxevents