nodejs中正确关闭http server的方法
发布在nodejs 边角料2014年4月6日view:12825
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

最近做项目时,我们使用原生的http模块可以方便create一个server出来,但是当我们调用官方api close方法的时候,却发现有时候并不能正确关闭。

先看下api的解释,http的close方法调用的是net模块的close方法:

server.close([callback])#

Stops the server from accepting new connections and keeps existing connections. This function is asynchronous, the server is finally closed when all connections are ended and the server emits a ‘close’ event. Optionally, you can pass a callback to listen for the ‘close’ event.

当我们编写如下代码时,我们看到类似bug一样的现象:

var server = http.createServer(function(req,res){
   res.end();
});

server.listen(7777);
setTimeout(function(){
    server.close(function(){
         console.log("server close");
    });
},3000);

看起来毫无问题,运行起来也是毫无问题,3秒后服务器关闭打印log。但是如果在这3秒中你打开了页面,喝喝。那么 server就永远都close不掉了,除非你关闭tab标签,或者整个浏览器。不同系统不同浏览器还不一样。

分析下问题,我加入了下面的2段代码:

var server = http.createServer(function(req,res){
res.writeHead(200,{
    "connection":"close",
    "transfer-encoding":"identity"
});       
res.end();
});

可能你认为,默认的nodejs会在header中加入connection keep-alive和transfer-encoding chunked。这2个参数导致的,那么改了这2个参数之后的效果如何?

喝喝,3秒之后,访问页面,返回暂无数据输出,感觉上像是close掉了,但是log依然没有打印出来,关掉tab或者浏览器,log打印。

咦。。。我了个擦,怎么回事呢。最近项目中遇到了这个问题,最后了解到,http协议1.1默认就是长连接,我指定了connection后,应该就及时关闭了,我也不以chunked的方式发送数据,应该都清掉了猜对。。最后,开始看socket的相关知识【麻痹,我还是前端么】发现浏览器与服务器之间的通信,大概是这么个流程。

本地浏览器开始一个socket实例,在本机一个端口上,这个是在http协议上层的,然后发送第一次握手到server,server接收,返回数据给client,然后client发请求说我走啦,拜拜,server再关闭socket这个实例。如果server单方面从server上close了,socket会进入一个close_wait状态。所以么。。客户端不断掉,我们就没办法了么?【具体刚才说的socket知识可见http://blog.9tech.cn/?c=site&m=article&id=81】 或者自己搜索引擎吧,说多了怕误导大家。

所以如果你想及时关闭一个server,或者重启它。你需要自己管理这些socket连接池。在nodejs中需要自己销毁他们。

简单的代码如下:

var sockets = [];
server.on("connection",function(socket){
  sockets.push(socket);
  socket.once("close",function(){
     sockets.splice(sockets.indexOf(socket),1);
  });
});

//关闭之前,我们需要手动清理连接池中得socket对象
function closeServer(){
     sockets.forEach(function(socket){
         socket.destroy();
     });
     server.close(function(){
         console.log("close server!");
     });
}

主要代码就是这些,我们再加入池子之前,记得在socket自己销毁时,排除掉就ok了,这样我们就可以实现及时关闭server,不必等待超时或者客户端主动销毁socket了。

评论
发表评论
3年前

太棒了,了解了背后的一些真相。

WRITTEN BY
PUBLISHED IN
nodejs 边角料

分享和记录一下,个人在学习nodejs中遇到的一些有意思的事。

我的收藏