引言
在當今快速演變的技術(shù)場景中,服務(wù)設(shè)計不僅僅是遵循通用的設(shè)計規(guī)范和最佳實踐的問題,它更深層次地觸及到如何在滿足這些標準的同時,確保服務(wù)能夠靈活適應(yīng)未來的變化、滿足用戶的期望。本篇文章旨在探討在遵循通用設(shè)計規(guī)范之外,服務(wù)設(shè)計過程中需要考慮的關(guān)鍵因素。希望經(jīng)過我們一系列的分享,能和大家一起討論如何將API設(shè)計的易對接,易理解和易擴展。
系統(tǒng)介紹
?京東企業(yè)業(yè)務(wù)VOP(智采):以API形式為客戶提供京東供應(yīng)鏈能力;VOP提供上百個標準API服務(wù),為上千家客戶搭建自采商城提供底層能力。
服務(wù)設(shè)計tips
1、服務(wù)路徑和模塊
? 服務(wù)路徑從域名后開始,應(yīng)有統(tǒng)一的一級路徑,如domain/api/,這樣做的好處是:將所有API請求集中到一個明確的路徑下可以簡化安全控制的實施。比如可以在NP或API網(wǎng)關(guān)層面上,針對特定路徑應(yīng)用特定的安全策略,如認證、授權(quán)、日志、限流和監(jiān)控等,從而增強API的安全性;
?再向后則是業(yè)務(wù)分組,比如訂單/api/order,商品/api/product等,可以讓調(diào)用方清晰明了此接口是屬于哪個業(yè)務(wù)模塊。
?還要考慮各服務(wù)的粒度,比如:商品價格是做成單獨的服務(wù),還是耦合在商品詳情接口直接吐出呢?訂單物流信息應(yīng)該包含在訂單詳情服務(wù)里嗎等等??紤]這部分,需要以當前業(yè)務(wù)場景作為基礎(chǔ),拿VOP來說,因為有客戶級別的商品變價消息存在,所以調(diào)用方在拉取到商品變價消息之后,可以單獨查詢價格服務(wù)更新自己本地存儲的價格,而不用全量查詢商品信息,所以解耦更優(yōu)。在保證業(yè)務(wù)場景完整度的情況下,盡可能的為調(diào)用方提供可靈活組裝的服務(wù)。
2、服務(wù)請求方式
對于大部分服務(wù)來說,應(yīng)支持ajax請求,并且指定固定的數(shù)據(jù)格式(JSON/XML等),既方便后續(xù)擴展,又能支持業(yè)內(nèi)絕大部分的客戶業(yè)務(wù)處理。但對于部分特殊場景的服務(wù),如收銀臺,登錄/登出等接口,則需要非ajax請求,由服務(wù)端進行后續(xù)的轉(zhuǎn)發(fā)跳轉(zhuǎn)處理。
3、服務(wù)出入?yún)?/h3>
?API服務(wù)出入?yún)⒌亩x,尤其是對外開放服務(wù)的出入?yún)ⅲ枰瓤紤]擴展性,防止后續(xù)擴展受限以至于不得已增加新的接口導(dǎo)致重復(fù)建設(shè)、接口混亂不清、同一個接口有好些大同小異的版本等問題;其次就是對于各字段的定義和說明,通用的規(guī)范不用贅述,想要強調(diào)的是:
?為了后續(xù)的擴展性以及對調(diào)用方屏蔽服務(wù)內(nèi)部變化,某些數(shù)字字段要優(yōu)先設(shè)計成Long甚至于直接定義成字符串,防止服務(wù)內(nèi)部變化(比如某些字段Integer不夠存儲需要轉(zhuǎn)換成Long時),給調(diào)用方增加額外的改造工作量。
?在某些情況下,字段可能需要區(qū)分“未設(shè)置”和“零值”這兩種情況。此時,需要考慮將字段設(shè)計為包裝類型,這樣,null可以表示“未設(shè)置”,而具體的零值(如0、0.0等)則有其特定含義。
?字段取舍:出參字段非必要不暴露,在滿足接口功能要求的前提下,盡可能的減少字段暴露;對于一些敏感字段,比如客戶地址,手機號等信息,更要仔細斟酌是否需要吐出該字段,如必需,則還需要考慮字段脫敏和加解密。
?字段定義及說明:詳細且合規(guī)的字段定義和說明可以降低調(diào)用方對于服務(wù)字段的理解成本,確保數(shù)據(jù)的一致性和準確性,常見的說明有以下幾種:
?字段長度的限制
?數(shù)字類型字段的范圍
?各業(yè)務(wù)域相同含義的字段名保持一致
?字段格式說明:對于日期、時間等特殊格式的字段,明確其格式
?枚舉類型的說明:對于枚舉類型的字段,所有可能的枚舉值,并對每個值的含義進行說明等等
?已發(fā)布服務(wù)需要新增出/入?yún)?已經(jīng)發(fā)布出去的服務(wù),如果要新增出入?yún)?,需要考慮對調(diào)用方的影響,如序列化/反序列化,字段處理等等;若調(diào)用方在進行接口出參反序列化時使用的是嚴格模式,那服務(wù)端隨意的新增 返回字段將會使調(diào)用方在服務(wù)出參數(shù)據(jù)反序列化時失敗,進而影響到業(yè)務(wù)進行。我們可以在服務(wù)入?yún)⒃黾訑U展字段,當調(diào)用方傳入指定數(shù)據(jù)時,服務(wù)再在返回體中增加新的出參。
?出參格式統(tǒng)一:對外服務(wù)要求有固定的格式,清晰的標明處理結(jié)果,狀態(tài)碼以及業(yè)務(wù)數(shù)據(jù)等字段,能讓調(diào)用方清晰的進行業(yè)務(wù)處理已經(jīng)異常處理,如:
{
"success": true,
"resultMessage": "success",
"resultCode": "0000",
"result": {}
}
4、服務(wù)內(nèi)業(yè)務(wù)處理的一些建議
?對于服務(wù)邏輯處理來說,盡量支持批量數(shù)據(jù)的處理。對于調(diào)用方來說,可以將自身重心放在數(shù)據(jù)處理邏輯上,而不是如何高并發(fā)的調(diào)用服務(wù)來處理這部分數(shù)據(jù),可以有效減輕服務(wù)方壓力。如訂單/商品查詢,發(fā)票開具等服務(wù)
?做水平越權(quán)處理:防止調(diào)用方查詢到不屬于自身的數(shù)據(jù),具體校驗嚴格程度視業(yè)務(wù)而定。比如VOP對于大部分的查詢服務(wù),都允許屬于同一合同下的不同pin互相查詢數(shù)據(jù)(訂單,發(fā)票等)
?對于重點業(yè)務(wù)邏輯需要多線程處理時,盡量使用單獨的線程池,防止線程池內(nèi)線程被其他業(yè)務(wù)搶占影響重要業(yè)務(wù)
5、異常處理
?對于開放服務(wù),尤其是寫操作的服務(wù),定義明確的異常碼是尤為重要的,調(diào)用方系統(tǒng)需要準確的錯誤碼和錯誤信息來自動化地處理接口調(diào)用的結(jié)果:重試、告警或直接忽略、業(yè)務(wù)中斷或繼續(xù)進行。 相信我們都遇到過調(diào)用一些沒有任務(wù)錯誤碼文檔的接口,調(diào)用方完全不知道這個接口會拋出或者返回一些什么樣的錯誤碼以及錯誤描述,沒辦法決策這些異常是否可以展示給客戶!(對于一些技術(shù)化的錯誤提示:比如'調(diào)用XX接口異常','XX數(shù)據(jù)為空'等等)
?第3點時我們提到了服務(wù)的出參格式需要統(tǒng)一,所以在出現(xiàn)業(yè)務(wù)異常時,我們要包裝合適的異常碼以及異常信息返給調(diào)用方,另外還需要注意出現(xiàn)未知異常(空指針,調(diào)用依賴接口超時等)時,對出參的包裝,防止出現(xiàn)直接拋異常給調(diào)用方的情況,可以在最外層使用攔截器做未知異常的兜底出參處理。
6、服務(wù)的強弱依賴
在構(gòu)建一個復(fù)雜的對外服務(wù)時通常會依賴多個外部接口。對于這些依賴,我們可以將它們分為兩類:強依賴和弱依賴。強依賴的接口是服務(wù)運行不可或缺的部分,如果這類接口出現(xiàn)異常,我們通常采用的策略是短路處理,即立即中斷當前操作,以避免進一步的錯誤傳播或數(shù)據(jù)不一致。
另一方面,對于弱依賴的接口,它們對主流程的影響相對較小,可以容忍一定程度的異?;蚴 T谶@種情況下,如果弱依賴接口發(fā)生異常,我們可以選擇忽略這些異常,并繼續(xù)執(zhí)行服務(wù)的核心流程。同時,為了確保服務(wù)的穩(wěn)定性和問題的可追蹤性,我們會實施業(yè)務(wù)告警機制,以便及時發(fā)現(xiàn)并處理這些異常情況。這種做法旨在保證服務(wù)的整體可用性,同時確保問題不會在無人知曉的情況下被忽略。
7、服務(wù)的監(jiān)控和日志記錄
?日志記錄:對于一個對外服務(wù)(尤其是直接面向外部客戶的服務(wù))來說,除了系統(tǒng)中常用的技術(shù)監(jiān)控以外,服務(wù)出入?yún)⑷罩镜挠涗浭侵陵P(guān)重要的一環(huán),尤其是涉及資金、訂單、支付等各種類型的寫操作服務(wù),一方面我們可以監(jiān)控重要服務(wù)中,各調(diào)用方都使用了哪些字段,從而為在后續(xù)新增、下線或修改某些字段時提供風險評估數(shù)據(jù);另一方面還可以從日志記錄出入?yún)⒅?strong>分析業(yè)務(wù)數(shù)據(jù),為后續(xù)業(yè)務(wù)決策、業(yè)務(wù)告警等提供數(shù)據(jù)支撐。另外,當出現(xiàn)調(diào)用方和服務(wù)方數(shù)據(jù)不一致而產(chǎn)生嚴重后果的情況時,這些日志的記錄也是判斷問題點的重要信息。
?調(diào)用數(shù)據(jù)收集:對于開放平臺的流量細節(jié)做統(tǒng)計和分析,是做流量治理、保障服務(wù)安全的重要前提,通過對各調(diào)用方的調(diào)用分析可以得知調(diào)用方的調(diào)用頻率、調(diào)用規(guī)律、請求是否合理等信息,對于一些非法調(diào)用(刷單、爬蟲等)進行有效識別,可以統(tǒng)計出調(diào)用方的平均調(diào)用量進而為服務(wù)限流、安全防護等提供數(shù)據(jù)支持。
8、合理設(shè)置降級點
在構(gòu)建復(fù)雜的服務(wù)時,特別是在分布式系統(tǒng)和微服務(wù)架構(gòu)的背景下,我們一般需要依賴很多第三方服務(wù)或數(shù)據(jù)。在這種情況下,服務(wù)降級策略就成為確保整體系統(tǒng)穩(wěn)定性的關(guān)鍵。服務(wù)降級主要是指在某些服務(wù)模塊遇到性能瓶頸或者故障時,主動地減少或關(guān)閉這些組件的某些功能,以此來保障整個系統(tǒng)的核心運作。
為了有效實施服務(wù)降級,需要在開發(fā)和設(shè)計過程中識別到潛在的風險點,并據(jù)此劃分出服務(wù)中的關(guān)鍵與非關(guān)鍵模塊。在非關(guān)鍵模塊的代碼層面嵌入降級邏輯,讓我們可以在出現(xiàn)問題時,手動或自動執(zhí)行降級操作,從而確保關(guān)鍵服務(wù)的持續(xù)可用性。這種設(shè)計不僅有助于提升系統(tǒng)的魯棒性,也可以有效地減少系統(tǒng)故障時對用戶的影響。
9、處理過時服務(wù)
每一個系統(tǒng)都會出現(xiàn)很多初期簡單設(shè)計、只滿足簡單業(yè)務(wù)場景的服務(wù),隨著業(yè)務(wù)的發(fā)展,初期設(shè)計的很多服務(wù)將不再能滿足新的業(yè)務(wù)場景,在原服務(wù)無法擴展的情況下,我們一般會增加新的服務(wù)(需要能覆蓋舊服務(wù)的能力),久而久之,就會產(chǎn)生很多幾乎無客戶使用的過時服務(wù),有可能會產(chǎn)生影響代碼問題排查、觸發(fā)業(yè)務(wù)場景漏洞等問題,對于這樣的服務(wù),我們需要有兩種處理辦法:
?若邏輯兼容,可以將舊服務(wù)路由到新服務(wù)
?下線處理,但不建議直接刪除代碼,可以統(tǒng)一攔截需要下線的路徑,給調(diào)用方返回統(tǒng)一的錯誤碼和提示信息,一旦監(jiān)控到攔截告警或者調(diào)用方反饋,可以先停止攔截并和調(diào)用方溝通后續(xù)處理方案。
10、不要相信任何調(diào)用方和依賴的第三方
在服務(wù)設(shè)計和編程過程中,對于所有依賴的第三方的服務(wù)都要保持不信任的前提,對其返回的數(shù)據(jù)、服務(wù)的超時時間、異常的返回等均需要細致處理,要考慮每一個可能出現(xiàn)的問題點,防止某一個依賴的服務(wù)出現(xiàn)問題進而影響全局。
同樣的,也不能信任調(diào)用方。無論事先如何保證或者規(guī)約,都無法保證調(diào)用方的調(diào)用行為符合預(yù)期,所以我們在服務(wù)上線前就需要考慮和構(gòu)建多種防御機制。這包括但不限于:實施有效的限流措施、進行嚴格的輸入驗證、以及設(shè)置精細的權(quán)限控制等。通過這樣的設(shè)計,我們不僅能夠提升服務(wù)的穩(wěn)定性和安全性,還能確保在面對不可預(yù)測的外部因素時,我們的系統(tǒng)能夠保持彈性和韌性。
保障服務(wù)和信息安全的措施
(1)敏感字段
在B2B場景中,企業(yè)對于用戶敏感數(shù)據(jù)的要求較高,比如手機號,用戶名,住址等信息,這就要求服務(wù)提供方在接口出參時對于這些數(shù)據(jù)進行加密處理,加密這些信息可以防止在數(shù)據(jù)傳輸過程中被未經(jīng)授權(quán)的第三方截取和讀取,從而保護客戶和企業(yè)的隱私和利益。
加密可以在不同的層面上實施:
?傳輸層加密:使用SSL/TLS等協(xié)議對客戶端和服務(wù)器之間的整個通信進行加密。這意味著所有傳輸?shù)臄?shù)據(jù),包括敏感字段,都會被加密,從而保障數(shù)據(jù)在傳輸過程中的安全。
?應(yīng)用層加密:在數(shù)據(jù)發(fā)送之前,直接在應(yīng)用層對特定的敏感字段進行加密。這通常涉及到使用加密算法(如AES、RSA等)對敏感數(shù)據(jù)進行編碼。即便在傳輸層已經(jīng)有加密,應(yīng)用層加密也提供了另一層安全保障。
?數(shù)據(jù)庫層加密:對存儲在數(shù)據(jù)庫中的敏感字段進行加密,這種方式使用不多,一般用來保存密碼等信息。
在使用敏感字段加密時,需要注意以下幾點:
?選擇合適的加密標準和算法,確保是業(yè)內(nèi)常用的、經(jīng)過驗證的。
?管理好加密密鑰,密鑰的生成、存儲、交換和銷毀都應(yīng)當安全可靠。
?考慮性能影響,加密和解密操作會增加計算復(fù)雜度,需要確保服務(wù)性能不會因此受到明顯影響。
(2)系統(tǒng)準入
部分對系統(tǒng)安全要求較高的客戶,會要求服務(wù)方對非法的調(diào)用方進行攔截,防止非法第三方在獲取到身份信息之后使用客戶身份進行調(diào)用進而產(chǎn)生資損或更多的數(shù)據(jù)泄露。面對這種情況,我們可以使用設(shè)置IP黑白名單的方式來進行攔截,限制某個身份標識只能是指定的ip來訪問或指定的ip不能訪問。
IP白名單:只有在白名單中的IP地址被允許訪問系統(tǒng)或接口。不在列表中的所有IP地址都會被拒絕。 優(yōu)點:提供了高級別的安全性,在客戶維度下,只有預(yù)先批準的IP地址可以訪問,其余IP均不能以該客戶身份進行訪問??梢杂行Х乐刮词跈?quán)的訪問嘗試,在某些特定場景下,甚至可以取消授權(quán)校驗,IP即代表客戶身份。 缺點:管理起來可能比較繁瑣,尤其是當合法用戶的IP地址經(jīng)常變化時。對于使用動態(tài)IP地址的用戶不太友好。 適用場景:適用于訪問者數(shù)量有限且IP地址固定或變化不大的環(huán)境。
?
IP黑名單:在黑名單中的IP地址不被允許訪問系統(tǒng)或接口。不在列表中的IP地址則可以訪問。 優(yōu)點:簡單易于管理,只需要列出已知的惡意IP地址。對于大多數(shù)合法用戶來說,不會影響他們的訪問。 缺點:安全性較低,因為新的未知攻擊者仍然可以訪問系統(tǒng)。惡意用戶可以通過更換IP地址來繞過黑名單控制。適用場景較少,目前VOP平臺尚未有客戶選擇此種IP校驗類型。 適用場景:適用于開放性較高的環(huán)境,或者作為其他安全措施的補充。
(3)接口出入?yún)⒎来鄹?/h3>
確保通過接口發(fā)送的數(shù)據(jù)在傳輸過程中未被修改。這對于保障調(diào)用雙方數(shù)據(jù)的完整性和安全性至關(guān)重要,常用的防篡改方法有以下幾種:
a. 使用HTTPS
確保所有數(shù)據(jù)傳輸通過HTTPS進行,這樣數(shù)據(jù)在傳輸過程中會被加密,從而降低被截取和篡改的風險。
b. 數(shù)字簽名
在發(fā)送數(shù)據(jù)前,調(diào)用方使用數(shù)字簽名對數(shù)據(jù)進行簽名,接收方可以使用相應(yīng)的公鑰驗證簽名,以確保數(shù)據(jù)自簽名以來未被修改。
數(shù)字簽名的步驟如下:
發(fā)送方使用私鑰對數(shù)據(jù)(可以是數(shù)據(jù)的哈希值或直接將參數(shù)按規(guī)則拼接成字符串)進行簽名。
發(fā)送方將數(shù)據(jù)和簽名一起發(fā)送給接收方。
接收方收到數(shù)據(jù)后,使用發(fā)送方的公鑰驗證簽名。
如果簽名驗證成功,說明數(shù)據(jù)未被篡改;如果失敗,說明數(shù)據(jù)在傳輸過程中可能遭到篡改。
c. 消息認證碼(MAC)
與數(shù)字簽名類似,消息認證碼(MAC)是一種確保消息完整性的技術(shù),它使用一個密鑰和消息內(nèi)容生成一個MAC值。接收方使用相同的密鑰生成MAC值,并與發(fā)送方提供的MAC值進行比較。
d.使用API密鑰和時間戳
結(jié)合API密鑰和時間戳可以提供另一層安全性。時間戳可以防止重放攻擊,API密鑰則確保只有授權(quán)的用戶可以發(fā)送請求。
(4)出入?yún)⒓咏饷?/h3>
出入?yún)⒓咏饷苁侵冈诳蛻舳税l(fā)送請求到服務(wù)器(入?yún)⒔饷埽┖头?wù)器返回響應(yīng)到客戶端(出參加密)的過程中,對數(shù)據(jù)進行加密和解密的操作。這樣做可以保護數(shù)據(jù)在傳輸過程中的安全性,防止敏感信息被竊取或篡改。
出入?yún)⒁笕羌用芎蟮臄?shù)據(jù),這種需求一般來自銀行或者國央企等十分看重數(shù)據(jù)安全的客戶。作為接口數(shù)據(jù)安全的可選項,并不適用于全部客戶,而且要求數(shù)據(jù)密文傳輸?shù)目蛻簦话愣紩付〝?shù)據(jù)的加密方式,有可能是業(yè)內(nèi)通用的加密方式,也有可能是客戶內(nèi)部自定義的一個jar包。
關(guān)于后者,我們搭建了一個可以加載不同客戶的加密SDK的ECI平臺,通過這個平臺,將每個客戶提供的加密SDK隔離加載,確保不會因為某個客戶SDK出現(xiàn)問題而影響全部客戶;在平臺上傳客戶的SDK并加載完成之后,會對外提供一個服務(wù)接口,該接口支持客戶維度的加密和解密兩種操作。我們只需要按客戶維度配置該客戶對應(yīng)的加解密方法,在攔截器層面進行統(tǒng)一處理加解密操作即可,對實際業(yè)務(wù)代碼完全無侵入。
偽代碼示例:
/**
* @description 指定客戶接口出入?yún)⒓咏饷軘r截器
*/
@Service
public class EncryptInterceptor extends HandlerInterceptorAdapter {
//請求進入,業(yè)務(wù)處理前解密
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(!(request instanceof DecryptionRequestWrapper)){
return true;
}
//獲取客戶授權(quán)信息,獲取到當前客戶身份
try {
//判斷該客戶是否開啟接口加解密
//組織參數(shù),獲取原始入?yún)?,進行解密操作
//解密完成后,將解密得到的參數(shù)全部加入到request的參數(shù)內(nèi)
//繼續(xù)向下進行
return true;
} catch (Throwable throwable){
//異常處理
}
}
//返回前加密
@Override
public void postHandle(HttpServletRequest request,HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
//獲取客戶身份標識
try{
//判斷該客戶是否開啟接口加解密
//調(diào)用原始的 postHandle 方法,讓響應(yīng)數(shù)據(jù)被寫入包裝類的輸出流
//調(diào)用加密方法,獲取加密后數(shù)據(jù)
//將加密后的數(shù)據(jù)寫回響應(yīng)
}catch (Throwable throwable){
//異常處理
}
}
}
結(jié)語
構(gòu)建一個健壯的服務(wù)和系統(tǒng)不僅是技術(shù)領(lǐng)域?qū)I(yè)人士的終極追求,也是確保我們能夠在面對未來不斷演變的業(yè)務(wù)需求和技術(shù)挑戰(zhàn)時保持競爭力的關(guān)鍵。在遵守廣泛認可的設(shè)計規(guī)范之外,我們必須進一步探索和優(yōu)化,確保我們的服務(wù)不僅可以全面覆蓋各種業(yè)務(wù)場景,還要在安全性、可擴展性以及可伸縮性和降級能力方面表現(xiàn)出色。
這種追求不僅要求我們在設(shè)計之初就深入考慮潛在的風險和挑戰(zhàn),還要求我們在整個開發(fā)過程中對服務(wù)進行持續(xù)評估和優(yōu)化。這樣的系統(tǒng)將成為支持企業(yè)業(yè)務(wù)持續(xù)增長和創(chuàng)新的堅實基礎(chǔ),幫助我們在不斷變化的技術(shù)和業(yè)務(wù)環(huán)境中保持領(lǐng)先!
讀完此篇,加入光榮的進化吧!
?
審核編輯 黃宇
-
API
+關(guān)注
關(guān)注
2文章
1472瀏覽量
61749
發(fā)布評論請先 登錄
相關(guān)推薦
評論