0
点赞
收藏
分享

微信扫一扫

nginx【20】同步&异步 阻塞&非阻塞之间的区别

很多同学对:同步&异步 阻塞&非阻塞这样的两个概念不是很清楚;

在我看来,阻塞和非阻塞主要是指操作系统和底层的C库提供的方法或者一个系统调用,也就是说我们在调用这个方法的时候,这个方法可能会导致我的进程进入sleep状态,为什么会进入sleep状态尼?就是当前的状态不满足的情况下,操作系统主动的把我的进程切换到另外一个进程来使用当前的CPU了,那么这样就是一个阻塞方法,而非阻塞尼就是我们调用该方法永远不会因为当我们时间片未用完时把我们的进程主动切换掉;

而同步和异步尼则是指我们的调用的方式而言,就是我们的编码中写我们的业务逻辑的这样的一个角度;

接下来我们看阻塞&非阻塞 同步&异步 到底有一些什么样具体的区别?

nginx【20】同步&异步 阻塞&非阻塞之间的区别_nginx
在阻塞调用中,我们以accept为例,因为绝大部分程序在调用accept的时候, 它都是在使用阻塞socket的;在使用阻塞socket的时候尼,当我们调accept方法的时候,如果说我们所监听的端口所对应的accept队列,就是操作系统已经为我们做好了几个三次握手建立成功的socket,那么阻塞方法可能会立刻得到返回,而不会被阻塞,但是如果accept队列是空的,那么操作系统就会去等待新的三次握手的连接到达我们的内核中,我们才会去唤醒这个accept调用;这个时间往往是可控的,我们可以去设置这个阻塞socket最长的超时时间;如果没有达到也可以唤醒我们这个调用;所以这里的流程中就会导致我们的进程间的主动切换;nginx是不能容忍进程间的切换的;因为它并发的连接实在是太多了;

那么非阻塞调用有什么差别?
nginx【20】同步&异步 阻塞&非阻塞之间的区别_非阻塞_02
 我们还以accept为例,如果你使用非阻塞套接字,使用accept调用去执行的时候,如果accept队列为空,它是不等待立刻返回的,它返回的时候返回个什么尼?

叫EAGAIN错误,其实是一个错误码,所以这个时候尼,我们的代码会收到一个错误码,但这个错误码是一个特殊的错误码,需要我们的代码去处理它,如果我们再次调用非阻塞的accept,如果accept队列不为空尼?就会成功的将套接字从accept队列中移出并拷贝给进程返回给我们的代码,所以这里有一个很大的问题,就是由我们的代码决定当accept收到一个EAGAIN错误码时,我们究竟应该是等一会儿继续处理这个连接,比如sleep一下,还是先切换到其他的任务再处理,我这里是举的一个非常简单的accept例子;如果涉及到我们的业务特性,比如http复杂的子请求,主请求等等,实际上会导致我们的代码会非常复杂的,因此,非阻塞调用是我们底层实现;如果我们使用异步的方法去使用非阻塞调用是非常自然而然的;

我们来看下是怎样使用异步的方式来处理非阻塞连接的:
nginx【20】同步&异步 阻塞&非阻塞之间的区别_linux_03
这里举个反向代理的例子:

Nginx做反向代理的时候有一个特点:

它会去考虑到上游服务处理的能力相对是不足的,所以如果是一个有body的http请求,那么nginx会先把body接收完;再去向上游服务器发起连接;

那么我们看标准的异步调用这段代码:当我们收完header的时候,我们已经知道接下来向谁哪一台上游服务器去发起反向代理建立连接了,但是我需要先读取body;所以调用了一个 方法,​​ngx_http_read_request_body(r,ngx_http_upstream_init)​​ 这个方法就是一个标准的异步方法;

nginx【20】同步&异步 阻塞&非阻塞之间的区别_linux_04
它表达当我执行完​​​read_request_body​​​以后,再去回调​​post_handler​​这个方法;就是我们对上游服务器建立连接的方法;

nginx【20】同步&异步 阻塞&非阻塞之间的区别_非阻塞_05

所以当我们调用这个异步调用的时候,它其实意味着先把body收完,再调上游服务的方法,非常的复杂,难以理解;

我们再来看看于此相反的同步调用方法:

比如说openresty写一段LUA代码:

nginx【20】同步&异步 阻塞&非阻塞之间的区别_错误码_06
我现在要对redis建立连接:

因为我们建立连接使用的是TCP,那么TCP一样有三次握手,我们在基于nginx的​​openresty​​上,也是不能使用阻塞方法的;

但是你用异步方式非常复杂,同步方式可以​​new​​​ 一个​​redis​​​ 类以后,设置好连接的超时时间;我们就可以调用 ​​connect​​;

这个connect就是一个同步调用,但是它的里面走的是非阻塞方法;

所以我们在写LUA代码的时候,完全不用考虑是否成功的来回调用;

我们只需要简单的​​connect​​​ 收到响应值了,那么ok,如果没有收到ok 我们打印个​​ngx.say('failed:',err)​​就可以了;

因为在​​connect​​方法执行的过程中;当connect没有被满足(也就是没有收到redis发来的ACK响应)这个connect方法不会返回,但是也不会阻塞nginx的代码,这叫做同步调用代码,它使用的是非阻塞的一个方式;

如果不是在极端场景下,都会去使用如openresty或者nginx的​​JavaScript​​模块来使用同步编程的方式来达成我们的目的;这样的开发效率非常的高;


举报

相关推荐

0 条评论