我正在对Java UDP客户端进行基准测试,该客户端以尽可能快的速度连续发送具有100字节有效负载的数据报。它是使用java.nio.*
实现的。测试表明,它能够实现每秒220k数据报的稳定吞吐量。我没有测试服务器;客户端只是将数据报发送到localhost上的一些未使用的端口。
我决定运行Node.js的同样的测试,比较这两种技术,这是令人惊讶的伤心地看到,Node.js的比Java执行慢10倍。让我带您浏览我的代码。
首先,我使用Node.js的dgram module创建一个UDP套接字:
var client = require('dgram').createSocket("udp4");
然后我创建一个使用该套接字发送数据报的函数:
function sendOne() {
client.send(message, 0, message.length, SERVER_PORT, SERVER_ADDRESS, onSend);
}
变量message
是在应用程序启动时从包含一百个字符的字符串创建的缓冲区:
var message = new Buffer(/* string with 100 chars */);
函数onSend
只增加一个变量,该变量包含到目前为止发送了多少数据报。接下来我有一个使用sendOne()
不断调用setImmediate()
的函数:
function sendForever() {
sendOne();
setImmediate(sendForever);
}
最初我尝试使用process.nextTick(sendForever)
,但我发现它总是把自己放在事件队列的顶端,甚至在IO事件之前,就像文档says:
它在事件循环的后续滴答中触发任何其他I / O事件(包括定时器)之前运行。
这可以防止发送IO事件发生,因为nextTick
在每次滴答时不断将sendForever
放在队列的尖端。队列随着未读IO事件而增长,直到它使Node.js崩溃:
fish: Job 1, 'node client' terminated by signal SIGSEGV (Address boundary error)
另一方面,setImmediate
在I / O事件回调后触发,这就是我使用它的原因。
我还创建了一个计时器,每1秒向控制台打印一次,在最后一秒发送了多少数据报:
setInterval(printStats, 1000);
最后我开始发送:
sendForever();
在运行Java测试的同一台机器上运行,Node.js实现了每秒21k数据报的稳定吞吐量,比Java慢十倍。
我的第一个猜测是为每个刻度线放两个sendOne
,看它是否会使吞吐量加倍:
function sendForever() {
send();
send(); // second send
setImmediate(sendForever);
}
但它并没有改变吞吐量。
我在GitHub上有一个存储库,其中包含完整的代码:
https://github.com/luciopaiva/udp-perf-js
只需将其克隆到您的机器,cd
进入该文件夹并运行:
node client
我想开一个关于如何在Node.js中改进这个测试的讨论,如果有一些方法我们可以增加Node.js的吞吐量。有任何想法吗?
P.S。:对于那些感兴趣的人,这里是Java part。
该测试有瑕疵。 UDP不保证任何内容的传递,并不保证在出现错误时会出现任何错误。
您的应用程序可以从Java应用程序以1GB / s的速度发送1000k数据报,但是90%的数据报从未到达目的地......目标可能甚至没有运行。
如果要进行任何类型的UDP测试,则需要两个应用程序,每端一个。发送编号的数据报1,2,3 ...并检查发送的内容和收到的内容。请注意,UDP不保证任何消息排序。
内核以特殊方式管理localhost网络。有巨大的缓冲区专用于它和更高的限制,没有流量通过任何网卡或驱动程序。这与发送真实数据包非常不同。
当它们仅在localhost上完成时,测试可能看起来有点好。当它通过任何真实的物理基础设施时,一切都会失败。
PC1 <-----> switch <-----> PC2
比方说,同一个房间里有两台电脑通过交换机连接起来。在这种简单的设置上实现10k / s UDP数据报并不随意丢失消息将是不小的壮举。
那只是同一个房间里的两台电脑。互联网和长途电话可能会更糟糕。
如果你想要的只是让性能测试变得更快,删除setImmediate
调用并在第一次完成后执行下一次发送,即在send
回调中,在我的慢速笔记本电脑上将其性能提高到每秒~10万次请求。
function send(socket, message) {
socket.send(message, SERVER_PORT, (err) => {
send(socket, message);
});
}
const socket = require('dgram').createSocket('udp4');
const message = new Buffer('dsdsddsdsdsjkdshfsdkjfhdskjfhdskjfhdsfkjsdhfdskjfhdskjfhsdfkjdshfkjdshfkjdsfhdskjfhdskjfhdkj');
send(socket, message);