提高NodeJS中SSL服务的性能
在浏览互联网时,我们都知道,通过SSL进行加密是非常重要的。在贝宝(PayPal),安全是我们的首要任务。我们使用端到端的加密,不仅只是我们的公共网站,对于我们的内部服务调用也同样如此。SSL加密技术将在很大程度上影响node.js的性能。我们已经花时间调整我们的对外服务,并充分地利用他们。下面是一些我们发现能显著地提高SSL对外性能的SSL配置调整清单。
SSL密码
开箱即用,Node.js的SSL使用一组非常强大的密码算法。特别是,迪菲赫尔曼密钥交换和椭圆曲线算法是极其昂贵的。而且当你在默认配置中用了太多的对外SSL调用,Node.js的性能将从根本上得到削弱。为了得到它到底有多慢这个结论,这儿有个服务调用的CPU样本:
918834.0ms100.0%0.0node(91770) 911376.0ms99.1%0.0start 911376.0ms99.1%0.0node::Start 911363.0ms99.1%48.0uv_run 909839.0ms99.0%438.0uv__io_poll 876570.0ms95.4%849.0uv__stream_io 873590.0ms95.0%32.0node::StreamWrap::OnReadCommon 873373.0ms95.0%7.0node::MakeCallback 873265.0ms95.0%15.0node::MakeDomainCallback 873125.0ms95.0%61.0v8::Function::Call 873049.0ms95.0%13364.0_ZN2v88internalL6InvokeEbNS0 832660.0ms90.6%431.0_ZN2v88internalL21Builtin 821687.0ms89.4%39.0node::crypto::Connection::ClearOut 813884.0ms88.5%37.0ssl23_connect 813562.0ms88.5%54.0ssl3_connect 802651.0ms87.3%35.0ssl3_send_client_key_exchange 417323.0ms45.4%7.0EC_KEY_generate_key 383185.0ms41.7%12.0ecdh_compute_key 1545.0ms0.1%4.0tls1_generate_master_secret 123.0ms0.0%4.0ssl3_do_write ...
让我们重点关注一下密钥的生成:
802651.0ms87.3%35.0ssl3_send_client_key_exchange 417323.0ms45.4%7.0EC_KEY_generate_key 383185.0ms41.7%12.0ecdh_compute_key
这个调用87%的时间都花在了生成密钥上!
这些密码能被改变以减少密集的计算。这个想法已经在https(或代理)得以实现了。例如:
varagent=newhttps.Agent({ "key":key, "cert":cert, "ciphers":"AES256-GCM-SHA384" });
上面的密钥已经没用昂贵的迪菲赫尔曼密钥交换。用相似的东西代替之后,在下面的样例中我们能看到显著的变化:
... 57945.0ms32.5%16.0ssl3_send_client_key_exchange 28958.0ms16.2%9.0generate_key 26827.0ms15.0%2.0compute_key ...
通过OpenSSL文档,你可以学习更多关于密码串的东西。
SSL会话恢复
如果您的服务器支持SSL会话恢复,那么你可以通过https(或代理)来传递会话。你也可以将代理的createConnection函数包裹起来:
varcreateConnection=agent.createConnection; agent.createConnection=function(options){ options.session=session; returncreateConnection.call(agent,options); };
通过给连接增加简短的握手机制,会话恢复能降低连接数的使用。
保持活动
允许代理保持活动将缓和SSL握手。一个保持活动的代理,比如agentkeepalive可以修复结点保持活动的问题,但在Node0.12中它是非必须的。
另一个需要铭记在心的东西是代理的maxSockets,这个值高的话能对性能造成负面的影响。在你创建的对外连接数量的基础上控制你的maxSockets值。
Slab的大小
tls.SLAB_BUFFER_SIZE决定了被tls客户端(服务器)使用的slab缓冲区的分配大小。它的大小默认为10MB。
这些分配的区间将会扩展你的rss且会增加垃圾回收的时间。这意味着高容量将会影响到性能。把这个容量调整到一个比较低的值可以改善内存和垃圾收集的性能。在0.12版本中,slab的分配已经得到改善了,没有必须再调整了。
SSL在0.12中近期的改变
测试Fedor的SSL增强版。
测试说明
运行一个作为SSL服务代理的http服务,全部运行在本机上。
v0.10.22
Running10stest@http://127.0.0.1:3000/ 20threadsand20connections ThreadStatsAvgStdevMax+/-Stdev Latency69.38ms30.43ms268.56ms95.24% Req/Sec14.954.1620.0058.65% 3055requestsin10.01s,337.12KBread Requests/sec:305.28 Transfer/sec:33.69KB
v0.11.10-pre(从主版本构建)
Running10stest@http://127.0.0.1:3000/ 20threadsand20connections ThreadStatsAvgStdevMax+/-Stdev Latency75.87ms7.10ms102.87ms71.55% Req/Sec12.772.4319.0064.17% 2620requestsin10.01s,276.33KBread Requests/sec:261.86 Transfer/sec:27.62KB
这没有太多的区别,但这应归于默认密码,所以让我们调整密码的代理选项。例如:
varagent=newhttps.Agent({ "key":key, "cert":cert, "ciphers":"AES256-GCM-SHA384" });
v0.10.22
Running10stest@http://localhost:3000/ 20threadsand20connections ThreadStatsAvgStdevMax+/-Stdev Latency59.85ms6.77ms95.71ms77.29% Req/Sec16.392.3622.0061.97% 3339requestsin10.00s,368.46KBread Requests/sec:333.79 Transfer/sec:36.83KB
v0.11.10-pre(从主版本构建)
Running10stest@http://localhost:3000/ 20threadsand20connections ThreadStatsAvgStdevMax+/-Stdev Latency38.99ms5.96ms71.87ms86.22% Req/Sec25.435.7035.0063.36% 5160requestsin10.00s,569.41KBread Requests/sec:515.80 Transfer/sec:56.92KB
正如我们所见,经过Fedor的修改,这有着巨大的区别:从0.10到0.12性能差不多差着2倍左右!
总结
有人可能会问“为什么不仅仅只是关掉SSL呢,关了之后它就会变得快起来”,且对于一些人来说这也是一种选择。实际上,当我问别人他们是如何解决SSL性能问题的时候这是比较有代表性的答案。但是,如果企业SSL要求的任何东西只增加不减少;且尽管已经做了很多来改善Node.js中的SSL,性能调整仍然还是需要的。我希望上述的一些技艺能够帮助你调整SSL用例性能。