千万别强制停机!我嘴都气歪了!
你知道強(qiáng)制停機(jī)的后果有多嚴(yán)重嗎!
有一天,我正在愉快地寫技術(shù)文章,結(jié)果電腦啪地一下就藍(lán)屏了!
哦豁,完蛋,寫了幾千字,忘了保存!
我盲猜很多同學(xué)都有這種體驗(yàn),可能因?yàn)橐恍┩话l(fā)意外,導(dǎo)致自己的電腦強(qiáng)制停機(jī)了,丟失了自己當(dāng)前的工作。
同樣,對(duì)于企業(yè),所有的網(wǎng)站、應(yīng)用、數(shù)據(jù)、服務(wù)都是掛在服務(wù)器上的,一旦意外發(fā)生,比如被挖斷了電線、遭遇了自然災(zāi)害,會(huì)導(dǎo)致服務(wù)器被強(qiáng)制停機(jī),使得機(jī)器上 所有進(jìn)行中的程序被強(qiáng)制中斷,后果不堪設(shè)想!
有同學(xué)就笑了,不就是程序被強(qiáng)制中斷么,我們自己偶爾也會(huì)用任務(wù)管理器或者 kill -9 命令殺個(gè)進(jìn)程啊,抓緊重新啟動(dòng)程序不就好了,有啥大不了的?
的確,我以前也是通過(guò)強(qiáng)殺進(jìn)程來(lái)下線和升級(jí)服務(wù)的,干脆利落爽。但直到后來(lái)有一次,因?yàn)閺?qiáng)殺進(jìn)程導(dǎo)致了線上事故,造成了經(jīng)濟(jì)損失和加班,把我嘴都?xì)馔崃?#xff01;我才意識(shí)到自己之前太粗暴、想法太簡(jiǎn)單了。
其實(shí),一個(gè)程序被強(qiáng)制中斷,除了無(wú)法提供服務(wù)外,還有很多嚴(yán)重的后果!
1. 請(qǐng)求丟失
對(duì)于一個(gè) web 服務(wù)器,比如 Java Web 開發(fā)中主流的 Tomcat。當(dāng)接受到請(qǐng)求時(shí),會(huì)開啟一個(gè)線程來(lái)處理該請(qǐng)求。而如果請(qǐng)求數(shù)較多,線程處理不過(guò)來(lái),就會(huì)將此請(qǐng)求放入等待隊(duì)列中,排隊(duì)等待空閑線程。
假設(shè) web 服務(wù)進(jìn)程突然中斷,會(huì)導(dǎo)致所有在內(nèi)存隊(duì)列中等待執(zhí)行的請(qǐng)求丟失,等了半天,等了個(gè)空!
2. 業(yè)務(wù)中斷
一旦進(jìn)程中斷,會(huì)導(dǎo)致 所有 正在執(zhí)行的業(yè)務(wù)中斷,會(huì)導(dǎo)致很多意想不到的后果。
比如有一個(gè)檢查數(shù)據(jù)的任務(wù),要檢查所有數(shù)據(jù)庫(kù)中狀態(tài)為 0 的數(shù)據(jù)是否正確,代碼流程如下:
// 開始檢查,數(shù)據(jù)狀態(tài)由 0 置為 1 startCheck(); // 檢查 doCheck(); // 結(jié)束檢查,將正確的數(shù)據(jù)狀態(tài)置為 2 endCheck();假設(shè)剛把數(shù)據(jù)的狀態(tài)置為 1,表示正在檢查中。然后程序就中斷了,會(huì)導(dǎo)致以后這條數(shù)據(jù)的狀態(tài)始終為 1,再也不會(huì)被檢查。
同理,如果已經(jīng)檢查完,并且數(shù)據(jù)正確,本來(lái)應(yīng)該將數(shù)據(jù)狀態(tài)置為 2,但這時(shí)程序中斷,也會(huì)導(dǎo)致 數(shù)據(jù)的狀態(tài)和預(yù)期不一致。
以上只是一個(gè)簡(jiǎn)單的例子,但實(shí)際的業(yè)務(wù)場(chǎng)景中,業(yè)務(wù)中斷可能直接影響收益,尤其是涉及交易的支付轉(zhuǎn)賬業(yè)務(wù),如果用戶已經(jīng)付款,卻因?yàn)槌绦虻闹袛?#xff0c;沒(méi)有存儲(chǔ)付款記錄,那這個(gè)支付業(yè)務(wù)不是真要涼涼?
3. 事務(wù)中斷
數(shù)據(jù)庫(kù)事務(wù)是指對(duì)數(shù)據(jù)庫(kù)的一系列 不可分割 的操作,具有一致性,每次執(zhí)行必須使數(shù)據(jù)庫(kù)從一個(gè)一致性狀態(tài)變到另一個(gè)一致性狀態(tài)。
比如轉(zhuǎn)賬業(yè)務(wù)中,用戶 A 要給用戶 B 轉(zhuǎn)賬 1 元,用戶 A 扣除 1 元,用戶 B 就要增加 1 元。
但如果用戶 A 已扣除 1元后,應(yīng)用程序或者數(shù)據(jù)庫(kù)系統(tǒng)突然掛了,導(dǎo)致事務(wù)尚未完成就被迫中斷,結(jié)果用戶 B 的總金額并沒(méi)有變化。這時(shí)數(shù)據(jù)庫(kù)就處于不一致狀態(tài)。同理,即使在程序中設(shè)計(jì)了回滾,回滾過(guò)程也可能會(huì)被中斷!
除了數(shù)據(jù)不一致外,事務(wù)中斷還可能導(dǎo)致鎖行、鎖表,使得這部分 數(shù)據(jù)的可用性受到影響。
4. 文件損壞
假設(shè)程序正在向一個(gè)文件進(jìn)行寫操作,還未完成,就被中斷了,可能會(huì)導(dǎo)致文件的不完整、甚至損壞。
這讓我想起小時(shí)候,電腦配置不高,有時(shí)玩游戲會(huì)卡住,然后我就強(qiáng)制殺了進(jìn)程,結(jié)果導(dǎo)致游戲文件損壞,只能重新下載游戲。
5. 任務(wù)丟失
我們?cè)诰帉憳I(yè)務(wù)代碼時(shí),經(jīng)常會(huì)將比較耗時(shí)的任務(wù)異步化,將任務(wù)提交到線程池后立即返回成功。線程池會(huì)從任務(wù)隊(duì)列中依次讀取并執(zhí)行任務(wù)。
而一旦程序中斷,線程池中的任務(wù)就會(huì)丟失,好像他從來(lái)沒(méi)有被提交過(guò)一樣。這種感覺(jué)就像你答應(yīng)別人要做一件事,別人對(duì)你很放心,但你最后卻放了鴿子跑路了。
6. 數(shù)據(jù)丟失
有時(shí),我們會(huì)先將數(shù)據(jù)臨時(shí)放在內(nèi)存中,然后定期、定時(shí)、或者分批地持久化到數(shù)據(jù)庫(kù)或本地磁盤中。
比如 Redis 數(shù)據(jù)庫(kù)的 RDB 機(jī)制,每隔一段時(shí)間,會(huì)將內(nèi)存中的數(shù)據(jù)進(jìn)行本地備份,從而降低大量數(shù)據(jù)并發(fā)寫入時(shí)的負(fù)載,提升性能。
但就像上面提到的任務(wù)丟失一樣,一旦程序中斷,可能會(huì)導(dǎo)致很多 未持久化的數(shù)據(jù)丟失,比如緩存、分批提交數(shù)據(jù)等。
7. 消息丟失
在分布式系統(tǒng)中,各個(gè)節(jié)點(diǎn)間經(jīng)常通過(guò)消息來(lái)進(jìn)行交互和協(xié)作,而程序的中斷可能會(huì)在不同情況下導(dǎo)致消息丟失。
1. 消息未發(fā)出
假設(shè)某支付業(yè)務(wù)中,已經(jīng)扣除了用戶的賬戶余額,并更新了數(shù)據(jù)庫(kù),接下來(lái)要向客戶端返回應(yīng)答消息。
但是消息正在發(fā)送隊(duì)列中排隊(duì)等待發(fā)送時(shí),由于進(jìn)程被強(qiáng)制退出導(dǎo)致消息未發(fā)出,從而導(dǎo)致應(yīng)答消息丟失。客戶端久久接收不到消息后,可能會(huì)發(fā)起重試,導(dǎo)致重復(fù)更新。
2. 消息未確認(rèn)
比如說(shuō)某段業(yè)務(wù)代碼從消息隊(duì)列中取出了一個(gè)消息,進(jìn)行消費(fèi)處理,代碼流程如下:
// 獲取下一個(gè)消息 Message msg = getNextMsg(); // 處理消息 int res = handleMsg(msg); // 處理成功? if(res == 0) {// 確認(rèn)消息ack(); } else {// 拒絕確認(rèn)消息nack(); }無(wú)論消息處理成功與否,都必須要給消息隊(duì)列一個(gè)回復(fù)!如果處理成功,要告訴他這條消息已經(jīng)被我處理完成啦,請(qǐng)給我下一條消息;即使處理失敗,也要告訴消息隊(duì)列,請(qǐng)給我重發(fā)本條消息。
而一旦程序中斷,這條消息的處理結(jié)果便無(wú)人知曉,可能導(dǎo)致消息隊(duì)列的 阻塞或者無(wú)限重發(fā)(根據(jù)具體消息隊(duì)列來(lái)決定)。
8. 資源占用
程序的強(qiáng)制中斷可能會(huì)導(dǎo)致很多資源的占用未被釋放。比如:
####9. 服務(wù)未下線
在微服務(wù)場(chǎng)景下,服務(wù)通常由集中的注冊(cè)中心進(jìn)行統(tǒng)一的服務(wù)發(fā)現(xiàn)和管理。
比如 Eureka 注冊(cè)中心,服務(wù)生產(chǎn)者向注冊(cè)中心注冊(cè)服務(wù),服務(wù)消費(fèi)者從注冊(cè)中心獲取服務(wù)地址,然后遠(yuǎn)程調(diào)用:
而一旦某個(gè)服務(wù)進(jìn)程還沒(méi)有即時(shí)通知注冊(cè)中心它要下線,就中斷了,會(huì)導(dǎo)致服務(wù)消費(fèi)者仍能獲取到該服務(wù)的路由,從而調(diào)用失敗。
此外,服務(wù)下線時(shí)如果未向上游(該服務(wù)調(diào)用方)通知,還可能導(dǎo)致上游的持續(xù)調(diào)用,嚴(yán)重時(shí)會(huì)產(chǎn)生雪崩效應(yīng),整條服務(wù)鏈路中斷!
尤其是在分布式場(chǎng)景下,出現(xiàn)進(jìn)程強(qiáng)制中斷對(duì)集群的影響(比如數(shù)據(jù)一致性)非常大。正如 FLP不可能定理 的描述:在異步通信場(chǎng)景,即使只有一個(gè)進(jìn)程失敗,也沒(méi)有任何算法能保證非失敗進(jìn)程達(dá)到一致性。
其實(shí),相比起這些問(wèn)題,更可怕的是,如果沒(méi)有完善的數(shù)據(jù)監(jiān)控和檢測(cè)機(jī)制,你甚至完全不知道在強(qiáng)制停機(jī)后有沒(méi)有出現(xiàn)問(wèn)題?出現(xiàn)了哪些問(wèn)題?哪些數(shù)據(jù)丟失?哪些數(shù)據(jù)不一致?哪些任務(wù)需要補(bǔ)償?看不見(jiàn)的危險(xiǎn)才最可怕啊!
因此,預(yù)防大于治療。一方面要養(yǎng)成良好習(xí)慣,無(wú)論是對(duì)自己的電腦還是服務(wù)器,都千萬(wàn)不要再主動(dòng)強(qiáng)制停機(jī)了;另一方面,也要在程序設(shè)計(jì)時(shí),做好應(yīng)對(duì)意外停機(jī)的防控措施。不要等到失去了,才追悔莫及。
總結(jié)
以上是生活随笔為你收集整理的千万别强制停机!我嘴都气歪了!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: [文件系统]Image映象文件
- 下一篇: [转贴]非技术:在广州天河北被抢全记录(