坦率地講 服務(wù)熔斷 & 服務(wù)降級

背景

之前遇到個問題,發(fā)現(xiàn)一個系統(tǒng)如果拆分了太多業(yè)務(wù)類服務(wù),或者依賴于大量的第三方服務(wù),就很容易因為某個服務(wù)的故障導(dǎo)致整個系統(tǒng)不可用,比如

  • 模塊中使用了 Elastic Search 進(jìn)行監(jiān)控,但是 ES 突然掛了,相關(guān)的 api 的調(diào)用報錯導(dǎo)致級聯(lián)的服務(wù)全部阻塞,那么應(yīng)該要有規(guī)避由 ES 調(diào)用 raise 出的異常或者調(diào)用超時而導(dǎo)致整個模塊或整個系統(tǒng)崩潰的保護(hù)措施。
  • 使用 AWS 或 阿里云 的 ECS 服務(wù)來作為 micro-service 的載體,但是 ECS 服務(wù)故障或者過載了導(dǎo)致整個業(yè)務(wù)鏈無法正常進(jìn)行,那么應(yīng)有對應(yīng)的降級或者限制調(diào)用頻度的方案來進(jìn)行保護(hù)。

服務(wù)熔斷

服務(wù)熔斷和電路熔斷是一個道理,如果一條線路電壓過高,保險絲會熔斷,防止出現(xiàn)火災(zāi),但是過后重啟仍然是可用的。

而服務(wù)熔斷則是對于目標(biāo)服務(wù)的請求和調(diào)用大量超時或失敗,這時應(yīng)該熔斷該服務(wù)的所有調(diào)用,并且對于后續(xù)調(diào)用應(yīng)直接返回,從而快速釋放資源,確保在目標(biāo)服務(wù)不可用的這段時間內(nèi),所有對它的調(diào)用都是立即返回,不會阻塞的。再等到目標(biāo)服務(wù)好轉(zhuǎn)后進(jìn)行接口恢復(fù)。

熔斷的方式有很多,最出名的奶飛的 hystrix 項目里有很全面的實踐,這里便先列個比較偷懶的案例。

舉個栗子,

# Elastic search service decorator
def api_trend(func):
  def wrapper(*args, **kwargs):
    # Call elastic search service to get api trend
    elastic_search_api_call()
    # Custom function
    return func(*args, **kwargs)
  return wrapper

# Custom task to do stuff
@api_trend
def custom_func(foo):
  retrun foo()
  

假設(shè)代碼中的 @api_trend 是個調(diào)用 Elastic Search 服務(wù)來監(jiān)控 api 執(zhí)行情況的裝飾器,那么如果 Elastic Search 服務(wù)掛了,則后續(xù)的 custom_func(foo) 也不會成功執(zhí)行或者被阻塞。所以我們需要做的就是阻止后續(xù)的程序繼續(xù)調(diào)用 @api_trend 或者 elastic_search_api_call() 這兩位老哥,把 custom_func(foo) 隔離開,這樣雖然暫時失去了監(jiān)控,但是仍能保證業(yè)務(wù)能正常執(zhí)行。

所以基于這點,我們可以簡單地加個熔斷控制器開關(guān)來隔離故障接口。

from threading import Timer

# Melt down flag
FUSE = True

# Melt down recover func
def recover():
  FUSE = True
  return

# Melt down decorator
def melt_down(threshold=5, inteval=60, timeout=300, recover_time=3600):
  def wrap_melt(func):
    def wrapper(*args, **kwargs):
      is_fuse = True
      while threshold > 0 and is_fuse:
        try:
          func(timeout, *args, **kwargs)
          is_fuse = False
        exception Exception, e:
          is_fuse = True
          threshold -= 1
          continue
        time.sleep(inteval)
      FUSE = is_fuse
      if not FUSE:
        tr = threading.Timer(recover_time, recover)
        tr.start()
      return FUSE
    return wrapper
  return wrap_melt

# Elastic search service decorator
def api_trend(func):
  def wrapper(*args, **kwargs):
    # Call elastic search service to get api trend
    if FUSE:
        elastic_search_api_call()
    # Custom function
    return func(*args, **kwargs)
  return wrapper

# Custom task to do stuff
@melt_down
@api_trend
def custom_func(foo):
  return foo()

通過在調(diào)用 @api_trend 之前加上熔斷控制器,進(jìn)行目標(biāo)服務(wù)的接口調(diào)用,如果在規(guī)定的重試次數(shù)內(nèi)均未成功,則認(rèn)為該服務(wù)在這一段時間內(nèi)不可用,對于該 api 的所有調(diào)用全都用一個 FUSE_FLAG 進(jìn)行隔離,并且設(shè)置一個定時 Thread, 在一定時間后重新打開 FUSE_FLAG,恢復(fù)目標(biāo)服務(wù)的調(diào)用。

服務(wù)降級

當(dāng)服務(wù)器壓力劇增的情況下,根據(jù)當(dāng)前業(yè)務(wù)情況及流量對一些服務(wù)和頁面有策略的降級,以此釋放服務(wù)器資源以保證核心任務(wù)的正常運行。

對于復(fù)雜系統(tǒng)而言,會有很多的微服務(wù)通過 rpc 調(diào)用,從而產(chǎn)生一個業(yè)務(wù)需要一條很長的調(diào)用鏈,其中任何一環(huán)故障了都會導(dǎo)致整個調(diào)用鏈?zhǔn)』虺瑫r而導(dǎo)致業(yè)務(wù)服務(wù)不可用或阻塞。

這種情況下,可以暫時去掉調(diào)用鏈中故障的服務(wù)來進(jìn)行降級,其中降級策略又有很多種,比如限流,接口拒絕等,這里就挑個簡單的來舉栗。

比如一個電商系統(tǒng),用戶模塊,商品模塊,訂單模塊,支付模塊,物流模塊分別是5個存在相互依賴性的服務(wù),但是如果用戶要下單購買個商品則可能需要一條長調(diào)用鏈依次 Call 到這5個模塊。

# Call chain
user = UserModule.sender.get_user()
product = ProductModule.sender.get_product(user.selected)
order = OrderModule.sender.post_order(product)
payment = PaymentModule.sender.post_payment(order)
logistics = LogisticsModule.sender.post_logistics(payment)

這時候如果物流模塊崩了,那么很可能在最終購買商品的流程會被回滾,導(dǎo)致用戶購買商品不成功,然而實際上,物流模塊即便失效,仍應(yīng)允許進(jìn)行商品查看,下單,購買等,所以,坦率地講,我們應(yīng)該對這5個模塊進(jìn)行一個上下游依賴的剝離,使之變?yōu)榧儍舻?rpc 調(diào)用。

簡單地說,

from xmlrpclib import ServerProxy 

MODULE_TO_ENABLE = [
  'UserAgent',
  'ProductAgent',
  'OrderAgent',
  'PaymentAgent',
  'LogisticsAgent'
]

def custom_call():
  return foo()

def call_nothing():
  return

class LogisticsAgent(object):
  self.sender = ServerProxy("http://{host}:{port}".format(host=host, port=port))
  if self.__class__.__name__ in MODULE_TO_ENABLE:
    self.sender.call = custom_call
  else:
    self.sender.call = call_nothing
  pass

# Call chain
if self.current_agent not in MODULE_TO_ENABLE:
    pass

這樣通過 diable Call chain 中不重要的一環(huán)來確保其他模塊可以正常使用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內(nèi)容