疑难杂症!!!请高手指教!!!如果理解NODEJS是单线程????
发布于 5 年前 作者 flftfqwxf 10326 次浏览 最后一次编辑是 4 年前 来自 问答

新手初学,关于NODEJS单进程有几个疑问,请大侠些指教。 1)许多资料都说明,NODEJS为单进程,故如果项目中一个页面报错,整个项目都会挂掉,在本地试过,当一个页面出错时,会【exited with code 1】。那如果避免这种一个页面错误,而引进的全站挂的情况,是否有强制容错的机制,或者捕获错误,而不会【exited with code 1】。 2)个人理解NODEJS的单进程,是指个人写的代码为单进程进行,而不是指整个NODEJS服务为单进程,关于此点,与其他的语言【比如PHP】,有何不同??是否是指其他的语言,可以通过配置,而使用多进程,或者可以在代码里使用多个子进程来实现某些功能?? 3)基于第二点个人理解,NODEJS的child_process 算不算多进程的一种实现??

请大家指教!!!谢谢

33 回复

1)有 try catch 2)php 来一个request 就要一个线程。所以一般性能的电脑,比如普通虚拟机可以同时处理 10k 个请求。http://www.kegel.com/c10k.html。 这个链接有点老,不过你懂这个意思 3)node 把那些耗时的io也开新线程的。

process.on('uncaugthException', function(err) {
  console.log(err);
});

第一个问题可以用错误捕获,try/catch。还可以用pm2等工具监控,自动重启。 不建议捕获全局的uncaugthException,有错误即时退出重启,不要让错误累计,尤其是有些致命性错误必须重启。

child_process 可以实现多进程。你还可以用cluster来实现,主进程可以监控子进程的状态,如果发现子进程死掉可以立即重启。 跟PHP比,node.js的进程建立一定数量之后,只要不发生错误,进程一直存活的。主进程监听某个端口,来了请求,转到某个进程上处理,进程内部是用异步来解决大量连接的问题。PHP是来一个请求就新开一个进程,处理完了之后就进程就结束了,大量连接就需要大量的进程来应对,进程的开启过程和维护进程本身都需要大量的资源,所以PHP这种模型在应对大量请求时比较无奈。 node.js这种模式可以通过CPU绑定,SO_REUSEPORT等选项来提升服务器效率,淘宝的Tengine加入SO_REUSEPORT特性,据他们测试,多进程效率比原来nginx强两倍多。node.js的使用的策略跟nginx是一样的,都是异步多进程模型,nginx还对静态文件做了优化,可以直接通过sendfile()系统调用直接从硬盘发送到网卡(Zero Copy),在理论上,node.js的静态性能是比不过nginx的。

另外建议楼主也去了解下febjs,这个项目本身的代码并不是很优雅,完全是工程的引导的产物,但是他对异步的理解能够拓宽我们的视野,也许node.js这种纯callback的模式不是最好的,即便是generator也不能根本改变这个问题。

server.listen() : 调用OS
	Socket()
	Bind()
	Listen()
	for (;;):
	    Accept()
		Fork()       # on('request')
		Read()
		Send()
		Close()
		Exit()

你可以如此理解

@coordcn 多谢,那既然能实现多进程,为什么又把NODEJS称为单进程的??是谬论??

多进程不是天然的,是要自己实现的,默认情况下的确是单进程的,这种情况下服务器稳定性很不好,也不能充分利用多核的性能。 tulayang的代码就解释了PHP运行的模式,nodejs不是这种模式,进程用完是不退出的,一种在做事件循环或者等待事件。

@coordcn

我想,你没看明白。 我写的是底层C语言编写的操作系统Socket握手通信的过程。 任何一种非C语言,最终都是这种模式。 C在底层撑起了Socket核心。

PHP和Nodejs的区别:

PHP 实例 - C socket PHP 实例 - C socket PHP 实例 - C socket

Nodejs实例 - C socket C socket C socket

Nodejs比PHP要节省到Socket建立的内存.

@tulayang 你确定node.js每次accept都需要fork?

@coordcn

不是node.js fork, 是C要fork,看清楚了

@tulayang 内核层面我不清楚,但是我真没注意到用户层每次accept都需要fork的,node.js恐怕没有这方面的c代码。node.js的多进程是在javascript层面实现的,是不存在每次accept都fork出一个进程来的,如果来一个客人就fork一个进程,那是PHP的做法,这种做法稳定有余,但性能不足,所以我认为node.js是没必要这么做的。建议你查看下node.js和libuv的代码,看看到底是不是每次accept都需要fork的。

@coordcn @tulayang 对你们说的这些不太懂,还有个问题,NODEJS是单进程,那当一个WEB项目在访问的时候,如果其中某个静态资源挂掉了,或者阻塞了,是不是也会影响整个页面的加载

@flftfqwxf 这种问题你应该先写个程序自己验证下,别人的经验太廉价,也不一定正确,还是自己实践为好。 我个人认为只要进程没有挂掉,对其他处理是没有影响的,顶多客户端请求资源超时了。但是这种情况最好做错误处理。 你写点代码试验下就清楚了。

@coordcn

nodejs需要linux windows unix mac支持是不是?

为什么需要linux windows unix mac支持???

@tulayang 你确定需要fork和exit?要看到确定的代码后再下结论阿。

@coordcn

你只需要知道nodejs底层是这么运作的就可以了

  • accept fork是c函数
  • 这些函数是底层调用的,跟你没关系,也没给你提供调用接口
  • 分清c操作系统和nodejs

@tulayang io.js src/tcp_wrap.cc 307行 ,如果不放心的话,还可以继续读libuv的源代码,看看到底accept之后要不要fork。

@coordcn

你可垃圾吧到吧,还用得着读libuv,写过C socket,懂点TCP常识的都知道为什么fork。 你知道fork什么意思吗?

自己好好查查accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len),弄清楚什么是accept。别再给我发C++,nodejs的代码链接。

@tulayang 我觉得你还是有必要读一下libuv源代码的,代码会告诉你为什么不再需要fork。

@coordcn

跟你真是对牛弹琴

@tulayang 读了源代码才能分清楚谁是牛谁是琴。太过浮躁和自以为是是不好的。

@coordcn

孩子,别跟我说源代码。你那些源代码调用的都是操作系统给你封装的库,你离真正的源代码还早着呢。

uv_accept(handle, client_handle)是调用的给你封装的库,懂?

accept(fd, &addr, &len)是什么都分不清楚,跟我在这扯源代码。

@tulayang 那就把你真正的源代码亮出来吧,技术这个东西,需要交流分享的,我非常期待你的分享。你就假设我一点都不懂,仔仔细细细的讲,这里或许也有像我一样什么都不懂的,你讲了其他人也会受益。

@tulayang 继续朝里面读吧,到底有没有fork你自然心里就有数了。

@coordcn

你懂什么叫库吗?

这就好比我在聊Nodejs的read()函数,你非跟我说Express源代码里边的send()函数没有调用read()! 你还真是挺极品!

这张图可以好好让你了解下什么叫TCP。

TCP

@tulayang 这张图证明我说的是正确的,你说的是错误的,我一直在问你确定不确定accept之后要fork,你自己给的图你看仔细了没?我还是那句话,埋下头来读代码。

fork的是工作进程,箭头那么明显,看仔细了。

你这个图是人家解释nginx的工作方式的,nodejs跟他大同小异,看到那个accept_mutex锁了么?这是nginx为了防止惊群问题,每次只有一个工作进程有权accept,这同时也说明了,进程不是accept之后新fork的,这个工作进程是跟服务器生命周期同步的,不是那种accept后临时fork,用完丢弃的。整个过程依靠的是事件驱动。

解决惊群问题,淘宝的tengine在linux3.9版本内核后引入了SO_REUSEPORT,这样惊群问题就交给操作系统去处理了,比上锁效率高多了,淘宝测试下来比nginx快一倍多。

@coordcn

跟你就是浪费时间

@tulayang 都无力吐槽了,读源代码去吧。libuv再怎么封装,他也不可能把accept之后fork的代码吃掉啊。书上的也许是正确的,但是未必就是最好的。

@tulayang 我可没觉得浪费时间,大家都可能有技术盲点,通过讨论,互相促进,补长取短也是有益的。技术这个东西,错就是错,对就是对,不会因为谁的讨论姿态高而改变。书上的东西可以参考,但是读书要多思考,你那段代码其实也可以拿来讨论,他有什么缺点,有什么优点,都可以讨论,这可以让大家知道现在的设计为什么会是这个样子的。

一看标题我就楞了,nodejs不是单线程的么,为什么写成单进程啦

@Lenchs node.js多进程不是天然的,要自己实现,child_process或cluster。默认情况下,说他是单进程也没什么错。 node.js不是单线程的,事件循环在主线程,fs模块是基于线程池的。也就是说,node.js的文件读写是用线程来模拟的,并不是操作系统本身提供的异步文件操作,febjs实现的方式也是类似。但我个人认为这种线程模拟还是有改进空间的,nginx提供了sendfile()和AIO,libuv其实也封装了sendfile(),所以node.js文件系统还是有改进的空间的。

@tulayang Node.js在处理TCP连接请求时实际上并不需要fork: 1、使用fork只是一些教科书里面用C做TCP编程的例子; 2、Windows系统是不支持fork的; 3、Node.js之所以为Node.js,就是因为没有用fork,而是用了@coordcn 所提到的libuv

@leizongmin 叫他读源码,他也没去读,不知道现在读了没,libuv的代码写得至少是80分以上,虽然为了跨平台,做了很多妥协,但正是因为如此,代码质量如此之高,真是令人叹服。

回到顶部