原文: 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. Reveal逆向工程:分析任意iOS应用的UI界面

    在iOS逆向工程中,Reveal扮演着重要角色,一般情况下,Reveal在iOS开发过程中可以分析UI界面的状态,同样也可以应用于分析其他任意的App.特别是对于初学者来说,去了解其他优秀App的界面 ...

  2. AN2820 Driving bipolar stepper motors using a medium-density STM32F103xx microcontroller

    AN2820 Driving bipolar stepper motors using a medium-density STM32F103xx microcontroller Introductio ...

  3. 微信emoji表情编码 、MySQL 存储 emoji 表情符号字符集

    相关资料 微信emoji表情编码 微信用户名显示「emoji表情」 PHP处理微信中带Emoji表情的消息发送和接收(Unicode字符转码编码) MySQL 存储emoji表情 MySQL 存储 e ...

  4. 在ASP.NET MVC下实现单个图片上传, 客户端服务端双重限制图片大小和格式, 服务端裁剪图片

    在"MVC文件图片ajax上传轻量级解决方案,使用客户端JSAjaxFileUploader插件01-单文件上传"一文中,使用JSAjaxFileUploader这款插件实现了单文 ...

  5. Warning: The Copy Bundle Resources build phase contains this target's Info.plist file 'Info

    WARNING: The Copy Bundle Resources build phase contains this target's Info.plist file 'Info.plist'. ...

  6. skb的两个函数pskb_copy和skb_copy

    转自:http://blog.csdn.net/farmwang/article/details/54235252 skb的两个函数pskb_copy和skb_copy 前者仅仅是将sk_buff的结 ...

  7. 解决eclipse中web项目出现Project facet Java version 1.8 is not supported.的问题

    项目的jdk和tomcat的jdk版本不同,将eclipse-preference-server-runtime environments点击你要用的tomcat点击edit-jre选择和你项目对应的 ...

  8. 不规则形状的Mask动画

    不规则形状的Mask动画 效果 源码 https://github.com/YouXianMing/Animations // // MaskShapeViewController.m // Anim ...

  9. Wireshark的简介

    -------------------------------------------------------------- <Wireshark数据包分析实战>这本书其实还很不错,当时买 ...

  10. 怎样把vector和string数据传给旧的C API

     通常情况下.旧的C API使用数组合char*指针来进行数据交换而不是vector或string对象. 这种API还将存在非常长的一段时间,假设我们想有效地使用STL.我们就必须与它们和平共处. ...