动态

详情 返回 返回

看完這篇,你的服務設計能力將再次進化! - 动态 详情

引言

在當今快速演變的技術場景中,服務設計不僅僅是遵循通用的設計規範和最佳實踐的問題,它更深層次地觸及到如何在滿足這些標準的同時,確保服務能夠靈活適應未來的變化、滿足用户的期望。本篇文章旨在探討在遵循通用設計規範之外,服務設計過程中需要考慮的關鍵因素。希望經過我們一系列的分享,能和大家一起討論如何將API設計的易對接,易理解和易擴展。

系統介紹

京東企業業務VOP(智採):以API形式為客户提供京東供應鏈能力;VOP提供上百個標準API服務,為上千家客户搭建自採商城提供底層能力。

服務設計tips

1、服務路徑和模塊

• 服務路徑從域名後開始,應有統一的一級路徑,如domain/api/,這樣做的好處是:將所有API請求集中到一個明確的路徑下可以簡化安全控制的實施。比如可以在NP或API網關層面上,針對特定路徑應用特定的安全策略,如認證、授權、日誌、限流和監控等,從而增強API的安全性;

•再向後則是業務分組,比如訂單/api/order,商品/api/product等,可以讓調用方清晰明瞭此接口是屬於哪個業務模塊。

•還要考慮各服務的粒度,比如:商品價格是做成單獨的服務,還是耦合在商品詳情接口直接吐出呢?訂單物流信息應該包含在訂單詳情服務裏嗎等等。考慮這部分,需要以當前業務場景作為基礎,拿VOP來説,因為有客户級別的商品變價消息存在,所以調用方在拉取到商品變價消息之後,可以單獨查詢價格服務更新自己本地存儲的價格,而不用全量查詢商品信息,所以解耦更優。在保證業務場景完整度的情況下,儘可能的為調用方提供可靈活組裝的服務。

2、服務請求方式

對於大部分服務來説,應支持ajax請求,並且指定固定的數據格式(JSON/XML等),既方便後續擴展,又能支持業內絕大部分的客户業務處理。但對於部分特殊場景的服務,如收銀台,登錄/登出等接口,則需要非ajax請求,由服務端進行後續的轉發跳轉處理。

3、服務出入參

•API服務出入參的定義,尤其是對外開放服務的出入參,需要先考慮擴展性,防止後續擴展受限以至於不得已增加新的接口導致重複建設、接口混亂不清、同一個接口有好些大同小異的版本等問題;其次就是對於各字段的定義和説明,通用的規範不用贅述,想要強調的是:

•為了後續的擴展性以及對調用方屏蔽服務內部變化,某些數字字段要優先設計成Long甚至於直接定義成字符串,防止服務內部變化(比如某些字段Integer不夠存儲需要轉換成Long時),給調用方增加額外的改造工作量。

•在某些情況下,字段可能需要區分“未設置”和“零值”這兩種情況。此時,需要考慮將字段設計為包裝類型,這樣,null可以表示“未設置”,而具體的零值(如0、0.0等)則有其特定含義。

•字段取捨:出參字段非必要不暴露,在滿足接口功能要求的前提下,儘可能的減少字段暴露;對於一些敏感字段,比如客户地址,手機號等信息,更要仔細斟酌是否需要吐出該字段,如必需,則還需要考慮字段脱敏和加解密

•字段定義及説明:詳細且合規的字段定義和説明可以降低調用方對於服務字段的理解成本,確保數據的一致性和準確性,常見的説明有以下幾種:

◦字段長度的限制

◦數字類型字段的範圍

◦各業務域相同含義的字段名保持一致

◦字段格式説明:對於日期、時間等特殊格式的字段,明確其格式

◦枚舉類型的説明:對於枚舉類型的字段,所有可能的枚舉值,並對每個值的含義進行説明等等

•已發佈服務需要新增出/入參:已經發布出去的服務,如果要新增出入參,需要考慮對調用方的影響,如序列化/反序列化,字段處理等等;若調用方在進行接口出參反序列化時使用的是嚴格模式,那服務端隨意的新增 返回字段將會使調用方在服務出參數據反序列化時失敗,進而影響到業務進行。我們可以在服務入參增加擴展字段,當調用方傳入指定數據時,服務再在返回體中增加新的出參

•出參格式統一:對外服務要求有固定的格式,清晰的標明處理結果,狀態碼以及業務數據等字段,能讓調用方清晰的進行業務處理已經異常處理,如:

    {
        "success": true,
        "resultMessage": "success",
        "resultCode": "0000",
        "result": {}
    }

4、服務內業務處理的一些建議

•對於服務邏輯處理來説,儘量支持批量數據的處理。對於調用方來説,可以將自身重心放在數據處理邏輯上,而不是如何高併發的調用服務來處理這部分數據,可以有效減輕服務方壓力。如訂單/商品查詢,發票開具等服務

•做水平越權處理:防止調用方查詢到不屬於自身的數據,具體校驗嚴格程度視業務而定。比如VOP對於大部分的查詢服務,都允許屬於同一合同下的不同pin互相查詢數據(訂單,發票等)

•對於重點業務邏輯需要多線程處理時,儘量使用單獨的線程池,防止線程池內線程被其他業務搶佔影響重要業務

5、異常處理

•對於開放服務,尤其是寫操作的服務,定義明確的異常碼是尤為重要的,調用方系統需要準確的錯誤碼和錯誤信息來自動化地處理接口調用的結果:重試、告警或直接忽略業務中斷或繼續進行。 相信我們都遇到過調用一些沒有任務錯誤碼文檔的接口,調用方完全不知道這個接口會拋出或者返回一些什麼樣的錯誤碼以及錯誤描述,沒辦法決策這些異常是否可以展示給客户!(對於一些技術化的錯誤提示:比如'調用XX接口異常','XX數據為空'等等)

•第3點時我們提到了服務的出參格式需要統一,所以在出現業務異常時,我們要包裝合適的異常碼以及異常信息返給調用方,另外還需要注意出現未知異常(空指針,調用依賴接口超時等)時,對出參的包裝,防止出現直接拋異常給調用方的情況,可以在最外層使用攔截器做未知異常的兜底出參處理。

6、服務的強弱依賴

在構建一個複雜的對外服務時通常會依賴多個外部接口。對於這些依賴,我們可以將它們分為兩類:強依賴和弱依賴。強依賴的接口是服務運行不可或缺的部分,如果這類接口出現異常,我們通常採用的策略是短路處理,即立即中斷當前操作,以避免進一步的錯誤傳播或數據不一致。

另一方面,對於弱依賴的接口,它們對主流程的影響相對較小,可以容忍一定程度的異常或失敗。在這種情況下,如果弱依賴接口發生異常,我們可以選擇忽略這些異常,並繼續執行服務的核心流程。同時,為了確保服務的穩定性和問題的可追蹤性,我們會實施業務告警機制,以便及時發現並處理這些異常情況。這種做法旨在保證服務的整體可用性,同時確保問題不會在無人知曉的情況下被忽略。

7、服務的監控和日誌記錄

日誌記錄:對於一個對外服務(尤其是直接面向外部客户的服務)來説,除了系統中常用的技術監控以外,服務出入參日誌的記錄是至關重要的一環,尤其是涉及資金、訂單、支付等各種類型的寫操作服務,一方面我們可以監控重要服務中,各調用方都使用了哪些字段,從而為在後續新增、下線或修改某些字段時提供風險評估數據;另一方面還可以從日誌記錄出入參中分析業務數據,為後續業務決策、業務告警等提供數據支撐。另外,當出現調用方和服務方數據不一致而產生嚴重後果的情況時,這些日誌的記錄也是判斷問題點的重要信息。

調用數據收集:對於開放平台的流量細節做統計和分析,是做流量治理、保障服務安全的重要前提,通過對各調用方的調用分析可以得知調用方的調用頻率、調用規律、請求是否合理等信息,對於一些非法調用(刷單、爬蟲等)進行有效識別,可以統計出調用方的平均調用量進而為服務限流、安全防護等提供數據支持。

8、合理設置降級點

在構建複雜的服務時,特別是在分佈式系統和微服務架構的背景下,我們一般需要依賴很多第三方服務或數據。在這種情況下,服務降級策略就成為確保整體系統穩定性的關鍵。服務降級主要是指在某些服務模塊遇到性能瓶頸或者故障時,主動地減少或關閉這些組件的某些功能,以此來保障整個系統的核心運作。

為了有效實施服務降級,需要在開發和設計過程中識別到潛在的風險點,並據此劃分出服務中的關鍵與非關鍵模塊。在非關鍵模塊的代碼層面嵌入降級邏輯,讓我們可以在出現問題時,手動或自動執行降級操作,從而確保關鍵服務的持續可用性。這種設計不僅有助於提升系統的魯棒性,也可以有效地減少系統故障時對用户的影響。

9、處理過時服務

每一個系統都會出現很多初期簡單設計、只滿足簡單業務場景的服務,隨着業務的發展,初期設計的很多服務將不再能滿足新的業務場景,在原服務無法擴展的情況下,我們一般會增加新的服務(需要能覆蓋舊服務的能力),久而久之,就會產生很多幾乎無客户使用的過時服務,有可能會產生影響代碼問題排查、觸發業務場景漏洞等問題,對於這樣的服務,我們需要有兩種處理辦法:

•若邏輯兼容,可以將舊服務路由到新服務

•下線處理,但不建議直接刪除代碼,可以統一攔截需要下線的路徑,給調用方返回統一的錯誤碼和提示信息,一旦監控到攔截告警或者調用方反饋,可以先停止攔截並和調用方溝通後續處理方案。

10、不要相信任何調用方和依賴的第三方

在服務設計和編程過程中,對於所有依賴的第三方的服務都要保持不信任的前提,對其返回的數據、服務的超時時間、異常的返回等均需要細緻處理,要考慮每一個可能出現的問題點,防止某一個依賴的服務出現問題進而影響全局。

同樣的,也不能信任調用方。無論事先如何保證或者規約,都無法保證調用方的調用行為符合預期,所以我們在服務上線前就需要考慮和構建多種防禦機制。這包括但不限於:實施有效的限流措施、進行嚴格的輸入驗證、以及設置精細的權限控制等。通過這樣的設計,我們不僅能夠提升服務的穩定性和安全性,還能確保在面對不可預測的外部因素時,我們的系統能夠保持彈性和韌性。

保障服務和信息安全的措施

(1)敏感字段

在B2B場景中,企業對於用户敏感數據的要求較高,比如手機號,用户名,住址等信息,這就要求服務提供方在接口出參時對於這些數據進行加密處理,加密這些信息可以防止在數據傳輸過程中被未經授權的第三方截取和讀取,從而保護客户和企業的隱私和利益。

加密可以在不同的層面上實施:

•傳輸層加密:使用SSL/TLS等協議對客户端和服務器之間的整個通信進行加密。這意味着所有傳輸的數據,包括敏感字段,都會被加密,從而保障數據在傳輸過程中的安全。

•應用層加密:在數據發送之前,直接在應用層對特定的敏感字段進行加密。這通常涉及到使用加密算法(如AES、RSA等)對敏感數據進行編碼。即便在傳輸層已經有加密,應用層加密也提供了另一層安全保障。

•數據庫層加密:對存儲在數據庫中的敏感字段進行加密,這種方式使用不多,一般用來保存密碼等信息。

在使用敏感字段加密時,需要注意以下幾點:

•選擇合適的加密標準和算法,確保是業內常用的、經過驗證的。

•管理好加密密鑰,密鑰的生成、存儲、交換和銷燬都應當安全可靠。

•考慮性能影響,加密和解密操作會增加計算複雜度,需要確保服務性能不會因此受到明顯影響。

(2)系統准入

部分對系統安全要求較高的客户,會要求服務方對非法的調用方進行攔截,防止非法第三方在獲取到身份信息之後使用客户身份進行調用進而產生資損或更多的數據泄露。面對這種情況,我們可以使用設置IP黑白名單的方式來進行攔截,限制某個身份標識只能是指定的ip來訪問或指定的ip不能訪問。

IP白名單:只有在白名單中的IP地址被允許訪問系統或接口。不在列表中的所有IP地址都會被拒絕。 優點:提供了高級別的安全性,在客户維度下,只有預先批准的IP地址可以訪問,其餘IP均不能以該客户身份進行訪問。可以有效防止未授權的訪問嘗試,在某些特定場景下,甚至可以取消授權校驗,IP即代表客户身份。 缺點:管理起來可能比較繁瑣,尤其是當合法用户的IP地址經常變化時。對於使用動態IP地址的用户不太友好。 適用場景:適用於訪問者數量有限且IP地址固定或變化不大的環境。



IP黑名單:在黑名單中的IP地址不被允許訪問系統或接口。不在列表中的IP地址則可以訪問。 優點:簡單易於管理,只需要列出已知的惡意IP地址。對於大多數合法用户來説,不會影響他們的訪問。 缺點:安全性較低,因為新的未知攻擊者仍然可以訪問系統。惡意用户可以通過更換IP地址來繞過黑名單控制。適用場景較少,目前VOP平台尚未有客户選擇此種IP校驗類型。 適用場景:適用於開放性較高的環境,或者作為其他安全措施的補充。

(3)接口出入參防篡改

確保通過接口發送的數據在傳輸過程中未被修改。這對於保障調用雙方數據的完整性和安全性至關重要,常用的防篡改方法有以下幾種:

a. 使用HTTPS

確保所有數據傳輸通過HTTPS進行,這樣數據在傳輸過程中會被加密,從而降低被截取和篡改的風險。

b. 數字簽名

在發送數據前,調用方使用數字簽名對數據進行簽名,接收方可以使用相應的公鑰驗證簽名,以確保數據自簽名以來未被修改。

數字簽名的步驟如下:

發送方使用私鑰對數據(可以是數據的哈希值或直接將參數按規則拼接成字符串)進行簽名。

發送方將數據和簽名一起發送給接收方。

接收方收到數據後,使用發送方的公鑰驗證簽名。

如果簽名驗證成功,説明數據未被篡改;如果失敗,説明數據在傳輸過程中可能遭到篡改。

c. 消息認證碼(MAC)

與數字簽名類似,消息認證碼(MAC)是一種確保消息完整性的技術,它使用一個密鑰和消息內容生成一個MAC值。接收方使用相同的密鑰生成MAC值,並與發送方提供的MAC值進行比較。

d.使用API密鑰和時間戳

結合API密鑰和時間戳可以提供另一層安全性。時間戳可以防止重放攻擊,API密鑰則確保只有授權的用户可以發送請求。

(4)出入參加解密

出入參加解密是指在客户端發送請求到服務器(入參解密)和服務器返回響應到客户端(出參加密)的過程中,對數據進行加密和解密的操作。這樣做可以保護數據在傳輸過程中的安全性,防止敏感信息被竊取或篡改。

出入參要求全是加密後的數據,這種需求一般來自銀行或者國央企等十分看重數據安全的客户。作為接口數據安全的可選項,並不適用於全部客户,而且要求數據密文傳輸的客户,一般都會指定數據的加密方式,有可能是業內通用的加密方式,也有可能是客户內部自定義的一個jar包。

關於後者,我們搭建了一個可以加載不同客户的加密SDK的ECI平台,通過這個平台,將每個客户提供的加密SDK隔離加載,確保不會因為某個客户SDK出現問題而影響全部客户;在平台上傳客户的SDK並加載完成之後,會對外提供一個服務接口,該接口支持客户維度的加密和解密兩種操作。我們只需要按客户維度配置該客户對應的加解密方法,在攔截器層面進行統一處理加解密操作即可,對實際業務代碼完全無侵入。

偽代碼示例:

/**
 * @description 指定客户接口出入參加解密攔截器
 */
@Service
public class EncryptInterceptor extends HandlerInterceptorAdapter {
        //請求進入,業務處理前解密
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(!(request instanceof  DecryptionRequestWrapper)){
            return true;
        }
        //獲取客户授權信息,獲取到當前客户身份
        try {
            //判斷該客户是否開啓接口加解密
                    //組織參數,獲取原始入參,進行解密操作
                    //解密完成後,將解密得到的參數全部加入到request的參數內
                    //繼續向下進行
            return true;
        } catch (Throwable throwable){
            //異常處理
        }
    }
        //返回前加密
    @Override
    public void postHandle(HttpServletRequest request,HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        
        //獲取客户身份標識
        try{
            //判斷該客户是否開啓接口加解密
                //調用原始的 postHandle 方法,讓響應數據被寫入包裝類的輸出流
            //調用加密方法,獲取加密後數據
            //將加密後的數據寫回響應
        }catch (Throwable throwable){
            //異常處理
        }
    }
}

結語

構建一個健壯的服務和系統不僅是技術領域專業人士的終極追求,也是確保我們能夠在面對未來不斷演變的業務需求和技術挑戰時保持競爭力的關鍵。在遵守廣泛認可的設計規範之外,我們必須進一步探索和優化,確保我們的服務不僅可以全面覆蓋各種業務場景,還要在安全性、可擴展性以及可伸縮性和降級能力方面表現出色。

這種追求不僅要求我們在設計之初就深入考慮潛在的風險和挑戰,還要求我們在整個開發過程中對服務進行持續評估和優化。這樣的系統將成為支持企業業務持續增長和創新的堅實基礎,幫助我們在不斷變化的技術和業務環境中保持領先!

讀完此篇,加入光榮的進化吧!

user avatar RCJL 头像 u_17443142 头像 kangkaidafangdezi 头像 linong 头像 pingcap 头像 mecode 头像 zzger 头像 greptime 头像 dependon 头像 tangbo_5f9242f233a7e 头像 qianniandanshendetiebanshao_dxy8l 头像
点赞 11 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.