偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

一篇帶你了解接口突然超時十宗罪

數(shù)據(jù)庫 其他數(shù)據(jù)庫
今天跟大家一起聊聊接口突然超時的10個原因,希望對你會有所幫助。?

前言

不知道你有沒有遇到過這樣的場景:我們提供的某個API接口,響應(yīng)時間?原本一直都很快,但在某個不經(jīng)意的時間點,突然出現(xiàn)了接口超時。

也許你會有點懵,到底是為什么呢?

今天跟大家一起聊聊接口突然超時的10個原因,希望對你會有所幫助。?

1、網(wǎng)絡(luò)異常

接口原本好好的,突然出現(xiàn)超時,最常見的原因,可能是網(wǎng)絡(luò)出現(xiàn)異常了。比如:偶然的網(wǎng)絡(luò)抖動,或者是帶寬被占滿了。

(1)網(wǎng)絡(luò)抖動

經(jīng)常上網(wǎng)的我們,肯定遇到過這樣的場景:大多數(shù)情況下我們訪問某個網(wǎng)站很快,但偶爾會出現(xiàn)網(wǎng)頁一直轉(zhuǎn)圈,加載不出來的情況。

有可能是你的網(wǎng)絡(luò)出現(xiàn)了抖動,丟包了。

網(wǎng)頁請求API接口,或者接口返回數(shù)據(jù)給網(wǎng)頁,都有可能會出現(xiàn)網(wǎng)絡(luò)丟包的情況。

網(wǎng)絡(luò)丟包可能會導(dǎo)致接口超時。

(2)帶寬被占滿

有時候,由于頁面或者接口設(shè)計不合理,用戶請求量突增的時候,可能會導(dǎo)致服務(wù)器的網(wǎng)絡(luò)帶寬被占滿的情況。

服務(wù)器帶寬?指的是在一定時間內(nèi)傳輸?數(shù)據(jù)的大小,比如:1秒傳輸了10M的數(shù)據(jù)。

如果用戶請求量突然增多,超出了1秒10M的上限,比如:1秒100M,而服務(wù)器帶寬本身1秒就只能傳輸10M,這樣會導(dǎo)致在這1秒內(nèi),90M數(shù)據(jù)就會延遲傳輸?shù)那闆r,從而導(dǎo)致接口超時的發(fā)生。

所以對于有些高并發(fā)請求場景,需要評估一下是否需要增加服務(wù)器帶寬。

2、線程池滿了

我們調(diào)用的API接口,有時候為了性能考慮,可能會使用線程池異步查詢數(shù)據(jù),最后把查詢結(jié)果進(jìn)行匯總,然后返回。

如下圖所示:調(diào)用遠(yuǎn)程接口總耗時 200ms = 200ms(即耗時最長的那次遠(yuǎn)程接口調(diào)用)

在java8之前可以通過實現(xiàn)Callable接口,獲取線程返回結(jié)果。

java8以后通過CompleteFuture類實現(xiàn)該功能。我們這里以CompleteFuture為例:

public UserInfo getUserInfo(Long id) throws InterruptedException, ExecutionException {
final UserInfo userInfo = new UserInfo();
CompletableFuture userFuture = CompletableFuture.supplyAsync(() -> {
getRemoteUserAndFill(id, userInfo);
return Boolean.TRUE;
}, executor);

CompletableFuture bonusFuture = CompletableFuture.supplyAsync(() -> {
getRemoteBonusAndFill(id, userInfo);
return Boolean.TRUE;
}, executor);

CompletableFuture growthFuture = CompletableFuture.supplyAsync(() -> {
getRemoteGrowthAndFill(id, userInfo);
return Boolean.TRUE;
}, executor);
CompletableFuture.allOf(userFuture, bonusFuture, growthFuture).join();

userFuture.get();
bonusFuture.get();
growthFuture.get();

return userInfo;
}

這里我用到了executor,表示自定義的線程池?,為了防止高并發(fā)場景下,出現(xiàn)線程過多的問題。

但如果用戶請求太多,線程池中已有的線程處理不過來,線程池會把多余的請求,放到隊列?中排隊?,等待空閑線程的去處理。

如果隊列中排隊的任務(wù)非常多,某次API請求一直在等待,沒辦法得到及時處理,就會出現(xiàn)接口超時問題。

這時候,我們可以考慮是否核心線程數(shù)設(shè)置太小了,或者有多種業(yè)務(wù)場景共用了同一個線程池。

如果是因為核心線程池設(shè)置太小,可以將其調(diào)大一些。

如果是因為多種業(yè)務(wù)場景共用了同一個線程池?,可以拆分成多個線程池。

3、數(shù)據(jù)庫死鎖

有時候接口超時得有點莫名其妙,特別是遇到數(shù)據(jù)庫出現(xiàn)死鎖的時候。

你提供的API接口中通過某個id更新某條數(shù)據(jù),此時,正好線上在手動執(zhí)行一個批量更新數(shù)據(jù)的sql語句。

該sql語句在一個事務(wù)當(dāng)中,并且剛好也在更新那條數(shù)據(jù),可能會出現(xiàn)死鎖的情況。

由于該sql語句執(zhí)行時間很長,會導(dǎo)致API接口的那次更新數(shù)據(jù)操作,長時間被數(shù)據(jù)庫鎖住,沒法即使返回數(shù)據(jù),而出現(xiàn)接口超時問題。

你說坑不坑?

所以建議在執(zhí)行數(shù)據(jù)庫批量操作前,一定要評估數(shù)據(jù)的影響范圍,不要一次性更新太多的數(shù)據(jù),不然可能會導(dǎo)致很多意想不到的問題。

此外,批量更新操作建議在用戶訪問少的時段執(zhí)行,比如:凌晨。

4、傳入?yún)?shù)太多

有時候,偶爾的一次接口超時,是由于參數(shù)傳入太多導(dǎo)致的。

例如:根據(jù)id集合批量查詢分類接口,如果傳入的id集合數(shù)據(jù)量不多,傳入幾十個或上百個id,不會出現(xiàn)性能問題。畢竟id是分類表的主鍵?,可以走主鍵索引,數(shù)據(jù)庫的查找速度是非常快的。

但如果接口調(diào)用方,一次性傳入幾千個,甚至幾萬個id,批量查詢分類,也可能會出現(xiàn)接口超時問題。

因為數(shù)據(jù)庫在執(zhí)行sql語句之前,會評估一下耗時情況,查詢條件太多,有可能走全表掃描更快。

所以這種情況下sql語句可能會丟失索引,讓執(zhí)行時間變慢,出現(xiàn)接口超時問題。

因此我們在設(shè)計批量接口的時候,建議要限制傳入的集合的大小,比如:500。

如果超過我們設(shè)置最大的集合大小,則接口直接返回失敗,并提示給用戶:一次性傳入?yún)?shù)過多。

該限制一定要寫到接口文檔中,避免接口調(diào)用方,在生產(chǎn)環(huán)境調(diào)用接口失敗而踩坑。要在接口開發(fā)階段通知到位。

此外,如果接口調(diào)用方要傳入的參數(shù)就是很多怎么辦?

答:可能是需求不合理,或者系統(tǒng)設(shè)計有問題,我們要盡量在系統(tǒng)設(shè)計階段就規(guī)避這個問題。

如果我們重新進(jìn)行系統(tǒng)設(shè)計改動比較大的話,有個臨時的解決方案:在接口調(diào)用方中多線程分批調(diào)用該接口,最后將結(jié)果進(jìn)行匯總。

5、超時時間設(shè)置過短

通常情況下,建議我們在調(diào)用遠(yuǎn)程API接口時,要設(shè)置連接超時時間和讀超時時間這兩個參數(shù),并且可以動態(tài)配置。

這樣做的好處是,可以防止調(diào)用遠(yuǎn)程API接口萬一出現(xiàn)了性能問題,響應(yīng)時間很長,把我們自己的服務(wù)拖掛的情況發(fā)生。

比如:你調(diào)用的遠(yuǎn)程API接口,要100秒才返回數(shù)據(jù),而你設(shè)置的超時時間是100秒。這時1000個請求過來,去請求該API接口,這樣會導(dǎo)致tomcat線程池很快被占滿,導(dǎo)致整個服務(wù)暫時不可用,至少新的請求過來,是沒法即使響應(yīng)的。

所以我們需要設(shè)置超時時間,并且超時時間還不能設(shè)置太長。

并發(fā)量不大的業(yè)務(wù)場景,可以將這兩個超時時間設(shè)置稍微長一點,比如:連接超時時間?為10秒,讀超時時間為20秒。

并發(fā)量大的業(yè)務(wù)場景,可以設(shè)置成秒級?或者毫秒級。

有些小伙伴為了開發(fā)方便,在多種業(yè)務(wù)場景共用這兩個超時時間。

某一天,在并發(fā)量大的業(yè)務(wù)場景中,你將該超時時間改短了。

但直接導(dǎo)致并發(fā)量不大的業(yè)務(wù)場景中,出現(xiàn)調(diào)用API接口超時的問題。

因此,不建議多種業(yè)務(wù)場景共用同一個超時時間,最好根據(jù)并發(fā)量的不同,單獨設(shè)置不同的超時時間。

6、一次性返回數(shù)據(jù)太多

不知道你有沒有遇到過這樣的需求:我們有個job,每天定時調(diào)用第三方API查詢接口,獲取昨天更新的數(shù)據(jù),然后更新到我們自己的數(shù)據(jù)庫表中。

由于第三方每天更新的數(shù)據(jù)不多,所以該API接口響應(yīng)時間還是比較快的。

但突然有一天,該API接口卻出現(xiàn)了接口超時問題。

查看日志發(fā)現(xiàn),該API接口一次性返回的數(shù)據(jù)太多,而且該數(shù)據(jù)的更新時間相同。

這就可以斷定,該API接口提供方進(jìn)行了批量更新操作,修改了大量的數(shù)據(jù),導(dǎo)致該問題的發(fā)生。

即使我們在job中加了失敗重試機制,但由于該API一次性返回數(shù)據(jù)實在太多太多,重試也很有可能會接口超時,這樣會導(dǎo)致一直獲取不到第三方前一天最新的數(shù)據(jù)。

所以第三方這種根據(jù)日期查詢增量數(shù)據(jù)的接口,建議做成分頁查詢的,不然后面沒準(zhǔn)哪一天,遇到批量更新的操作,就可能出現(xiàn)接口超時的問題。

7、死循環(huán)

死循環(huán)也會導(dǎo)致接口超時?

死循環(huán)不應(yīng)該在接口測試階段?就發(fā)現(xiàn)了,為什么要到生產(chǎn)環(huán)境才發(fā)現(xiàn)?

確實,絕大部分死循環(huán)問題,在測試階段可以發(fā)現(xiàn)。

但有些無限遞歸隱藏的比較深,比如下面的情況。

死循環(huán)其實有兩種:

  1. 普通死循環(huán)
  2. 無限遞歸

(1)普通死循環(huán)

有時候死循環(huán)是我們自己寫的,例如下面這段代碼:

while(true) {
if(condition) {
break;
}
System.out.println("do samething");
}

這里使用了while(true)?的循環(huán)調(diào)用,這種寫法在CAS自旋鎖中使用比較多。

當(dāng)滿足condition等于true的時候,則自動退出該循環(huán)。

如果condition條件非常復(fù)雜,一旦出現(xiàn)判斷不正確,或者少寫了一些邏輯判斷,就可能在某些場景下出現(xiàn)死循環(huán)的問題。

出現(xiàn)死循環(huán),大概率是開發(fā)人員人為的bug導(dǎo)致的,不過這種情況很容易被測出來。

還有一種隱藏的比較深的死循環(huán),是由于代碼寫的不太嚴(yán)謹(jǐn)導(dǎo)致的。如果用正常數(shù)據(jù),可能測不出問題,但一旦出現(xiàn)異常數(shù)據(jù),就會立即出現(xiàn)死循環(huán)。

(2)無限遞歸

如果想要打印某個分類的所有父分類,可以用類似這樣的遞歸方法實現(xiàn):

public void printCategory(Category category){
if(category == null
|| category.getParentId() == null) {
return;
}
System.out.println("父分類名稱:"+ category.getName());
Category parent = categoryMapper.getCategoryById(category.getParentId());
printCategory(parent);
}

正常情況下,這段代碼是沒有問題的。

但如果某次有人誤操作,把某個分類的parentId指向了它自己,這樣就會出現(xiàn)無限遞歸?的情況。導(dǎo)致接口一直不能返回數(shù)據(jù),最終會發(fā)生堆棧溢出。

建議寫遞歸方法時,設(shè)定一個遞歸的深度,比如:分類最大等級有4級,則深度可以設(shè)置為4。然后在遞歸方法中做判斷,如果深度大于4時,則自動返回,這樣就能避免無限遞歸的情況。

8、sql語句沒走索引

你有沒有遇到過這樣一種情況:明明是同一條sql,只有入?yún)⒉煌?。有的時候走的索引a,有的時候卻走的索引b?

沒錯,有時候mysql會選錯索引,甚至有時會不走索引。

mysql在執(zhí)行某條sql語句之前,會通過抽樣統(tǒng)計來估算掃描行數(shù),根據(jù)影響行數(shù)、區(qū)分度、基數(shù)、數(shù)據(jù)頁等信息,最后綜合評估走哪個索引。

有時候傳入?yún)?shù)1,sql語句走了索引a,執(zhí)行時間很快。但有時候傳入?yún)?shù)2,sql語句走了索引b,執(zhí)行時間明顯慢了很多。

這樣有可能會導(dǎo)致API接口出現(xiàn)超時問題。

必要時可以使用force index來強制查詢sql走某個索引。

9、服務(wù)OOM

我之前遇到過這樣一種場景:一個根據(jù)id查詢分類的接口,該id是主鍵,sql語句可以走主鍵索引,竟然也出現(xiàn)了接口超時問題。

我當(dāng)時覺得有點不可思議,因為這個接口平均耗時只有十幾毫秒,怎么可能會出現(xiàn)超時呢?

但從當(dāng)時的日志看,接口響應(yīng)時間有5秒,的確出現(xiàn)了接口超時問題。

最后從Prometheus的服務(wù)內(nèi)存監(jiān)控中,查到了OOM問題。

其實該API接口部署的服務(wù)當(dāng)時由于OOM內(nèi)存溢出,其實掛了一段時間。

當(dāng)時所有的接口都出現(xiàn)了請求超時問題。

但由于K8S?集群有監(jiān)控?,它自動會將掛掉的服務(wù)節(jié)點kill?掉,并且在容器中重新部署了一個新的服務(wù)節(jié)點,幸好對用戶沒造成太大的影響。

10、在debug

我們有時候需要在本地開發(fā)工具,比如:idea中,直接連接測試環(huán)境的數(shù)據(jù)庫,調(diào)試某個API接口的業(yè)務(wù)邏輯。

因為在開發(fā)環(huán)境,某些問題不太好復(fù)現(xiàn)。

為了排查某個bug,你在請求某個本地接口時,開啟了debug模式,一行行的跟蹤代碼,排查問題。

走到某一行代碼的時候,停留了很長一段時間,該行代碼主要是更新某條數(shù)據(jù)。

此時,測試同學(xué)在相關(guān)的業(yè)務(wù)頁面中,操作更新了相同的數(shù)據(jù)。

這種也可能會出現(xiàn)數(shù)據(jù)庫死鎖的問題。

由于你在idea的debug模式中,一直都沒有提交事務(wù),會導(dǎo)致死鎖的時間變得很長,從而導(dǎo)致業(yè)務(wù)頁面請求的API接口出現(xiàn)超時問題。

責(zé)任編輯:姜華 來源: 蘇三說技術(shù)
相關(guān)推薦

2021-09-27 09:18:30

ListIterato接口方法

2021-05-20 06:57:16

RabbitMQ開源消息

2011-06-17 14:24:28

Java

2024-10-08 15:51:09

JavaScriptStorage 接口

2021-08-06 07:51:48

JavaScriptStorage接口

2024-08-06 14:44:48

JavaScriptStorage 接口

2021-06-29 10:00:02

JavaScriptStorage接口

2024-05-23 15:32:33

StorageJavaScript

2021-07-14 08:24:23

TCPIP 通信協(xié)議

2021-07-28 10:02:54

建造者模式代碼

2021-06-30 00:20:12

Hangfire.NET平臺

2023-05-12 08:19:12

Netty程序框架

2021-08-11 07:02:21

npm包管理器工具

2021-11-24 08:51:32

Node.js監(jiān)聽函數(shù)

2021-08-02 06:34:55

Redis刪除策略開源

2021-12-15 11:52:34

GPLLinuxGNU

2021-11-08 08:42:44

CentOS Supervisor運維

2022-02-16 10:03:06

對象接口代碼

2021-07-08 06:30:03

Linux CPULinux 系統(tǒng)

2021-08-14 10:01:43

Python條件語句Python基礎(chǔ)
點贊
收藏

51CTO技術(shù)棧公眾號