原文: https://fullstack-developer.academy/concurrent-http-connections-in-node-js/

------------------------------------------------------------------------------------------

Browsers, as well as Node.js, have limitations on concurrent HTTP connections. It is essential to understand these limitations because we can run into undesired situations whereby an application would function incorrectly. In this article, we will review everything that you, as a developer, need to be familiar with regarding concurrent HTTP connections.

Browser

Browsers adhere to protocols - and the HTTP 1.1 protocol states that a single client (a user client) should not maintain more than two concurrent connections. Now, some older browsers do enforce this, however, generally speaking, newer browsers - often referred to as "modern" browsers - allow a more generous limit. Here's a more precise list:

  • IE 7: 2 connections
  • IE 8 & 9: 6 connections
  • IE 10: 8 connections
  • IE 11: 13 connections
  • Firefox, Chrome (Mobile and Desktop), Safari (Mobile and Desktop), Opera: 6 connections

For the rest of this article remember the number 6 - this will play a crucial part when we go through our example.

Node.js

If you have worked with, learned or just read about Node.js before, you know that it is a single-threaded, non-blocking framework. This means that it allows a significant number of concurrent connections - all of this is made available by the JavaScript event loop.

The actual limit of connections in Node.js is determined by the available resources on the machine running the code and by the operating system settings as well.

Back in the early days of Node.js (think v0.10 and earlier), there was an imposed limit of 5 simultaneous connections to/from a single host. What does this mean? Under the hood when you are using the Node.js built-in HTTP module or any other module that uses the HTTP module like Express.js or Restify, you are in fact using a connection pool and HTTP keep-alive. This is great for performance improvement - think about the cycle like the following: an HTTP request is processed, this opens a TCP connection, for a new request an existing TCP connection can be used. (Without the keep-alive the process would be less performant by having to create a TCP connection, serve a response close the TCP connection and start this again for the next request)

In version higher than 0.10 the maxSockets value has been changed to Infinity.

The keep-alive is sent by the browser and we can easily see this if we log the request object in Node.js in the appropriate location. It should yield something similar to this (example taken from a Restify server):

headers:
{ host: 'localhost:3000',
'content-type': 'text/plain;charset=UTF-8',
origin: 'http://127.0.0.1:8080',
'accept-encoding': 'gzip, deflate',
connection: 'keep-alive',

Example

Let's take a look at a very straightforward example. Let's assume that we have some sort of a frontend where we are sending data to a backend (this is usually how modern applications work, a frontend framework making requests to a Backend API). For the our example, the data that we are sending is less important - it's equally applicable to a bulk file upload or anything else.

Trivia: I have in fact came across this issue while working on an application that did a bulk upload of images and sent it to a backend API for further processing.

Let's create a simple Restify API server:

const restify = require('restify');
const corsMiddleware = require('restify-cors-middleware');
const port = 3000;
const server = restify.createServer();
const bunyan = require('bunyan'); const cors = corsMiddleware({
origins: ['*'],
}); server.use(restify.plugins.bodyParser());
server.pre(cors.preflight);
server.use(cors.actual); server.post('/api', (req, res) => {
const payload = req.body;
console.log(`Processing: ${payload}`);
}); server.listen(port, () => console.info(`Server is up on ${port}.`));

This is very straightforward. Astute readers would already have noticed a somewhat crucial mistake in the code above but don't worry; it is made deliberately. So this API receives data sent via an HTTP POST request and displays a log message stating that it is processing whatever was sent as part of the request. (Again, the processing could be whatever we wanted, but for this discussion, it's just a simple console statement.)

Let's also create a simple frontend. Let's create a very simple index.htmland add the following content in between <script> tags:

const array = Array.from(Array(10).keys());
array.forEach(arrayItem => {
fetch('http://localhost:3000/api', {
method: 'POST',
mode: 'cors',
body: JSON.stringify(`hello${arrayItem}`)
})
.then(response => console.log(response.json()))
.catch(error => console.error(`Fetch Error: `, error));
});

Here, the Fetch API is used to iterate through 9 items (mimicking an upload of 9 files for example) and sending 9 HTTP POST requests to the Restify API discussed earlier.

Start up the API, also load the index.html via an HTTP server and let's see the results.

Here are two easy ways of firing up an HTTP server in an easy way: either use python -m SimpleHTTPServer 8000 (v2) or python -m http.server 8080 (v3). Or do a global npm install of http-server and then just do http-server from the folder where you have the index.html file.

It's fascinating what we see. Even though we have made 9 HTTP POSTrequests only six have arrived to the Restify API since we see 6 log statements.

However, if you wait about 2 minutes, additional log statements will appear.

So what is going on here?

Remember what we said before - the browser (in this case Safari) is capable of making six requests to the same host (in this case the connection is between our browser and the API running on port 3000 on localhost).

The connection is kept alive because we are not returning anything from the Node.js API. This was the mistake that I have deliberately made to make a point. So the browser sends six requests, and Node.js receives these but it never sends any information back rendering the remaining requests to be blocked.

So why are the other log statements visible later? The answer is simple: there's also a timeout, which is by default 2 minutes. After 2 minutes the request is cleared, so new requests are processed.

Let's update our code with these values:

server.server.maxConnections = 20;
function getConnections() {
server.server.getConnections((error, count) => console.log(count));
}
// add getConnections() in the API call:
server.post('/api', (req, res) => {
// ...
getConnections();
});

The server.server.maxConnections = 20; is there just to make a point that no matter how big this number is it's not going to change the outcome because we are still not returning anything (remember it is set to be Inifity anyway):

However, add the following setting to change the behaviour:

server.server.setTimeout(500);

The result is going to be a lot different. Since we are overwriting the timeout of the server, we only wait 500 ms and get rid of a pending request, allowing new requests to come in.

Please note that this is not a real solution to this problem, it is just for demonstration purposes.

Solving the problem

The right way to solve this problem is of course to return a response from the API:

server.post('/api', (req, res) => {
const payload = req.body;
console.log(`Processing: ${payload}`);
return res.json(`Done processing: ${payload}`);
});

Now all data is going to be processed just fine:

All uploads are now processed just fine.

Remember, res.json() under the hood uses res.send() which in turn also uses res.end() to send a response and to end it. This is true for both Restify and Express.js as well.

Conclusion

What is the moral of the story? Always close HTTP connections - no matter how, but close them - if you're making API calls consult the API documentation as well to close any active HTTP connection.

Concurrent HTTP connections in Node.js的更多相关文章

  1. 深入浅出node.js游戏服务器开发1——基础架构与框架介绍

    2013年04月19日 14:09:37 MJiao 阅读数:4614   深入浅出node.js游戏服务器开发1——基础架构与框架介绍   游戏服务器概述 没开发过游戏的人会觉得游戏服务器是很神秘的 ...

  2. What are some advantages of using Node.js over a Flask API?

    https://www.quora.com/What-are-some-advantages-of-using-Node-js-over-a-Flask-API Flask is a Python w ...

  3. 002/Node.js(Mooc)--Http知识

    1.什么是Http 菜鸟教程:http://www.runoob.com/http/http-tutorial.html 视频地址:https://www.imooc.com/video/6713 h ...

  4. (翻译)《Hands-on Node.js》—— Introduction

    今天开始会和大熊君{{bb}}一起着手翻译node的系列外文书籍,大熊负责翻译<Node.js IN ACTION>一书,而我暂时负责翻译这本<Hands-on Node.js> ...

  5. JavaScript(Node.js)+ Selenium自动化测试

    Selenium is a browser automation library. Most often used for testing web-applications, Selenium may ...

  6. Node.js连接Mysql,并把连接集成进Express中间件中

    引言 在node.js连接mysql的过程,我们通常有两种连接方法,普通连接和连接池. 这两种方法较为常见,当我们使用express框架时还会选择使用中间express-myconnection,可以 ...

  7. node.js + webstorm :配置开发环境

    一.配置开发环境: 1.先安装node (1).访问http://nodejs.org打开安装包,正常安装,点击next即可. 为了测试是否安装成功,打开命令提示符,输入node,则进入node.js ...

  8. Node.js Web 开发框架大全《中间件篇》

    这篇文章与大家分享优秀的 Node.js 中间件模块.Node 是一个服务器端 JavaScript 解释器,它将改变服务器应该如何工作的概念.它的目标是帮助程序员构建高度可伸缩的应用程序,编写能够处 ...

  9. [译]简单得不得了的教程-一步一步用 NODE.JS, EXPRESS, JADE, MONGODB 搭建一个网站

    原文: http://cwbuecheler.com/web/tutorials/2013/node-express-mongo/ 原文的源代码在此 太多的教程教你些一个Hello, World!了, ...

随机推荐

  1. 马士兵hadoop第三课:java开发hdfs

    马士兵hadoop第一课:虚拟机搭建和安装hadoop及启动 马士兵hadoop第二课:hdfs集群集中管理和hadoop文件操作 马士兵hadoop第三课:java开发hdfs 马士兵hadoop第 ...

  2. HDU 4498 Function Curve (自适应simpson)

    Function Curve Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)To ...

  3. C# 对WinForm应用程序的App.config的使用及加密

    原文地址:http://blog.163.com/zhou_zzq/blog/static/1019622120137621739874/   我们在写C#应用程序时,在工程文件中放置一个app.co ...

  4. wordpress入门

    安装bitnami wordpress. 打开仪表盘:开始菜单--Bitnami Wordpress协议栈 Manager Tool -- Go to Appllication -- Access W ...

  5. LAMP学习路线图

    站点开发概述 LAMP开发概述 HTML基础 CSS基础 DIV+CSS Javascript Jquery(Ajax) WAMP 环境搭建 PHP基本的语法,变量.数据类型,表达式,常量,流程控制, ...

  6. C++学习笔记23,类内函数重载

    该博文仅用于交流学习.请慎用于不论什么商业用途,本博主保留对该博文的一切权利. 博主博客:http://blog.csdn.net/qq844352155 转载请注明出处: 在一个类内,最常见的就是构 ...

  7. iOS中block简介-作用域

    转:http://www.2cto.com/kf/201401/269467.html 用block可以定义任意的代码片段,将其像对象一样传入另一个方法:它是c级别的语法,和C语言中的函数指针非常相似 ...

  8. 程序员必须知道的HTML常用代码有哪些?

    HTML即超文本标记语言,是目前应用最为广泛的语言之一,是组成一个网页的主要语言.在现今这个HTML5华丽丽地占领了整个互联网的时候,如果想要通过网页抓住浏览者的眼球光靠因循守旧是不行的,程序猿们需要 ...

  9. WordPress主题开发实例:查询单篇文章

    xxx/?page_id=5 想在首页调用以上页面的内容怎么做呢? 完整: <?php //查询 $my_query = new WP_Query( 'page_id=5' ); if($my_ ...

  10. java根据jar包反编译后修改再打包回jar的做法

    1. 得到一个待要修改的jar包 2. 我的环境是windows,然后解压这个jar包,得到一堆class文件,这时候就找到你需要的那个class文件 3. 我首先是使用jd-gui工具看一下这个cl ...