python web 任務異步處理的探索

發現問題

請求日誌寫入 kafka 上線後,發現線上一直報警:請求總耗時過長。 而後就去測試了一下操做請求日誌的那個函數的耗時,結果是讓我挺驚訝的,最長耗時達到了 300ms,最重要的是這個函數影響了全局的接口請求(全部的請求都要通過該函數) 當時增長新操做就直接往函數里加,沒有考慮到會如此耗時,大概加了三四個動做吧。python

思考問題

既然這個函數如此耗時,那麼就直接將這個函數異步咯。想法聽上去好像是完美的,然而現實是殘酷的。bash

探索過程

  1. 既然語言用的是 python,那確定優先考慮使用 coroutine,首先嚐試整個函數使用 coroutine 作異步處理,可是因爲函數中使用到了 request 這個線程級別的全局變量(只能在請求上下文中使用)。若是這裏開了一個新的協程去作處理,由於是異步的,因此原請求會立刻結束,該請求所在的請求上下文也隨之銷燬了。新的協程將沒法使用該 reqeust,由於該協程已經不在原請求上下文中,因此第一次嘗試失敗。
  2. 既然沒法直接整個函數作異步,那麼就將這個函數拆開,先開一個函數把須要從 request 中獲取的信息拿到(這裏只是簡單的賦值操做,耗時很小)。將對信息的處理的過程又開一個新的函數(主要是處理過程比較耗時),只對處理過程作異步,再次嘗試使用協程,然而仍是得不到預期的效果。由於協程是屬於線程的(至關於線程是屬於進程的),當請求結束時,其使用的線程也被銷燬了,因此咱們剛開的協程也沒了,處理還沒開始就結束了(固然能夠經過 join 等到函數執行結束,但這樣已經失去了異步的意義了)。第二次嘗試失敗。
  3. 最後妥協了,仍是使用線程吧,直接開啓一個線程去異步執行,看起來應該沒啥問題了吧,但線程是比較重量級的,啓動一個線程帶來的消耗是比較大的,相對於這種毫秒級別的優化顯得毫無優點,最後結果就是使用了異步和以前的同步效果相差不大。。。第三次嘗試再次失敗。
  4. 既然不能每次開啓一個線程,那就使用線程池吧,每次使用直接從線程池中取線程就行了,每次只須要提交一個任務到線程池中,主線程就能夠結束了,後面的動做無感知,當任務比較多來不及處理時,還能夠放到阻塞隊列中。此次嘗試的結果比較滿意,除了線程池第一次啓動線程也須要必定的消耗,後面幾乎是零消耗。

Diff 效果

單位是微秒異步

同步執行

print access log cost time:  175002
print access log cost time:  18812
print access log cost time:  32655
print access log cost time:  60613
print access log cost time:  57820
print access log cost time:  6242
print access log cost time:  5541
print access log cost time:  17026
print access log cost time:  19471
複製代碼

異步執行

print access log cost time:  62
print access log cost time:  61
print access log cost time:  67
print access log cost time:  87
print access log cost time:  60
print access log cost time:  149
print access log cost time:  79
print access log cost time:  58
print access log cost time:  65
複製代碼
相關文章
相關標籤/搜索