該分享主要兩部分:
1.分析https的性能瓶頸點(diǎn) 2.根據(jù)瓶頸點(diǎn)的優(yōu)化方案
性能分析
https比起http的缺點(diǎn)主要是慢和貴,慢主要體現(xiàn)在未經(jīng)過(guò)任何優(yōu)化的https比http要慢幾百毫秒,在移動(dòng)端,甚至有500毫秒以上。貴主要體現(xiàn)在計(jì)算性能和服務(wù)器成本,服務(wù)端需要不斷做加解密操作。https比http多了一層tls協(xié)議,因此這些性能問(wèn)題也主要是tls的問(wèn)題。關(guān)務(wù)tls的握手過(guò)程和加解密過(guò)程,可以參考阮一峰的博客,筆者看過(guò)覺(jué)得通俗易懂,更能便于理解tls的性能優(yōu)化點(diǎn)。
1.關(guān)于ssl握手過(guò)程
http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html
2.關(guān)于數(shù)字簽名
http://www.ruanyifeng.com/blog/2011/08/what_is_a_digital_signature.html
3.關(guān)于RSA原理
http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html
po出演講中的兩張圖(圖片來(lái)自演講ppt)便于理解:


由上圖中,我們可以看到https中主要有四個(gè)計(jì)算環(huán)節(jié),證書(shū)校驗(yàn)、非對(duì)稱密鑰交換、對(duì)稱加解密和完整性校驗(yàn)。證書(shū)校驗(yàn)保證雙方的身份的合法性,非對(duì)稱秘鑰交換主要用來(lái)生成保密數(shù)據(jù)的對(duì)稱秘鑰,完整性主要保證數(shù)據(jù)的完整性,不被篡改。
從上圖的握手耗時(shí),我們可以看到在ECDHE_RSA 非對(duì)稱密鑰交換握手中,其中ServerKeyExchange過(guò)程,耗時(shí)2.4毫秒,在演講者的性能試驗(yàn)中,CPU的一核每秒只能處理400多次。這里補(bǔ)充一下這個(gè)過(guò)程耗時(shí)嚴(yán)重的原因,ServerKeyExchange過(guò)程其實(shí)就是使用DH算法中,服務(wù)端使用RSA算法的私鑰對(duì)DH參數(shù)做數(shù)字簽名,這個(gè)過(guò)程因?yàn)槭褂梅?wù)端的私鑰加密,私鑰的位數(shù)一般是遠(yuǎn)大于公鑰的,因?yàn)橛?jì)算復(fù)雜度很高,就會(huì)非常耗時(shí)。同樣在通用的RSA非對(duì)稱秘鑰中,服務(wù)端同樣會(huì)因?yàn)樾枰褂盟借€去解密客戶端傳來(lái)pre-master key,因而同樣會(huì)存在耗時(shí)嚴(yán)重的問(wèn)題。
對(duì)于握手耗時(shí),演講者也給出了其他計(jì)算過(guò)程的耗時(shí)實(shí)驗(yàn)統(tǒng)計(jì)。如下圖

上圖統(tǒng)計(jì)可以看出性能最好的是AES-128-GCM,性能最差的是AES-256-CBC,但即使它性能最差,它也只需要47微秒就能處理4000個(gè)字節(jié),性能相比來(lái)說(shuō)也還能接受。
根據(jù)上面的統(tǒng)計(jì)可以看出,非對(duì)稱密鑰交換過(guò)程的耗時(shí)占據(jù)了整體耗時(shí)75%左右,而對(duì)稱加密對(duì)性能影響較小。
優(yōu)化方案
簡(jiǎn)化握手過(guò)程
方式一:使用Session ID。Session ID由服務(wù)器生成并返回給客戶端,客戶端再次發(fā)起SSL握手時(shí)會(huì)攜帶上Session ID,服務(wù)端拿到后會(huì)從自己的內(nèi)存查找,如果找到便意味著客戶端之前已經(jīng)發(fā)生過(guò)完全握手,是可以信任的,然后可以直接進(jìn)行簡(jiǎn)化握手。
缺點(diǎn):在使用Nginx時(shí),只支持單機(jī)多進(jìn)程間共享的Session Cache。假如接入用的是一臺(tái)服務(wù)器、一臺(tái)Nginx的話,那ID生成和查找都在一起,肯定是可以命中的,但是大部分特別是流量比較大的接入環(huán)境都是多臺(tái)機(jī)器接入,那么當(dāng)用戶隔了較長(zhǎng)時(shí)間后攜帶Session ID發(fā)起https請(qǐng)求時(shí),可能落到了其他機(jī)器上而找不到之前服務(wù)器保存的Session ID,導(dǎo)致請(qǐng)求失敗。
方式二:使用分布式Session Cache,服務(wù)端會(huì)將Session ID寫(xiě)入全局緩存中,比如redis緩存,這樣用戶下次發(fā)起握手時(shí),可以直接從全局的緩存中查找到Session ID,提高成功率。可以參考的開(kāi)源庫(kù):OpenResty,BoringSSL
缺點(diǎn):消耗大量緩存。
同時(shí)上述兩種方式中,都需要對(duì)openssl做改造,因?yàn)镺penSSL提供了一個(gè)Session Cache的callback可以回調(diào),但是這個(gè)回調(diào)函數(shù)是同步的。這會(huì)拖累整體服務(wù)性能。BoringSSL中實(shí)現(xiàn)了Session Cache的異步查找,可以參考。
方式三:使用Session Ticket,Session Ticket由服務(wù)端加密分發(fā)給用戶,只要服務(wù)端的機(jī)器都使用相同的秘鑰,就可以保證簡(jiǎn)化握手的成功率。
缺點(diǎn):服務(wù)端需要做解密,影響性能。
Session Ticket,Session ID和Session Cache都有一個(gè)共同點(diǎn),都是基于內(nèi)存的,但當(dāng)客戶端重啟,瀏覽器tab關(guān)閉后又打開(kāi)時(shí),會(huì)導(dǎo)致Session數(shù)據(jù)丟失,所以可以采用如下方式:對(duì)于安全系數(shù)要求不是很高的業(yè)務(wù)可以控制Session數(shù)據(jù)存儲(chǔ)在App的私有路徑中,并且有秘鑰開(kāi)關(guān)隨時(shí)關(guān)閉這個(gè)功能。
異步代理耗時(shí)計(jì)算
雖然簡(jiǎn)化握手能提高很多性能,但完全握手在總的握手比例中仍然很高,有30%。所以對(duì)完全握手可以把耗時(shí)算法分離出來(lái)交給計(jì)算集群去計(jì)算,這個(gè)過(guò)程是異步的,不需要同步等待計(jì)算結(jié)果返回。即將耗時(shí)圖中ServerKeyExchange過(guò)程(設(shè)計(jì)到私鑰加解密的過(guò)程)異步化。
工程實(shí)現(xiàn):Nginx需要改造(這部分沒(méi)怎么看懂,后面去了解下Nginx的工作流程)。對(duì)OpenSSL改造,需要對(duì)OpenSSL的協(xié)議棧進(jìn)行修改,主要涉及到s3_srvr.c這個(gè)文件。OpenSSL1.1.0已經(jīng)支持異步事件。
寫(xiě)在最后
我司使用的私有協(xié)議和tls協(xié)議中,tls的性能明顯不如私有協(xié)議,目前服務(wù)端對(duì)tls應(yīng)該還沒(méi)有做優(yōu)化,可以考慮使用這個(gè)分享中異步代理的方式來(lái)提高tls的處理性能。畢竟有不少客戶會(huì)切到tls協(xié)議做請(qǐng)求的。