如何設計一個介面?是在我們日常開發或者面試時經常問及的一個話題。
很多人覺得這不就是CRUD,能實現不就行了。單純實現來說,並非難事,但要做到易用、易擴充套件、易維護並不是一件簡單的事。這裡並不強調一些個介面設計的原則或者設計方法,僅從如何設計一個好的介面出發,簡單討論。
我們寫程式碼,不僅僅是為了實現當前的功能,也要有利於後面的維護。所謂的維護,就是程式碼不僅僅是寫給自己看的,也是給別人看的。所以介面定義要清晰易懂、命名規範。
除了介面、方法、出入參命名規範,也要注意程式碼規範問題。一開始接觸到各種程式碼壞味道的小夥伴,大多都會覺得這些規範很多餘、很煩人,但實際上,這些好的編碼習慣是讓大家都能按照基本規約開發,易於閱讀易於維護的基礎。
在介面定義時,也請注意介面功能的單一性。其實這也是微服務的一些思想,介面功能的單一職責、明確簡單。比如登入介面,它做的事情就是校驗賬戶名和密碼相關;訂單服務、積分服務、商品資訊相關的介面都是劃分開的。
入參出參校驗是每個程式設計師必備的基本素養。你設計的介面,必須先校驗引數。比如入參是否允許為空、入參長度要求、入參是否在列舉值範圍內等等。日常開發中,很多低階bug都是不校驗引數導致的。
提到引數,就必須提到介面狀態和錯誤碼。無論是失敗還是成功,一個完備的介面都應該告訴呼叫方返回資訊。如果介面失敗,具體失敗的原因是什麼,這就需要定義明確的錯誤碼和對應描述。同時,儘量對報錯資訊進行封裝,不要直接將伺服器端異常資訊丟擲去。
如何評判一個介面的效能就必須要有監控,這對於伺服器端監視介面效能和異常報警至關重要。呼叫次數、可用率、TP99、TP999等監視指標,極其重要的核心介面,還需要細分至秒級監控加以標識。
一個介面的效能不單單隻看自己業務邏輯,外部遠端呼叫也是消耗效能的重要部分。如果你呼叫第三方介面或者遠端服務,就需要考慮異常和超時。如果異常了,怎麼處理,是重試還是當作失敗還是告警處理。如果重試,重試幾次?這就需要站在業務角度思考這個問題。這些措施也是直接影響當前介面效能。
提高效能的利器還可以考慮快取。快取用得好,可以承載更多的請求,提升查詢效率,減少資料庫的壓力。但是使用快取需要考慮快取和資料庫一致性保證、快取擊穿等問題。
介面的關鍵程式碼,要有紀錄檔的保駕護航。首先紀錄檔級別需要合理使用:error > warn > info > debug。
其次紀錄檔資訊包含哪些呢,核心程式碼塊呼叫前的入參列印、介面呼叫後的異常捕獲紀錄檔等。需要注意的是,如果紀錄檔中涉及比較大的JSON富文字,請使用log.isInfoEnable(),在高並行和複雜log資訊拼接的情況下,使用這種標準的方法輸出log能夠省去不小的系統開銷。另外,如果構造log資訊的過程需要大量字串操作,建議使用StringBuilder來完成字串拼接。
實現一個好的介面,離不開優雅的例外處理。比如異常匹配的順序,優先捕獲具體的異常;使用流時記得使用finally關閉流資源;
對於執行時錯誤,比如資料邊界越界、空指標也在日常開發中出現,該判斷、該校驗的還是一項不能少哦。
超時問題也經常會導致介面不可用。設定合理的超時時間,也是在保護你的介面。超時一般與重試搭配使用,不過請注意,設定超時時間時,需要充分考慮你的上下游設定的超時時間。比如一個請求率先存取你的上游,而你的上游設定的超時時間是500ms,上游呼叫你的介面,但你設定的超時是2000ms,這其實就是無效超時時間。
對於介面耗時優化,也是有一些手段的,比如遠端序列改為並行呼叫、單次呼叫改為批次呼叫等等。但請注意儘量不在迴圈或者事務裡遠端呼叫。
介面有些場景,使用非同步更合理。舉個簡單的例子,對於一些運營操作的介面,往往需要記錄對應操作的操作紀錄檔,記錄下是誰在什麼時間操作了什麼物件,便於追蹤「事發現場」。但是記錄操作紀錄檔並不在介面主流程上,記錄操作紀錄檔是否成功失敗也不應該影響正常主流程的執行,這個時候就應該考慮用訊息佇列等方式進行非同步解耦。
可以說,註釋也是良好程式碼的重要組成部分。有些人,一直相信show me the code,卻不想寫一行註釋,認為沒有必要。但是你無法保證程式碼邏輯一直清晰、高效。如果是比較複雜的話,就建議把註釋寫清楚,這對於後續維護和縷清程式碼邏輯很重要。
如今的請求呼叫基本都是分散式呼叫鏈路,當分散式系統中某個基礎服務不可用時,就會最終導致整個系統不可用,所以當下遊系統或者自身服務出現問題時,一定要考慮降級。如果做的更完備的話,還可以考慮熔斷。
同時,針對高並行的流量洪峰介面,必須考慮限流應對超出系統的承載能力挑戰。限流措施也同樣可以限制爬蟲,保護系統,丟棄多餘的請求。
這裡說的安全,範圍可太大了。比如線性安全,很多人反手上來就是HashMap,因為它是非線性安全的,可以考慮高並行下的ConcurrentHashMap。
如果前端重複請求,你的邏輯如何處理?是不是考慮介面去重處理(有時候是防刷處理)。簡單點,可以使用redis防重處理,同樣的請求,一定時間間隔內進行過濾。當然,對於一些並行不高的介面,比如轉賬類介面,推薦使用資料庫主鍵或者唯一索引。
如果訊息佇列出現重複消費的情況,你的業務邏輯怎麼控制?是不是考慮冪等性校驗。
防重主要為了避免產生重複資料,把重複請求攔截下來即可。而冪等設計除了攔截已經處理的請求,還要求每次相同的請求都返回一樣的結果。不過很多時候,它們的處理流程和方式是類似的。
還有一些其他安全方面的考慮,比如讀寫分離、程式碼鎖的粒度控制、資料加密等等。
為什麼要有溝通?又為什麼把溝通放在最後呢?遇到一些技術難題,跟技術leader對齊方案。實現需求的過程中,有什麼問題,需要及時跟產品溝通。需要跟使用者端對齊介面,一定不能上來就自己埋頭把介面定義完了。種種場景,學會溝通是非常重要的,有效、高效溝通不僅會帶來愉悅心情,開發起來很順暢,也會提高人際關係。
好啦,以上就是根據自身經驗,對「如何設計一個介面?」問題的小小回答,如有不足,敬請指教。
作者:京東零售 李澤陽
來源:京東雲開發者社群 轉載請註明來源