这一系列文章主要分析nodejs中的核心库Libuv。
我的参考书:
朴灵的深入浅出nodejs
Jeffrey Richter的Windows核心编程
Anthony Williams的C++并发编程实战
暂定为四篇:
1) 征服之初识篇(背景基础以及重要的概念,图示,libuv的编译,例子) 2) 征服之进展篇(内部用到的c语言技巧以及QUEUE的使用) 3) 征服之高潮篇(线程池,iocp,同步,并发,线程间的通信等) 4) 征服之和谐篇(Libuv的初始化,主循环,主线程和线程池之间的和谐交往)
通过征服Libuv系列文章,目的是让大家了解:
1) C语言之美(语法简单,功能强大,贴近硬件,适合系统级别编程,有很多技巧) 2) 多线程与线程的同步技术 3) 线程池技术 4) windows IOCP技术 5) 了解与ms PPL, intel TBB以及libdispatch(ios gcd)之间的异同点和优缺点。 6) vs c++中多线程下的debug技巧
之所以称为了解,而不是掌握,是因为这些技术偏向于底层的系统编程,并不是一撮而就的,需要一定的时间和经历才能沉淀下来的。因此只能说是让大家了解。
为了简单期间,一些限制条件:
仅关注windows下的实现 仅重点分析libuv的通用cpu密集型计算的框架,了解cpu密集计算框架,实际上异步io就比较容易理解了 因为windows下,异步io通过IOCP(完成端口),会使代码实现非常简单,而效率非常高。
在写此篇blog时,突然感觉到,是不是应该将libuv库进行拆分,将核心部分代码抽离出来单独运行,这样的好处就是要分析的代码量要少很多,仅关注我们感兴趣的代码,有利于演示。
还有一个目的:
就是在简化精华版的基础上升级一下,看看是否能够实现任务的动态均衡(ms PPL库和intel TBB库都有动态均衡,Work Stealing的功能,libuv目前确定没有此功能,libdispatch目前没发现,源码正在阅读中,还没看到)。这个实现难度还是很大的,就当练练手吧,哈哈
我尝试一下吧,看看是否可行!
(本文写于2016年,经过这段时间研究,于2017-2-10放弃上面的拆分这个想法,实在是牵涉到的代码以及操作系统太多,还是果断放弃这个念想吧!)
前段时间,完成了一个微信项目,在后台选型过程中,花了将近一个月考察了java,php几个库,但最终却选择了nodejs,原因很简单:
1、java的配置实在是太麻烦了 2、php实在不熟悉,特别扭 3、nodejs使用js编程,我本人还是比较熟悉js的基础部分(不算es6.0 es7.0标准),并且npm真好用,实在是资源太丰富了,有时选择太多也是一种痛苦啊! 4、nodejs基于异步io技术,效率非常高。而且分布式部署简单。
通过无数次的比较,最终选定了令我非常满意的组合,感觉最起码少写了80%的代码。
framework: strongloop/loopback(被IBM收购了,只能说强大无比)
朴灵的: wechat / wechat-api / wechat-oauth
supersheep: wechat-pay
数据库: mongodb用于不需要事务处理的表 mysql用于支付的事务处理
因为没经验,在支付时需要事务处理,所以调整为使用mysql。不过从另外一个角度说明loopback的强大,支持多数据源的统一api操作。
angularjs1.x
jquery
总体而言,该前后台配套系统的开发非常令人愉悦。前台不好统计,但是微信后台,至少少写了80%代码!!
目前基于异步回调的开发非常盛行,例如ios中的gcd,android中的基于接口回调方式。如果有过这些开发经验,你会觉得nodejs的异步事件开发模型还是蛮熟悉的。
随着逐渐熟悉nodejs,深深的喜欢上了nodejs,因此更想深入的了解一下nodejs是如何实现的。
通过了解nodejs的代码结构,结合深入浅出nodejs第三章的异步io所示流程图,逐步验证其正确性。特别是看到libuv中具有深厚c语言功底的老鸟所写的代码,忍不住想与大家分享c的代码之美!!
官方介绍:
libuv is a multi-platform support library with a focus on asynchronous I/O. It was primarily developed for use by Node.js
由上面的介绍,我们可以知道:
1) 跨平台:windows linux unix都支持 2) 异步io:windows IOCP 、linux epoll、unix kqueue 3) 主要目的是用于nodejs,实际还有很多库或程序使用了libuv 例如目前风头正劲的跨平台.NetCore库(就是曾经大名鼎鼎的微软的asp.net)也是使用libuv作为核心 4) libuv不仅仅支持异步io操作,而且还具有一个强劲的线程池,用于支持多线程并行的cpu密集型操作。 这个才是我们重点要分析的模块
1) nodejs主要由google v8 javascript引擎和libuv组成 2) v8引擎绑定libuv实现的api,因此,既能使用ecma js标准语言来执行js代码,又能通过js调用libuv相关接口。 3) 由此可见,libuv本身是独立的c语言库,既可以直接使用c/c++来调用,也可以被绑定到c#(.NetCore)或者其他任何语言,例如java ,lua...... c/c++实现的库最大的好处就是能被各种其他编程语言所绑定和调用。 因为其他各种编程语言基本都是用c/c++来实现的,都留有接口与c/c++互调。
借用深入浅出nodejs(经作者同意)中的两张图来了解整个流程:
一定要明确的了解哪些事情是发生在主线程的,哪些事情是发生在线程池中的!!!
nodejs就是数据通过v8引擎(主线程:数据输入)传递给Libuv进行处理(线程池:数据处理—根据数据类型不同,io数据由IOCP[windows]线程池处理,通用计算则由自己实现的线程池处理),libuv处理好后通知v8引擎我已经完成了,你来进行完成处理(主线程:完成回调,信息输出)。
其实从上面的叙述可以了解到以下几点:
1) v8 javascript引擎是单线程的,数据的输入,信息的输出(完成回调)都是在主线程中处理。这一点以后在源码分析中我们可以验证,通过vs强大的debug功能,我们可以很清晰的看到具体代码到底是运行在哪个线程中。
2) 但是数据处理模块(libuv)不是单线程的,它根据数据请求类型是否是io请求(socket,文件读写或管道等)还是work请求(非io请求)。不同的请求使用不同的处理策略。例如io请求,在windows下用IOCP,在linux下用epool。而work请求,windows和linux下都是使用统一的,自己实现的线程池。而我们的源码分析就是要证明上面的描述。
5、visual studio编译及测试Libuv库:
1) 如何编译libuv库 2) 在测试libuv库的时候,如何解决各种链接错误 3) 重点是根据链接错误编号,通过msdn来定位问题、分析问题以及解决问题,掌握方法学是关键
编译:
1) 安装python2.,6或者2.7版本,并设置好环境变量。千万别安装3.0或以上版本。
目前很多跨平台库,都通过python脚本进行编译或引导,因此python是必装程序。、
2) 在cmd中:
cd到你libuv所在目录
并输入 git clone https://chromium.googlesource.com/external/gyp.git build/gyp
将google gyp系统克隆到libuv所在目录的build/gyp文件夹下面
由于GFW的关系,google网站无法访问,作为程序员,我想你应该有办法、
3) 运行libuv所在目录下的vcbuild.bat,生成visual studio解决方案。双击运行,然后F5编译调试,一切尽在掌握中!
如果你目前可以登陆google网站,则直接运行vcbuild.bat,如果没安装过gyp的话,自动会下载gyp构建系统。因此可以省略第二步
因为Libuv在windows中编译后的结果是一个静态链接库,那么我们需要重新建个exe工程,并将libuv.lib链接入exe这个程序,步骤如下:
1) 新建一个win32/控制台/空项目,名称例如:LibuvTest
2) 在源文件中添加main.cpp文件,实现以下简单代码并运行,F5,调试运行
3) 如果运行时报无法启动程序,则将exe设置为启动项:
4) 使用uv_wort_t进行cpu密集计算的测试代码,F5编译调试
F5运行后出现链接错误
5) LNK2019链接错误,表示相关的Lib没有被引入,观察相关错误内容,可以确定Libuv.lib没有被导入。
1、查找到Libuv.lib被编译到的目录
2、在LibuvTest项目上右击鼠标,选择属性菜单,弹出libuvTest Property Pages,然后选择Linker/input/Additional Dependencies界面
3、相对路径方式,添加libuv.lib库
参考
6) 添加好libuv.lib后,继续F5调试,仍旧有大量链接错误:
7) 解决LNK4098错误,该错误是由于LIBCMTD库引起的,我们忽略该库,具体如下:
8) 解决LNK2019错误,该错误前面也提到过,是由于没有引入相应的lib导致的。
1、查看具体的链接错误描述,可以看到和socket相关的
2、msdn是法宝,在msdn中查找closesocket函数,看看closesocket属于哪个lib库的?
3、按照前面添加libuv.lib方式(),添加Ws2_32.lib。或者也可以参考该文档使用另外一种方式:#pragma comment(lib,”path”)方式进行链接。
9) 周而复始,不断使用8)的方式,解决所有LNK2019链接错误
10) 最终需要的所有链接库
11) F5继续编译调试,正确运行输出结果
12) 至此为止,libuv的uv_work_t的Demo能够编译并且正确运行。我们也可以休息一下了! 可能太基础了,但是也是体现了解决问题的方法,一并记录下来,供大家参考。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。