2. 修改老接口時,注意接口的兼容性
3. 設計接口時,充分考慮接口的可擴展性
4. 接口考慮是否需要防重處理
5. 重點接口,考慮線程池隔離
6. 調(diào)用第三方接口要考慮異常和超時處理
7. 接口實現(xiàn)考慮熔斷和降級
8. 日志打印好,接口的關鍵代碼,要有日志保駕護航
9. 接口的功能定義要具備單一性
10. 接口有些場景,使用異步更合理
11. 優(yōu)化接口耗時,遠程串行考慮改并行調(diào)用
12. 接口合并或者說考慮批量處理思想
13. 接口實現(xiàn)過程中,恰當使用緩存
14. 接口考慮熱點數(shù)據(jù)隔離性
15. 可變參數(shù)配置化,比如紅包皮膚切換等
16. 接口考慮冪等性
17. 讀寫分離,優(yōu)先考慮讀從庫,注意主從延遲問題
18. 接口注意返回的數(shù)據(jù)量,如果數(shù)據(jù)量大需要分頁
19. 好的接口實現(xiàn),離不開 SQL 優(yōu)化
20. 代碼鎖的粒度控制好
21. 接口狀態(tài)和錯誤需要統(tǒng)一明確
22. 接口要考慮異常處理
23. 優(yōu)化程序邏輯
24. 接口實現(xiàn)過程中,注意大文件、大事務、大對象
25. 你的接口,需要考慮限流
26. 代碼實現(xiàn)時,注意運行時異常(比如空指針、下標越界等)
27. 保證接口安全性
28. 分布式事務,如何保證
29. 事務失效的一些經(jīng)典場景
30. 掌握常用的設計模式
31. 寫代碼時,考慮線性安全問題
32. 接口定義清晰易懂,命名規(guī)范
33. 接口的版本控制
34. 注意代碼規(guī)范問題
35. 保證接口正確性,其實就是保證更少的 bug
36. 學會溝通,跟前端溝通,跟產(chǎn)品溝通
作為后端開發(fā),不管是什么語言,Java、Go還是C++,其背后的后端思想都是類似的。
后端開發(fā)工程師,主要工作就是:如何把一個接口設計好 。
今天就給大家介紹,設計好接口的 36 個錦囊。
1. 接口參數(shù)校驗
入?yún)⒊鰠⑿r炇敲總€程序員必備的基本素養(yǎng)。設計接口,必須先校驗參數(shù)。
比如入?yún)⑹欠裨试S為空,入?yún)㈤L度是否符合預期長度。這個要養(yǎng)成習慣,日常開發(fā)中,很多低級 bug 都是不校驗參數(shù)導致的。
比如你的數(shù)據(jù)庫表字段設置為varchar(16),對方傳了一個 32 位的字符串過來,如果你不校驗參數(shù),插入數(shù)據(jù)庫就直接異常了 。
出參也是,比如你定義的接口報文,參數(shù)是不為空的,但是你的接口返回參數(shù)沒有做校驗,因為程序某些原因,返回別人一個null值。
2. 修改老接口時,注意接口的兼容性
很多 bug 都是因為修改了對外老接口但是卻不做兼容 導致的。關鍵這個問題多數(shù)是比較嚴重的,可能直接導致系統(tǒng)發(fā)版失敗。新手程序員很容易犯這個錯誤。
所以,如果你的需求是在原來接口上做修改,尤其這個接口是對外提供服務的話,一定要考慮接口兼容。
舉個例子吧,比如 dubbo 接口,原本是只接收 A、B 參數(shù),現(xiàn)在加了一個參數(shù) C,就可以考慮這樣處理:
//老接口 voidoldService(A,B){ //兼容新接口,傳個null代替C newService(A,B,null); } //新接口,暫時不能刪掉老接口,需要做兼容。 voidnewService(A,B,C){ ... }
3. 設計接口時,充分考慮接口的可擴展性
要根據(jù)實際業(yè)務場景設計接口,充分考慮接口的可擴展性。
比如你接到一個需求:用戶添加或者修改員工時,需要刷臉。那你是反手提供一個員工管理的提交刷臉信息接口呢?還是先思考:提交刷臉是不是通用流程呢?比如轉賬或者一鍵貼現(xiàn)需要接入刷臉的話,你是否需要重新實現(xiàn)一個接口呢?還是當前按業(yè)務類型劃分模塊,復用這個接口就好,保留接口的可擴展性。
如果按模塊劃分的話,未來如果其他場景比如一鍵貼現(xiàn)接入刷臉的話,不用再搞一套新的接口,只需要新增枚舉,然后復用刷臉通過流程接口,實現(xiàn)一鍵貼現(xiàn)刷臉的差異化即可。
4. 接口考慮是否需要防重處理
如果前端重復請求,你的邏輯如何處理?是不是考慮接口去重處理。
當然,如果是查詢類的請求,其實不用防重。如果是更新修改類的話,尤其金融轉賬類的,就要過濾重復請求了。
簡單點,你可以使用 Redis 防重復請求,同樣的請求方,一定時間間隔內(nèi)的相同請求,考慮是否過濾。當然,轉賬類接口,并發(fā)不高的話,推薦使用數(shù)據(jù)庫防重 表,以唯一流 水號作為主鍵或者唯一索引 。
5. 重點接口,考慮線程池隔離
一些登錄、轉賬交易、下單等重要接口,考慮線程池隔離。
如果你所有業(yè)務都共用一個線程池,有些業(yè)務出 bug 導致線程池阻塞打滿的話,那就杯具了,所有業(yè)務都影響了 。
因此進行線程池隔離,重要業(yè)務分配多一點的核心線程,就能更好保護重要業(yè)務。
6. 調(diào)用第三方接口要考慮異常和超時處理
如果你調(diào)用第三方接口,或者分布式遠程服務的話,需要考慮:
異常處理
比如,你調(diào)別人的接口,如果異常了,怎么處理,是重試還是當做失敗還是告警處理。
接口超時
沒法預估對方接口多久返回,一般設置個超時斷開時間,以保護你的接口。之前見過一個生產(chǎn)問題 ,就是 http 調(diào)用不設置超時時間,最后響應方進程假死,請求一直占著線程不釋放,拖垮線程池。
重試次數(shù)
你的接口調(diào)失敗,需不需要重試?重試幾次?需要站在業(yè)務角度思考這個問題。
7. 接口實現(xiàn)考慮熔斷和降級
當前互聯(lián)網(wǎng)系統(tǒng)一般都是分布式部署的。而分布式系統(tǒng)中經(jīng)常會出現(xiàn)某個基礎服務不可用,最終導致整個系統(tǒng)不可用的情況,這種現(xiàn)象被稱為服務雪崩效應 。
比如分布式調(diào)用鏈路A->B->C....,下圖所示:
如果服務 C 出現(xiàn)問題,比如是因為慢 SQL 導致調(diào)用緩慢 ,那將導致 B 也會延遲,從而 A 也會延遲。堵住的 A 請求會消耗占用系統(tǒng)的線程、IO 等資源。當請求 A 的服務越來越多,占用計算機的資源也越來越多,最終會導致系統(tǒng)瓶頸出現(xiàn),造成其他的請求同樣不可用,最后導致業(yè)務系統(tǒng)崩潰。
為了應對服務雪崩,常見的做法是熔斷和降級 。最簡單是加開關控制,當下游系統(tǒng)出問題時,開關降級,不再調(diào)用下游系統(tǒng)。還可以選用開源組件Hystrix。
8. 日志打印好,接口的關鍵代碼,要有日志保駕護航
關鍵業(yè)務代碼無論身處何地,都應該有足夠的日志保駕護航。
比如:你實現(xiàn)轉賬業(yè)務,轉個幾百萬,然后轉失敗了,接著客戶投訴,然后你還沒有打印到日志,想想那種水深火熱的困境下,你卻毫無辦法。。。
那么,你的轉賬業(yè)務都需要哪些日志信息呢?至少,方法調(diào)用前,入?yún)⑿枰蛴“?,接口調(diào)用后,需要捕獲一下異常吧,同時打印異常相關日志,如下:
publicvoidtransfer(TransferDTOtransferDTO){ log.info("invoketranferbegin"); //打印入?yún)?log.info("invoketranfer,paramters:{}",transferDTO); try{ res=transferService.transfer(transferDTO); }catch(Exceptione){ log.error("transferfail,account:{}", transferDTO.getAccount()) log.error("transferfail,exception:{}",e); } log.info("invoketranferend"); }
9. 接口的功能定義要具備單一性
單一性是指接口做的事情比較單一、專一。比如一個登錄接口,它做的事情就只是校驗賬戶名密碼,然后返回登錄成功以及userId即可。但是如果為了減少接口交互,把一些注冊、一些配置查詢等全放到登錄接口,就不太妥。
其實這也是微服務一些思想,接口的功能單一、明確。比如訂單服務、積分、商品信息相關的接口都是劃分開的。將來拆分微服務的話,是不是就比較簡便啦。
10. 接口有些場景,使用異步更合理
舉個簡單的例子,比如實現(xiàn)一個用戶注冊的接口,用戶注冊成功時,發(fā)個郵件或者短信去通知用戶。這個郵件或者發(fā)短信,就更適合異步處理。因為總不能一個通知類的失敗,導致注冊失敗吧。
至于做異步的方式,簡單的就是用線程池 。還可以使用消息隊列,就是用戶注冊成功后,生產(chǎn)者產(chǎn)生一個注冊成功的消息,消費者拉到注冊成功的消息,就發(fā)送通知。
不是所有的接口都適合設計為同步接口。比如你要做一個轉賬的功能,如果是單筆的轉賬,你是可以把接口設計同步。用戶發(fā)起轉賬時,客戶端再靜靜等待轉賬結果就好。如果是批量轉賬,一個批次一千筆,甚至一萬筆的,你則可以把接口設計為異步。就是用戶發(fā)起批量轉賬時,持久化成功就先返回受理成功。然后用戶隔十分鐘或者十五分鐘再來查轉賬結果就好。又或者,批量轉賬成功后,再回調(diào)上游系統(tǒng)。
11. 優(yōu)化接口耗時,遠程串行考慮改并行調(diào)用
假設我們設計一個 APP 首頁的接口,它需要查用戶信息、需要查 banner 信息、需要查彈窗信息等等。那是一個一個接口串行調(diào),還是并行調(diào)用呢?
如果是串行一個一個查,比如查用戶信息 200ms,查 banner 信息 100ms、查彈窗信息 50ms,那一共就耗時350ms了,如果還查其他信息,那耗時就更大了。這種場景是可以改為并行調(diào)用的。也就是說查用戶信息、查 banner 信息、查彈窗信息,可以同時發(fā)起。
12. 接口合并或者說考慮批量處理思想
數(shù)據(jù)庫操作或者是遠程調(diào)用時,能批量操作就不要 for 循環(huán)調(diào)用。
一個簡單例子,我們平時一個列表明細數(shù)據(jù)插入數(shù)據(jù)庫時,不要在 for 循環(huán)一條一條插入,建議一個批次幾百條,進行批量插入。同理遠程調(diào)用也類似想法,比如你查詢營銷標簽是否命中,可以一個標簽一個標簽去查,也可以批量標簽去查,那批量進行,效率就更高嘛。
//反例 for(inti=0;i
小伙伴們是否了解過kafka為什么這么快呢?其實其中一點原因,就是 kafka 使用批量消息 提升服務端處理能力。
13. 接口實現(xiàn)過程中,恰當使用緩存
哪些場景適合使用緩存?讀多寫少且數(shù)據(jù)時效要求越低的場景 。
緩存用得好,可以承載更多的請求,提升查詢效率,減少數(shù)據(jù)庫的壓力。
比如一些平時變動很小或者說幾乎不會變的商品信息,可以放到緩存,請求過來時,先查詢緩存,如果沒有再查數(shù)據(jù)庫,并且把數(shù)據(jù)庫的數(shù)據(jù)更新到緩存。但是,使用緩存增加了需要考慮這些點:緩存和數(shù)據(jù)庫一致性如何保證、集群、緩存擊穿、緩存雪崩、緩存穿透等問題。
保證數(shù)據(jù)庫和緩存一致性:緩存延時雙刪、刪除緩存重試機制、讀取 biglog 異步刪除緩存 。
緩存擊穿:設置數(shù)據(jù)永不過期。
緩存雪崩:Redis 集群高可用、均勻設置過期時間。
緩存穿透:接口層校驗、查詢?yōu)榭赵O置個默認空值標記、布隆過濾器。
一般用Redis分布式緩存,當然有些時候也可以考慮使用本地緩存,如Guava Cache、Caffeine等。使用本地緩存有些缺點,就是無法進行大數(shù)據(jù)存儲,并且應用進程的重啟,緩存會失效。
14. 接口考慮熱點數(shù)據(jù)隔離性
瞬時間的高并發(fā),可能會打垮你的系統(tǒng)??梢宰鲆恍狳c數(shù)據(jù)的隔離。比如業(yè)務隔離、系統(tǒng)隔離、用戶隔離、數(shù)據(jù)隔離 等。
業(yè)務隔離,比如 12306 的分時段售票,將熱點數(shù)據(jù)分散處理,降低系統(tǒng)負載壓力。
系統(tǒng)隔離:比如把系統(tǒng)分成了用戶、商品、社區(qū)三個板塊。這三個塊分別使用不同的域名、服務器和數(shù)據(jù)庫,做到從接入層到應用層再到數(shù)據(jù)層三層完全隔離。
用戶隔離:重點用戶請求到配置更好的機器。
數(shù)據(jù)隔離:使用單獨的緩存集群或者數(shù)據(jù)庫服務熱點數(shù)據(jù)。
15. 可變參數(shù)配置化,比如紅包皮膚切換等
假如產(chǎn)品經(jīng)理提了個紅包需求,圣誕節(jié)的時候,紅包皮膚為圣誕節(jié)相關的,春節(jié)的時候,為春節(jié)紅包皮膚等。
如果在代碼寫死控制,可有類似以下代碼:
if(duringChristmas){ img=redPacketChristmasSkin; }elseif(duringSpringFestival){ img=redSpringFestivalSkin; }
如果到了元宵節(jié)的時候,運營小姐姐突然又有想法,紅包皮膚換成燈籠相關的,這時候,是不是要去修改代碼了,重新發(fā)布了?
從一開始接口設計時,可以實現(xiàn)一張紅包皮膚的配置表 ,將紅包皮膚做成配置化。更換紅包皮膚,只需修改一下表數(shù)據(jù)就好了。
當然,還有一些場景適合一些配置化的參數(shù):一個分頁多少數(shù)量控制、某個搶紅包多久時間過期這些,都可以搞到參數(shù)配置化表里面。這也是擴展性思想的一種體現(xiàn)。
16. 接口考慮冪等性
接口是需要考慮冪等性的,尤其搶紅包、轉賬這些重要接口。最直觀的業(yè)務場景,就是用戶連著點擊兩次 ,你的接口有沒有 hold 住 ?;蛘呦㈥犃谐霈F(xiàn)重復消費的情況,你的業(yè)務邏輯怎么控制?
回憶下,什么是冪等?
計算機科學中,冪等表示一次和多次請求某一個資源應該具有同樣的副作用,或者說,多次請求所產(chǎn)生的影響與一次請求執(zhí)行的影響效果相同。
大家別搞混哈,防重和冪等設計其實是有區(qū)別的 。防重主要為了避免產(chǎn)生重復數(shù)據(jù),把重復請求攔截下來即可。而冪等設計除了攔截已經(jīng)處理的請求,還要求每次相同的請求都返回一樣的效果。不過呢,很多時候,它們的處理流程、方案是類似的。
接口冪等實現(xiàn)方案主要有 8 種:
select + insert + 主鍵/唯一索引沖突
直接 insert + 主鍵/唯一索引沖突
狀態(tài)機冪等
抽取防重表
token 令牌
悲觀鎖
樂觀鎖
分布式鎖
17. 讀寫分離,優(yōu)先考慮讀從庫,注意主從延遲問題
我們的數(shù)據(jù)庫都是集群部署的,有主庫也有從庫,當前一般都是讀寫分離的。
比如寫入數(shù)據(jù),肯定是寫入主庫,但是對于讀取實時性要求不高的數(shù)據(jù),則優(yōu)先考慮讀從庫,因為可以分擔主庫的壓力。
如果讀取從庫的話,需要考慮主從延遲的問題。
18. 接口注意返回的數(shù)據(jù)量,如果數(shù)據(jù)量大需要分頁
一個接口返回報文,不應該包含過多的數(shù)據(jù)量。
過多的數(shù)據(jù)量不僅處理復雜,并且數(shù)據(jù)量傳輸?shù)膲毫σ卜浅4蟆?/p>
如果數(shù)量實在是比較大,可以分頁返回,如果是功能不相關的報文,那應該考慮接口拆分。
19. 好的接口實現(xiàn),離不開 SQL 優(yōu)化
我們做后端的,寫好一個接口,離不開 SQL 優(yōu)化。
SQL 優(yōu)化從以下這幾個維度思考:
explain 分析 SQL 查詢計劃(重點關注 type、extra、filtered 字段)。
show profile 分析,了解 SQL 執(zhí)行的線程的狀態(tài)以及消耗的時間。
索引優(yōu)化(覆蓋索引、最左前綴原則、隱式轉換、order by 以及 group by 的優(yōu)化、join 優(yōu)化)
大分頁問題優(yōu)化(延遲關聯(lián)、記錄上一頁最大 ID)
數(shù)據(jù)量太大(分庫分表 、同步到 es,用 es 查詢)
20. 代碼鎖的粒度控制好
什么是加鎖粒度呢?
其實就是你要鎖住的范圍是多大。比如你在家上衛(wèi)生間,你只要鎖住衛(wèi)生間就可以了吧,不需要將整個家都鎖起來不讓家人進門吧,衛(wèi)生間就是你的加鎖粒度。
我們寫代碼時,如果不涉及到共享資源,就沒有必要鎖住。這就好像你上衛(wèi)生間,不用把整個家都鎖住,鎖住衛(wèi)生間門就可以了。
比如,在業(yè)務代碼中,有一個 ArrayList 因為涉及到多線程操作,所以需要加鎖操作,假設剛好又有一段比較耗時的操作(代碼中的slowNotShare方法)不涉及線程安全問題,你會如何加鎖呢?
反例:
//不涉及共享資源的慢方法 privatevoidslowNotShare(){ try{ TimeUnit.MILLISECONDS.sleep(100); }catch(InterruptedExceptione){ } } //錯誤的加鎖方法 publicintwrong(){ longbeginTime=System.currentTimeMillis(); IntStream.rangeClosed(1,10000).parallel().forEach(i->{ //加鎖粒度太粗了,slowNotShare其實不涉及共享資源 synchronized(this){ slowNotShare(); data.add(i); } }); log.info("cosumetime:{}",System.currentTimeMillis()-beginTime); returndata.size(); }
正例:
publicintright(){ longbeginTime=System.currentTimeMillis(); IntStream.rangeClosed(1,10000).parallel().forEach(i->{ slowNotShare();//可以不加鎖 //只對List這部分加鎖 synchronized(data){ data.add(i); } }); log.info("cosumetime:{}",System.currentTimeMillis()-beginTime); returndata.size(); }
21. 接口狀態(tài)和錯誤需要統(tǒng)一明確
提供必要的接口調(diào)用狀態(tài)信息。比如一個轉賬接口調(diào)用是成功、失敗、處理中還是受理成功等,需要明確告訴客戶端。如果接口失敗,那么具體失敗的原因是什么。這些必要的信息都必須要告訴給客戶端,因此需要定義明確的錯誤碼和對應的描述。同時,盡量對報錯信息封裝一下,不要把后端的異常信息完全拋出到客戶端。
22. 接口要考慮異常處理
實現(xiàn)一個好的接口,離不開優(yōu)雅的異常處理。
對于異常處理,提十個小建議:
盡量不要使用e.printStackTrace(),而是使用log打印。因為e.printStackTrace()語句可能會導致內(nèi)存占滿。
catch住異常時,建議打印出具體的exception,利于更好定位問題。
不要用一個Exception捕捉所有可能的異常。
記得使用finally關閉流資源或者直接使用try-with-resource。
捕獲異常與拋出異常必須是完全匹配,或者捕獲異常是拋異常的父類。
捕獲到的異常,不能忽略它,至少打點日志吧。
注意異常對你的代碼層次結構的侵染。
自定義封裝異常,不要丟棄原始異常的信息Throwable cause。
運行時異常RuntimeException ,不應該通過catch的方式來處理,而是先預檢查,比如:NullPointerException處理。
注意異常匹配的順序,優(yōu)先捕獲具體的異常。
23. 優(yōu)化程序邏輯
優(yōu)化程序邏輯這塊還是挺重要的,也就是說,你實現(xiàn)的業(yè)務代碼,如果是比較復雜的話,建議把注釋寫清楚 。還有,代碼邏輯盡量清晰,代碼盡量高效。
比如,要使用用戶信息的屬性,根據(jù) session 已經(jīng)獲取到userId了,然后就把用戶信息從數(shù)據(jù)庫查詢出來,使用完后,后面可能又要用到用戶信息的屬性,有些小伙伴沒想太多,反手就把userId再傳進去,再查一次數(shù)據(jù)庫。。。我在項目中,見過這種代碼。。。直接把用戶對象傳下來不好嘛。。。
反例:
publicResponsetest(Sessionsession){ UserInfouser=UserDao.queryByUserId(session.getUserId()); if(user==null){ reutrnnewResponse(); } returndo(session.getUserId()); } publicResponsedo(StringUserId){ //多查了一次數(shù)據(jù)庫 UserInfouser=UserDao.queryByUserId(session.getUserId()); ...... returnnewResponse(); }
正例:
publicResponsetest(Sessionsession){ UserInfouser=UserDao.queryByUserId(session.getUserId()); if(user==null){ reutrnnewResponse(); } returndo(session.getUserId()); } //直接傳UserInfo對象過來即可,不用再多查一次數(shù)據(jù)庫 publicResponsedo(UserInfouser){ ...... returnnewResponse(); }
當然,這只是一些很小的一個例子,還有很多類似的例子,需要大家開發(fā)過程中,多點思考的哈。
24. 接口實現(xiàn)過程中,注意大文件、大事務、大對象
讀取大文件時,不要Files.readAllBytes直接讀取到內(nèi)存,這樣會 OOM 的,建議使用BufferedReader一行一行來。
大事務可能導致死鎖、回滾時間長、主從延遲等問題,開發(fā)中盡量避免大事務。
注意一些大對象的使用,因為大對象是直接進入老年代的,可能會觸發(fā) fullGC。
25. 你的接口,需要考慮限流
如果你的系統(tǒng)每秒扛住的請求是 1000,如果一秒鐘來了十萬請求呢?換個角度就是說,高并發(fā)的時候,流量洪峰來了,超過系統(tǒng)的承載能力,怎么辦呢?
如果不采取措施,所有的請求打過來,系統(tǒng) CPU、內(nèi)存、Load 負載飚得很高,最后請求處理不過來,所有的請求無法正常響應。
針對這種場景,我們可以采用限流方案。就是為了保護系統(tǒng),多余的請求,直接丟棄。
限流定義:
在計算機網(wǎng)絡中,限流就是控制網(wǎng)絡接口發(fā)送或接收請求的速率,它可防止 DoS 攻擊和限制 Web 爬蟲。限流,也稱流量控制。是指系統(tǒng)在面臨高并發(fā),或者大流量請求的情況下,限制新的請求對系統(tǒng)的訪問,從而保證系統(tǒng)的穩(wěn)定性。
可以使用 Guava 的RateLimiter單機版限流,也可以使用Redis分布式限流,還可以使用阿里開源組件sentinel限流。
26. 代碼實現(xiàn)時,注意運行時異常(比如空指針、下標越界等)
日常開發(fā)中,我們需要采取措施規(guī)避數(shù)組邊界溢出、被零整除、空指針 等運行時錯誤。類似代碼比較常見:
Stringname=list.get(1).getName();//list可能越界,因為不一定有2個元素哈
應該采取措施,預防一下數(shù)組邊界溢出。正例如下:
if(CollectionsUtil.isNotEmpty(list)&&list.size()>1){ Stringname=list.get(1).getName(); }
27. 保證接口安全性
如果你的 API 接口是對外提供的,需要保證接口的安全性。保證接口的安全性有 token 機制和接口簽名 。
token 機制身份驗證 方案還比較簡單的,就是:
客戶端發(fā)起請求,申請獲取 token。
服務端生成全局唯一的 token,保存到 redis 中(一般會設置一個過期時間),然后返回給客戶端。
客戶端帶著 token,發(fā)起請求。
服務端去 redis 確認 token 是否存在,一般用 redis.del(token) 的方式,如果存在會刪除成功,即處理業(yè)務邏輯,如果刪除失敗不處理業(yè)務邏輯,直接返回結果。
接口簽名 的方式,就是把接口請求相關信息(請求報文,包括請求時間戳、版本號、appid 等),客戶端私鑰加簽,然后服務端用公鑰驗簽,驗證通過才認為是合法的、沒有被篡改過的請求。
除了加簽驗簽和 token 機制,接口報文一般是要加密的 。當然,用 https 協(xié)議是會對報文加密的。如果是我們服務層的話,如何加解密呢?
可以參考 HTTPS 的原理,就是服務端把公鑰給客戶端,然后客戶端生成對稱密鑰,接著客戶端用服務端的公鑰加密對稱密鑰,再發(fā)到服務端,服務端用自己的私鑰解密,得到客戶端的對稱密鑰。這時候就可以愉快傳輸報文啦,客戶端用對稱密鑰加密請求報文 ,服務端用對應的對稱密鑰解密報文 。
有時候,接口的安全性,還包括手機號、身份證等信息的脫敏 。就是說,用戶的隱私數(shù)據(jù),不能隨便暴露 。
28. 分布式事務,如何保證
分布式事務:就是指事務的參與者、支持事務的服務器、資源服務器以及事務管理器分別位于不同的分布式系統(tǒng)的不同節(jié)點之上。簡單來說,分布式事務指的就是分布式系統(tǒng)中的事務,它的存在就是為了保證不同數(shù)據(jù)庫節(jié)點的數(shù)據(jù)一致性。
分布式事務的幾種解決方案:
2PC(二階段提交)方案、3PC
TCC(Try、Confirm、Cancel)
本地消息表
最大努力通知
seata
29. 事務失效的一些經(jīng)典場景
我們的接口開發(fā)過程中,經(jīng)常需要使用到事務。所以需要避開事務失效的一些經(jīng)典場景。
方法的訪問權限必須是 public,其他 private 等權限,事務失效。
方法被定義成了 final 的,這樣會導致事務失效。
在同一個類中的方法直接內(nèi)部調(diào)用,會導致事務失效。
一個方法如果沒交給 spring 管理,就不會生成 spring 事務。
多線程調(diào)用,兩個方法不在同一個線程中,獲取到的數(shù)據(jù)庫連接不一樣的。
表的存儲引擎不支持事務。
如果自己 try...catch 誤吞了異常,事務失效。
錯誤的傳播特性。
30. 掌握常用的設計模式
把代碼寫好,還是需要熟練常用的設計模式,比如策略模式、工廠模式、模板方法模式、觀察者模式等等。
設計模式,是代碼設計經(jīng)驗的總結。使用設計模式可以可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。
31. 寫代碼時,考慮線性安全問題
在高并發(fā) 情況下,HashMap可能會出現(xiàn)死循環(huán)。因為它是非線性安全的,可以考慮使用ConcurrentHashMap。所以這個也盡量養(yǎng)成習慣,不要上來反手就是一個new HashMap()。
Hashmap、Arraylist、LinkedList、TreeMap 等都是線性不安全的。
Vector、Hashtable、ConcurrentHashMap 等都是線性安全的。
32. 接口定義清晰易懂,命名規(guī)范
我們寫代碼,不僅僅是為了實現(xiàn)當前的功能,也要有利于后面的維護。
說到維護,代碼不僅僅是寫給自己看的,也是給別人看的。
所以接口定義要清晰易懂,命名規(guī)范。
33. 接口的版本控制
接口要做好版本控制。就是說,請求基礎報文,應該包含version接口版本號字段,方便未來做接口兼容。其實這個點也算接口擴展性的一個體現(xiàn)點吧。
比如客戶端 APP 某個功能優(yōu)化了,新老版本會共存,這時候我們的version版本號就派上用場了,對version做升級,做好版本控制。
34. 注意代碼規(guī)范問題
注意一些常見的代碼壞味道:
大量重復代碼(抽共用方法,設計模式)。
方法參數(shù)過多(可封裝成一個 DTO 對象)。
方法過長(抽小函數(shù))。
判斷條件太多(優(yōu)化 if...else)。
不處理沒用的代碼。
不注重代碼格式。
避免過度設計。
35. 保證接口正確性,其實就是保證更少的 bug
保證接口的正確性,換個角度講,就是保證更少的bug,甚至是沒有bug。所以接口開發(fā)完后,一般需要開發(fā)自測一下 。
然后的話,接口的正確性還體現(xiàn)在,多線程并發(fā)的時候,保證數(shù)據(jù)的正確性 ,等等。比如做一筆轉賬交易,扣減余額的時候,可以通過 CAS 樂觀鎖的方式保證余額扣減正確。
如果你是實現(xiàn)秒殺接口,得防止超賣問題吧。可以使用 Redis 分布式鎖防止超賣問題。
36. 學會溝通,跟前端溝通,跟產(chǎn)品溝通
我把這一點放到最后,學會溝通是非常非常重要的。
比如你開發(fā)定義接口時,一定不能上來就自己埋頭把接口定義完了 ,需要跟客戶端先對齊接口 。遇到一些難點時,跟技術 leader 對齊方案。實現(xiàn)需求的過程中,有什么問題,及時跟產(chǎn)品溝通。
總之就是,開發(fā)接口過程中,一定要溝通好~
來源:撿田螺的小男孩 在此特別鳴謝!
-
接口
+關注
關注
33文章
8450瀏覽量
150726 -
數(shù)據(jù)庫
+關注
關注
7文章
3752瀏覽量
64233 -
指針
+關注
關注
1文章
478瀏覽量
70491
原文標題:為什么有公司規(guī)定所有接口都必須用Post?
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論