我在支付寶是如何處理高併發case的

寫在開頭,好多人都問我在支付寶是如何處理高併發的。哎~~~~,那我今天就說一下,我是怎麼解決天天上百萬的還款的,順便介紹一下高併發的思路。spring

首先,先看一個圖(看不懂不要緊,稍後我會解釋)。數據庫

首先,介紹下,我在支付寶主要負責開發信貸核心系統,它是網商銀行的核心繫統(不是皮系統),通俗的說就是貸款和還款,固然實際上很是複雜,舉個例子,如今國內銀行的信貸系統都是從IBM或者oracle買的(開發太難了),而網商這邊開發了本身的一套系統。編程

有一天,有這麼一個大需求:資金驅動。我來解釋一下,首先,若是用戶被打上了逾期或者強制扣款標誌,那麼,若是你的支付寶帳號有money,支付寶受託系統就會給我發消息,而後我處理,最後調用支付寶受託扣款。這是爲何,由於怕用戶還不上錢或者不還錢,否則你想一想貸出去的錢都收不回來,馬爸爸不哭暈在廁所~~~~網絡

言歸正傳,這個需求看起來特別的清楚明瞭,可是通常噁心都看起來簡單(以往的編程經驗)。併發

看到這個,我因而想到了這個處理辦法:oracle

一、接收消息分佈式

二、消息落地高併發

三、處理消息,掉支付寶受託接口。性能

ok,思路清晰,而且完成了。測試

~~~~~~~~~~~~~~~~~~~~~~~沾沾自喜,當自測的時候,哭暈在廁所,媽的,處理消息的時候簡直太慢了,由於對於一個用戶,要找出他全部未結清的支用(借據),每筆支用還包含支用帳單(分期帳單),須要先結息,計算每筆分期帳單的表內,表外的正常利息,逾期利息,逾本罰,逾利罰,滯納金,還有寬限期處理。。。。。(業務極其複雜,學過會計學的就懂了)。形成的問題就是時間太長了,一個事務時間很是長,再加上調支付寶受託接口的時間,太長了。並且網絡訪問放在了事務中,簡直是噩夢(緣由很簡單:時間太長,事務可能會自動終止,鏈接不能及時歸還致使數據庫掛了)。

考慮到以上這個問題,我又想了一個方案:

一、處理消息後將消息落地。

二、起一個調度任務處理消息生成扣款指令。

三、再起一個調度任務負責撈取扣款指令掉支付寶接口。

~~~~~~~~~~~~~~~~~~~~~~~~~so easy,可是當調試的時候發現,我太天真,由於對於每一個消息處理很慢(上面介紹過),等我生成扣款指令,消息列表都他媽的都滿了,致使消息多的處理不過來,怎麼解決?只有優化。

換個思路,既然消息堆積,能不能快速處理消息,固然能夠,可是又不能真的去處理消息生成扣款指令,由於太耗時間了,哎~~~~~~

我可不能夠這樣,在處理消息的時候只是生成相應的記錄,也就是生成那個用戶要被扣錢了,並非真的去處理它,而後起一個調度器去處理他。

因而方案變成了:

一、接受消息。

二、消息落地

三、起一個調度器處理消息生成用戶扣款記錄

四、起一個調度器處理用戶扣款記錄生成扣款指令

五、起一個調度器處理扣款指令調用支付寶受託

~~~~~~~~~~~~~~~~~~~~~~~~~~~完美!哈哈,終於經過了測試能夠上線了。線上運行了一段時間,沒有什麼問題,好開心。直到有一天的來臨,大促!!!!!!!!!!媽的,大促的時候,隨着用戶的購買,賣家的收錢,帳上會不斷有資金流入,不停地產生資金驅動消息,並且都是小批量的,數量太大了,上千萬條,簡直崩潰了。看來仍是須要優化!

進一步處理,在觀察大促的時候發現,單位時間內,一個商戶會不停地有少許資金的不斷流入,可是觀察發現,其實數據都是同樣的,簡單都說無非是我收了1塊錢,2塊錢。只有額度不一樣,其餘的都同樣。那麼我能夠把它們都當作重複記錄。哇塞,能夠這麼處理。我能夠去重,而後生成還款指令。

繼續優化方案:加油!!!

一、接受消息,並消息落地。

二、處理消息生產前置還款指令(aegis_repay_pretreat)

三、由於前置還款指令會有大量的重複記錄,去重而後產生還款命令(aeghis_repay_cmd),同時刪去aegis_repay_pretreat中的記錄。

四、處理還款命令生產扣款命令。

五、處理扣款命令,調用支付寶受託。

大概須要4個定時任務調度。終於解決了問題了,也處理高併發場景,太爽了################################

真的沒問題了麼??在測試的時候發現,因爲調度器會不停地搶鎖,掃表,oracle吃不消啊!!!!!!!!!!

進一步優化,通常用的調度任務,除了Linux的crontab,再就是spring的quartz調度,固然支付寶有比較牛逼的分佈式調度中心。可是鑑於優化訪問oracle的性能,選用本身搞quartz。

開始了:

假設我有10臺機器,他們都須要從同一個表中撈取數據,那麼須要加鎖,而後更改任務狀態,別人就撈不到了。可是若是不停地撈取,頻繁掃表,數據庫性能極具降低,可是這種定時任務都是每時每刻在執行,如何解決?

首先作一個阻塞隊列arrayBlockingQueue,而後先加鎖而後撈數據,搶不到鎖就sleep幾秒,而後對撈取的數據判斷,若是沒有撈到,那麼說明此時表中並無多少數據,那能夠再sleep,看起來沒什麼問題。

可不能夠進一步優化,由於咱們知道通常用到線程池,都須要關注他的coreThreadCount,maxThreadCount,隊列長度。具體處理是當前線程數量小於coreThreadCount,則會建立線程,若是大於,則加入隊列,若是放不進去建立線程,若是大於maxThreadCount拋異常。

因此,能夠看到,若是線程比較忙的時候,阻塞隊列數量會不少,線程壓力會比較大。那麼能夠這樣,每次處理判斷下隊列的長度,若是大於飢餓值就sleep,下降線程壓力。

還能不能再進一步優化?

既然全部的處理都圍繞着隊列長度,撈取的數量,可不能夠計算下撈取的數量,也就是由於每次撈取隊列當前容量size - 隊列當前長度length 的指令數量,那麼,若是說撈取後,發現隊列中的長度仍是低於整個隊列容量的1/3是否是表示此時表中並無多少數據,仍然能夠sleep以此下降掃表頻率。

get it

------------------------------------------------------------------------

通過一番折騰,終於解決了!這裏留下一個問題:如何解決搶鎖頻繁,更改狀態會在機器重啓或者發佈時卡主指令如何處理?留給你們討論下,歡迎交流。發完了以後,有些小夥伴問我,爲啥處理這麼複雜,在這裏說一下,通常用戶還款大概峯值是幾百萬比,可是在大促的時候洪峯大概上億比。因此只能這樣處理。

相關文章
相關標籤/搜索