nodejs 异步回调
异步非阻塞
nodejs 的软肋是CPU密集型,但它适应IO密集型服务。事件驱动,异步非阻塞。个人理解,异步与非阻塞细分下来是两个概念。异步与同步强调的当前线程(或进程)的两个代码块执行是没有拓扑顺序的的;阻塞与非阻塞强调的是当前线程(或进程)的状态。在操作系统层面上来看,有五种网络模式方式可以进行I/O访问。(具体比较看这里)
- 阻塞 I/O(blocking IO)
- 非阻塞 I/O(nonblocking IO)
- I/O 多路复用( IO multiplexing)
- 信号驱动 I/O( signal driven IO)
- 异步 I/O(asynchronous IO)
异步、非阻塞两种方式都可以实现 nodejs 的 I/O并行。
非阻塞的方式,需要当前线程(或进程)不断的询问操作系统: “数据准备好了吗?”,直到系统回答ok。例如linux中,采用read 方法会不断重复检查 I/O的 状态;而select 和 poll 是利用的多路复用技术,检查的是文件描述符,在描述fd集合的方式上有所不同;epoll是对poll的改进。
理想的异步I/O应该是应用程序发起异步调用,而不需要进行轮询,进而处理下一个任务,只需在I/O完成后通过信号或是回调将数据传递给应用程序即可。盗图如下
参考资料
初探Node.js的异步I/O实现
Linux IO模式及 select、poll、epoll详解
libuv 和 nodejs 的关系是什么?其他语言(如:python)是如何实现异步I/O的?
libuv 介绍
nodejs底层实现事件驱动的库是libuv,盗图如下:
libuv源码传送门可以先下载源码,安装感受一下
git clone git@github.com:libuv/libuv.git
有两种方式可以使用GCC编译,autools或者GYP工具。下面在linux环境下使用autools作为实例
$ sh autogen.sh
$ ./configure
$ make
$ make check
$ make install
linux下autogen脚本里依赖automake和libtool工具。如果是ubuntu用户,所以使用之前可以提前使用以下命令,避免有库依赖问题
sudo apt-get install make automake libtool
make check出现问题
make check中途出现如下错误,但是可以不理会。从传送门1、传送门2,上可以该问题知道是网络本身问题,个人怀疑是墙的原因。
make install 成功
注意中间那段话
尝试写一个程序 helloworld.c
#include <stdio.h>
#include <uv.h>
int main(){
uv_loop_t *loop = uv_loop_new();
printf("now quitting.\n");
uv_run(loop, UV_RUN_DEFAULT);
return 0;
}
编译报错
g++ helloworld.c -o helloworld
/tmp/ccJnLb1A.o:在函数‘main’中:
helloworld.c:(.text+0x9):对‘uv_loop_new’未定义的引用
helloworld.c:(.text+0x28):对‘uv_run’未定义的引用
collect2: error: ld returned 1 exit status
从错误中可以知道是库的引用问题,从make install 结果来看可以加指定参数-L库路径/usr/local/lib;指定参数-l指定库名,尽管库名是libuv,但是链接的时候要去掉前面的lib,即-luv;
gcc helloworld.c -L/usr/local/lib/ -luv -o helloworld
编译成功但是运行错误
./helloworld
./helloworld: error while loading shared libraries: libuv.so.1: cannot open shared object file: No such file or directory
添加环境变量 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
ok,运行成功
补充一点:
LIBRARY_PATH和LD_LIBRARY_PATH的区别
1. Linux gcc编译链接时的动态库搜索路径(不会递归搜索)
1、gcc编译、链接命令中的-L选项;
2、gcc的环境变量的LIBRARY_PATH(多个路径用冒号分割);
3、gcc默认动态库目录:/lib:/usr/lib:usr/lib64:/usr/local/lib。
2.执行二进制文件时的动态库搜索路径
1、编译目标代码时指定的动态库搜索路径:用选项-Wl,rpath和include指定的动态库的搜索路径,比如gcc -Wl,-rpath,include -L. -ldltest hello.c,在执行文件时会搜索路径`./include`;
2、环境变量LD_LIBRARY_PATH(多个路径用冒号分割);
3、在 /etc/ld.so.conf.d/ 目录下的配置文件指定的动态库绝对路径(通过ldconfig生效,一般是非root用户时使用);
4、gcc默认动态库目录:/lib:/usr/lib:usr/lib64:/usr/local/lib等。
其中可以用 查看gcc编译连接搜索的路径
ld --verbose | grep SEARCH_DIR