電商架構(gòu)回顧之二-網(wǎng)關(guān)功能介紹及改造方案

前言

架構(gòu)1.0版本上線后,用戶量及訪問量都是呈現(xiàn)爆發(fā)式增長。尤其在每周五推文之后,都會(huì)出現(xiàn)一波用戶訪問高峰。用戶訪問量的瞬間暴增,對(duì)我們的網(wǎng)關(guān)組件的挑戰(zhàn)也是最大的。


網(wǎng)關(guān)故障

概述

18年11月2號(hào)和11月9號(hào)這兩天,都出現(xiàn)用戶訪問量突然暴增的情況,在這種情況下,我們的網(wǎng)關(guān)組件都出現(xiàn)的故障。導(dǎo)致部分用戶無法訪問我們的接口服務(wù)。

現(xiàn)象及原因分析

當(dāng)gateway不可用時(shí),會(huì)打印大量如下的日志信息??????

2018-11-09 18:27:09 [reactor-http-nio-1] ERROR org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler- Failed to handle request [POST http://api.
com/account/login]
io.netty.handler.codec.EncoderException: java.lang.IllegalStateException: unexpected message type: DefaultHttpRequest
        at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:106) ~[netty-codec-4.1.23.Final.jar!/:4.1.23.Final]
        at io.netty.channel.CombinedChannelDuplexHandler.write(CombinedChannelDuplexHandler.java:348) ~[netty-transport-4.1.23.Final.jar!/:4.1.23.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:738) ~[netty-transport-4.1.23.Final.jar!/:4.1.23.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:730) ~[netty-transport-4.1.23.Final.jar!/:4.1.23.Final]
        at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:816) ~[netty-transport-4.1.23.Final.jar!/:4.1.23.Final]



網(wǎng)上的解釋

According to our tests the highest probability when onCompete() for client requests is not called happens in the following conditions:
1.HttpClient receives HTTP error response, 404 in our case. Most likely the error code value might be any. However, with 200 code everything seem to work fine.
2.HttpClient uses HTTPS to connect a server. With HTTP it seems to work well, or at least the probability to fail is much lower. The only explanation coming to our minds is that HTTPS uses chuncked encoding, while HTTP sets content length and thus someone tracking end of package upload operation properly calls onComplete.
3.One more thing, the uploaded content in HTTP request should be greater than some value. E.g. uploading 200 bytes file works fine, while 2MBs file reproduces the case almost immediately.
https://github.com/reactor/reactor-netty/issues/177
https://github.com/spring-cloud/spring-cloud-gateway/issues/228

我們發(fā)現(xiàn)這個(gè)問題是SpringCloud Gateway的bug造成的,大致的原因應(yīng)該是在一些異常場景下,connection已經(jīng)broken了,但是沒有立即回收掉,導(dǎo)致broken的connectioin還被繼續(xù)使用。
我們當(dāng)前使用的Springboot版本是2.0.1.RELEASE。經(jīng)過在測試環(huán)境反復(fù)的壓測后,我們將版本升級(jí)到2.0.4.RELEASE,這個(gè)問題暫時(shí)得到的解決。

思考

前期的架構(gòu)選型中,對(duì)組件的穩(wěn)定性考慮不夠。SpringCloud Gateway作為一個(gè)全新的組件出來,在還沒有經(jīng)過大量實(shí)踐的考驗(yàn)下,直接使用,其實(shí)風(fēng)險(xiǎn)是很大的。我們要承擔(dān)新組件內(nèi)部版本快速升級(jí)過程中,和其他組件的版本兼容性問題,還有就是新版本會(huì)不會(huì)引發(fā)新的bug的問題呢?基于對(duì)SpringCloud Gateway版本穩(wěn)定性的擔(dān)憂,我們決定將網(wǎng)關(guān)組件進(jìn)行重新的選型和改造。


網(wǎng)關(guān)功能介紹

在介紹網(wǎng)關(guān)改造方案之前,先大概說明一下現(xiàn)有網(wǎng)關(guān)都包含了哪些功能。這個(gè)可以幫忙我們?cè)诤罄m(xù)網(wǎng)關(guān)選型上提供一些參考的方向。

網(wǎng)關(guān)功能組成

  • JWT 校驗(yàn)
  • 訪問權(quán)限控制
  • 請(qǐng)求動(dòng)態(tài)路由轉(zhuǎn)發(fā)
JWT 簡介

JWT是一種用于雙方之間傳遞安全信息的簡潔的、URL安全的表述性聲明規(guī)范。JWT作為一個(gè)開放的標(biāo)準(zhǔn)(RFC 7519),定義了一種簡潔的,自包含的方法用于通信雙方之間以Json對(duì)象的形式安全的傳遞信息。因?yàn)閿?shù)字簽名的存在,這些信息是可信的,JWT可以使用HMAC算法或者是RSA的公私秘鑰對(duì)進(jìn)行簽名。

JWT 適用場景
  • 身份認(rèn)證
    當(dāng)用戶成功登錄后,后端會(huì)頒發(fā)一個(gè)JWT,在接下來的每個(gè)請(qǐng)求中包含JWT,網(wǎng)關(guān)就可以根據(jù)請(qǐng)求中JWT來判斷用戶的合法性。由于它的開銷非常小,可以輕松的在不同域名的系統(tǒng)中傳遞。
JWT 組成
image.png

由上圖可以看到,JWT由三個(gè)部分組成

  • header(頭部信息)
    頭部信息中包含了token的類型和所使用到加密算法。經(jīng)過base64編碼后,作為JWT的一部分。
  • payload(內(nèi)容信息)
    這里包含了實(shí)際需要使用的用戶信息。 一般包括用戶名、用戶id、過期時(shí)間等信息。
  • signature(簽名信息)
    signature=header(base64) + payload(base64) + secret
JWT 使用技巧

JWT生成策略

  • 方案一:Token過期后,再重新觸發(fā)登錄邏輯,登錄成功重新頒發(fā)新的Token給前端。
    這種方案每次token都需要讓用戶重新走一次登錄流程,安全性有保證。但是頻繁的用戶重登錄會(huì)降低用戶的體驗(yàn),所以token的過期時(shí)間不能設(shè)置太短。一般建議設(shè)置3~7天比較合適。

  • 方案二:Token過期后,重新生成新的Token并直接頒發(fā)給前端。
    這種方案對(duì)用戶來說完全是無感的,token可以設(shè)置成5分鐘就失效。失效后,等待下一次訪問了又在重新頒發(fā)一個(gè)token。這種方案的有點(diǎn)就是用戶體驗(yàn)更好,但是無法確定用戶的合法性,當(dāng)非法用戶拿到token后,可以隨意使用,而不需要在進(jìn)行任何登錄判斷。

訪問控制

在網(wǎng)關(guān)層面針對(duì)API設(shè)置了不同的訪問權(quán)限控制

動(dòng)態(tài)路由轉(zhuǎn)發(fā)
網(wǎng)關(guān)路由請(qǐng)求簡圖.png

網(wǎng)關(guān)改造

選型考慮

網(wǎng)關(guān)作為所有流量的入口,要求必須是高并發(fā)的、穩(wěn)定的、并且是經(jīng)過大量驗(yàn)證的。經(jīng)過住性能和穩(wěn)定性考驗(yàn)的,首選肯定就是nginx,或者是基于nginx搭建的產(chǎn)品。

nginx
nginx基于C語言實(shí)現(xiàn),采用的非阻塞模型能輕松支持大量并發(fā)連接數(shù)。但作為網(wǎng)關(guān)來使用,除了性能高,同時(shí)還需要具備良好的可擴(kuò)展性,包括上文提到的限流、鑒權(quán)、監(jiān)控、路由等等。

Openresty
基于nginx+lua實(shí)現(xiàn)的高性能web平臺(tái)。其內(nèi)部集成了大量優(yōu)秀的lua庫,開發(fā)者也可以基于自身的需求來實(shí)現(xiàn)自己的lua腳本,非常適合用來做一些高性能的web應(yīng)用、網(wǎng)關(guān)。

最終選擇了Openresty,這個(gè)主要還是根據(jù)我們目前的業(yè)務(wù)量和開發(fā)成本綜合考慮的結(jié)果。其實(shí),業(yè)界還有像Kong、Orange這些基于Openresty搭建的網(wǎng)關(guān)產(chǎn)品,他們提供的組件更加豐富,能力更強(qiáng)大。 但最終我們沒選擇,其實(shí)也沒什么原因,就是覺得Openresty已經(jīng)足夠能滿足我們,所以就選擇了Openresty。

解決方案

  • JWT 校驗(yàn) —> 集成jwt nginx模塊
  • 訪問權(quán)限控制 —> 自編lua腳本
  • 路由轉(zhuǎn)發(fā) —> consul-template

簡單說明
Openresty提供了jwt的模塊,我們只需要基于jwt來簡單實(shí)現(xiàn)我們對(duì)token合法性校驗(yàn)和異常處理邏輯即可;路由轉(zhuǎn)發(fā)Nginx提供了upstream的模塊來實(shí)現(xiàn),但是我們需要基于consul來實(shí)現(xiàn)一套動(dòng)態(tài)生成upstream的機(jī)制,這里我們選擇了Consul-template.

Consul-template

是Consul官網(wǎng)提供的模塊,可以基于Consul實(shí)現(xiàn)自動(dòng)替換配置文件的應(yīng)用。

處理流程

Consul-Template流程

完整教程入口:http://www.itdecent.cn/p/6534ac03ab28

結(jié)束語

網(wǎng)關(guān)改造耗時(shí)1個(gè)月時(shí)間完成。部署上線后到現(xiàn)在已經(jīng)快1年了,再?zèng)]出現(xiàn)過因?yàn)榫W(wǎng)關(guān)故障而導(dǎo)致服務(wù)不可用的情況了。

參考資料

openresty:http://openresty.org/cn/
jwt:http://www.itdecent.cn/p/576dbf44b2ae
consul-template:https://www.hi-linux.com/posts/36431.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容