node.js的HTTP 事务处理示例

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了node.js的HTTP 事务处理示例相关的知识,希望对你有一定的参考价值。

参考技术A

本示例意图向读者传递 Node.js 关于 HTTP 处理过程的详实概念。在不考虑编程语言和环境的情况下,我们假设您已经知晓通常情况下 HTTP 请求是如何工作的,并且对 Node.js 的 EventEmitters 和 Streams 也已知晓。如果您对他们并不熟悉,通过 API 文档可以快速查阅。
创建服务
Node 的网络应用都需要先创建一个网络服务对象,这里我们通过 createServer 来实现。 var http = require('http');var server = http.createServer(function(request, response)    // handle your requset );传入 createServer 的 function 在每次 HTTP 请求时都将被调用执行,因此这个 function 也被称为请求的处理者。事实上通过 createServer 返回的 Server 对象是一个 EventEmitter,我们需要做的仅仅是在这里保存这个 server 对象,并在之后对其添加监听器。 var http = require('http');var server = http.createServer(); server.on('request', function(request, response) // handle your requset );当 HTTP 请求这个服务时,node 调用请求处理者 function 并传入一些用于处理事务相关的对象:request 和 response。我们可以非常方便的获得这两个对象。 var http = require('http');var server = http.createServer(); server.on('request', function(request, response) // handle your requset ).listen(8080); 为了对实际的请求提供服务,在 server 对象上需要调用 listen 方法。绝大多数情况你需要传入 listen 你想要服务监听的端口号,这里也存在很多其他的可选方案,参见 API reference。

参考技术B 即使刚刚接触 node.js 的新手也能很轻松的创建网络应用,但是处理请求的回调函数以及其中的参数,也经常给新手带来疑惑。下文参考官网的资料,分析 node.js 在对 HTTP协议的事务处理过程中的几个重要定义以及原理过程。
创建服务器
所有的 node.js 网络应用都需要创建服务器,我们首先使用语法糖 createServer 来创建服务器。
var http = require('http');

var server = http.createServer(function(request, response)
// 神奇发生在这里!
);

现在具体分析一下语句中的各个成分,访问服务器的 HTTP 请求 (HTTP Request) 都由http.createServer 中的回调函数来处理,因此我们称它为请求处理器 (Request Handler)。 实际上从http.createServer 返回的 server 对象是 http.Server 的一个实例,同时也是一个 EventEmitter (http.Server 继承自 net.Server 而 net.Server 是一个 EventEmitter),而 http.createServer 的请求处理器会自动添加一个 'request' 事件。
根据上述分析,我们来分解语法糖。首先初始化一个 http.Server 实例,然后为它添加监听器,这样会得到一个更具体的代码。
var server = http.createServer();
server.on('request', function(request, response)
// 神奇同样会发生在这里!
);

当 HTTP 请求到达服务器时,会触发 'request' 事件,请求处理器会使用 request 和 response 两个对象来处理相关事务。request 对象是 http.IncomingMessage 的一个实例,而 response 对象则是 http.ServerResponse 的一个实例。具体属性可参考官网的 API 文档。
方法,统一资源定位器和头部 (Method, URL and Headers)
在处理一个HTTP 请求时,首先希望了解的就是 method 和 URL, 并以此采取合适的处理手段。 node.js 将这些属性很自然的赋予在 request 对象上。
var method = request.method;
var url = request.url;
var headers = request.headers;
var userAgent = headers['user-agent'];

代码中的 method 对应 HTTP 协议中的动词方法,如: 'GET', 'DELETE' 。 url 则对应没有协议,主机和端口的 URL , 如: '/status?name=ryan' 。 headers 需要注意的是,为了方便解析都使用小写字母。
请求主体 (Request Body)
收到 POST 和 PUT 请求时,node 就需要解析请求主体。 获取主体数据比较复杂, 首先利用 request 对象传递一个回调函数来实现 ReadableStream 接口,然后通过对流的 'data' 和 'end' 事件的监听来抓取数据流。
每次响应 'data' 事件的数据块是一个 Buffer ,如果想输出字符串数据,那么最佳方案是将数据块集中到一个数组,然后在 'end' 事件中连接数组并字符串化。
var body = [];
request.on('data', function(chunk)
body.push(chunk);
).on('end', function()
body = Buffer.concat(body).toString();
// 至此, `body` 是请求主体的字符串形式。
);

错误处理
当 request 流中出现错误,将触发流的 'error' 事件,如果程序没有监听该事件,错误会被抛出同时程序有可能崩溃。 因此在 request 流上添加 'error' 事件监听器显得异常重要。
request.on('error', function(err)
// 打印错误信息
console.error(err.stack);
);

目前为止
我们学习了创建服务器,从 request 中获取 method, URL, headers 以及 body, 将这些内容联系起来:
var http = require('http');

http.createServer(function(request, response)
var headers = request.headers;
var method = request.method;
var url = request.url;
var body = [];
request.on('error', function(err)
console.error(err);
).on('data', function(chunk)
body.push(chunk);
).on('end', function()
body = Buffer.concat(body).toString();
);
).listen(8080); // http 协议的监听端口为 8080

如果运行这个程序,并在浏览器中输入 http://localhost:8080 , 那么你的请求会超时,没有任何内容返回到浏览器。因为到目前为止我们还没有接触到 response 对象, 它是 http.ServerResponse 的实例,同时也是一个 WritableStream 。 后面的内容将详细学习response 对象。
参考技术C var http = require("http");
//创建一个服务器,回调函数表示接收到请求之后做的事情
var server = http.createServer(function(req,res)
//req参数表示请求,res表示响应
console.log(req.url);
//设置头部
res.writeHead(200,"Content-Type":"text/html;charset=UTF8");
res.write("服务器接收到了请求" + req.url);
res.end("end");
);
server.listen(3000,"127.0.0.1");

在 Node.js 中处理回滚的 MySQL 事务

【中文标题】在 Node.js 中处理回滚的 MySQL 事务【英文标题】:Handling rollbacked MySQL transactions in Node.js 【发布时间】:2016-05-04 05:55:01 【问题描述】:

我这几天一直在处理一个问题,我真的希望你能帮助我。

这是一个基于 node.js 的 API,使用 sequelize 表示 MySQL

在某些 API 调用中,代码会启动锁定某些表的 SQL 事务,如果我同时向 API 发送多个请求,则会收到 LOCK_WAIT_TIMEOUT 错误。

var SQLProcess = function () 
    var self = this;
    var _arguments = arguments;

    return sequelize.transaction(function (transaction) 
            return doSomething(transaction: transactioin);
        )
        .catch(function (error) 
            if (error && error.original && error.original.code === 'ER_LOCK_WAIT_TIMEOUT') 

                return Promise.delay(Math.random() * 1000)
                    .then(function () 
                        return SQLProcess.apply(self, _arguments);
                    );

             else 
                throw error;
            
        );
;

我的问题是,同时运行的请求相互锁定了很长时间,我的请求在很长一段时间(~60秒)后返回。

我希望我能解释清楚易懂,你可以给我一些解决方案。

【问题讨论】:

这不是node.jssequelize 麻烦,这是来自MySQL 1205 Lock wait timeout exceeded; try restarting transaction 的错误消息。您应该查看 MySQL 服务器中的事务及其状态。 非常清楚,为什么我会收到此错误。我正在寻找如何处理它的解决方案。 只有一种方法可以了解发生了什么,那就是SHOW ENGINE INNODB STATUS\G。根据我的经验,我在 MySQL 中多次遇到死锁事务。 目前还不清楚您期望得到什么样的答案。 “把手”是什么意思?你想摆脱锁定(在这种情况下,你的doSomething 做什么很重要)?或者你想重复你的交易(在这种情况下,你为什么不从你的处理程序再次运行doSomething?或者你的catch语句不起作用,你想让它起作用?我有一个很大的列表与死锁有关的随机笔记here,现在我看起来很困惑,但是检查一下,也许你会发现一些有用的东西。 【参考方案1】:

死锁的主要原因是糟糕的数据库设计。如果没有有关您的数据库设计的更多信息以及哪些确切的查询可能会或可能不会相互锁定,就不可能为您的问题提供特定的解决方案。 不过,我可以为您提供解决此问题的一般建议/方法:

我会确保您的数据库至少在 第三范式 中是normalized,或者,如果这还不够进一步的话。可能有一些工具可以为您自动执行此过程。 除了减少死锁的可能性之外,这还有助于保持数据的一致性,这始终是一件好事。 让您的交易尽可能少。如果您要向表中插入新行并相应地更新其他表,您可能希望使用触发器而不是另一个 SQL 语句来执行此操作。这同样适用于读取行和值。此类事情可以在您的交易之前或之后完成。

选择正确的隔离级别。可能的隔离级别是:

READ_UNCOMMITTED READ_COMMITTED 可重复阅读 可序列化

Sequelize 的official documentation 描述了如何自己设置隔离级别和锁定/解锁事务。

正如我所说,在没有进一步了解您的数据库和查询设计的情况下,我现在只能为您做这一切。 希望这会有所帮助。

【讨论】:

【参考方案2】:

这可能不是您问题的直接答案,但也许通过查看您遇到此问题的原因也会有所帮助。

1) doSomething() 有什么作用?无论如何我们可以在那里做一些改进?

首先,需要 60 秒的事务是可疑的。如果您将表锁定这么长时间,则很有可能应该重新访问设计。给定一个典型的数据库操作运行 10 - 100 毫秒。

理想情况下,所有数据准备都应该在事务之外完成,包括从数据库读取的数据。并且事务实际上应该只用于事务性操作。

2) 是否可以使用mysql存储过程?

确实,mysql 的存储过程没有编译为 Oracle 的 PL/SQL。但它仍在数据库服务器上运行。如果您的应用程序真的很复杂,并且在该事务中包含大量反向和强制数据库和您的节点应用程序之间的网络流量,并且考虑到有如此多的 javascript 调用层,它可能真的会减慢速度。如果 1) 不能为您节省很多时间,请考虑使用 mysql 存储过程。

显然,这种方法的缺点是在 nodejs 和 mysql 中都很难维护代码。

如果 1) 和 2) 绝对不可能,您可以考虑使用某种流量控制或排队工具。要么您的应用程序确保第二个请求在第一个请求完成之前不会发送,或者您有一些第 3 方排队工具来处理它。看来您在运行这些请求时不需要任何并行性。

【讨论】:

谢谢!排队工具做到了!我已经设置了一个bluebird-queue,现在所有的请求一个接一个地运行,没有阻塞。

以上是关于node.js的HTTP 事务处理示例的主要内容,如果未能解决你的问题,请参考以下文章

NodeJs使用Mysql模块实现事务处理

Node.js mysql 事务

有些交易有效,有些则无效。处理事务时出现 UnhandledPromiseRejectionWarning 和 VM 异常

使用 mysql 和 node.js 的可序列化事务

SqlServer 事务和异常处理示例

.NET C#事务处理