一起聊聊redis檔案事件和時間事件

2022-03-08 19:01:04
本篇文章給大家帶來了關於的相關知識,其中主要介紹了檔案事件與時間事件的相關問題,檔案事件就是伺服器對通訊端操作的抽象,時間事件就是伺服器對這類定時操作的抽象,希望對大家有幫助。

推薦學習:

Redis在6.0以前是單執行緒的,在6.0之後可以通過組態檔開啟多執行緒,6.0之後的多執行緒是指在io方面使用多執行緒來執行以加快I/O的速度。

Redis伺服器是一個事件驅動程式,伺服器需要處理以下兩類事件:

  • 檔案事件(file event)
    Redis伺服器通過通訊端與使用者端(或者其他Redis伺服器)進行連線,而檔案事件就是伺服器對通訊端操作的抽象。伺服器與使用者端(或者其他伺服器)的通訊會產生相應的檔案事件,而伺服器則通過監聽並處理這些事件來完成一系列網路通訊操作。檔案事件處理器使用I/O多路複用(multiplexing)程式來同時監聽多個通訊端,並根據通訊端目前執行的任務來為通訊端關聯不同的事件處理器。當被監聽的通訊端準備好執行連線應答(accept)、讀取(read)、寫入(write)、關閉(close)等操作時,與操作相對應的檔案事件就會產生,這時檔案事件處理器就會呼叫通訊端之前關聯好的事件處理器來處理這些事件。
  • 時間事件(time event)
    Redis伺服器中的一些操作(比如serverCron函數)需要在給定的時間點執行,而時間事件就是伺服器對這類定時操作的抽象。時間事件分兩類第一類是定時事件(讓一段程式在指定的時間之後執行一次)另一類是週期性事件(讓一段程式每隔指定時間就執行一次)。

一、檔案事件

1、檔案事件處理器

檔案事件處理器是由四部分組成的分別是通訊端、I/O多路複用程式、檔案事件分派器、事件處理器。
在這裡插入圖片描述
I/O多路複用程式負責監聽多個通訊端,並向檔案事件分派器傳送那些產生了事件的通訊端。儘管多個檔案事件可能會並行的出現,單I/O多路複用程式總是會將所有產的事件的通訊端都放到一個佇列裡面,然後通過這個佇列,以有序的、同步的、每次一個通訊端的方式向檔案事件分派傳送通訊端。當上一個通訊端產生的事件被處理完畢之後(該通訊端所關聯的事件處理執行完畢),I/O多路複用程式才會繼續向檔案事件分派器傳送下一個通訊端,檔案事件分派器接收I/O多路複用程式傳來的通訊端,並根據通訊端產生的事件的型別條用相應的事件處理器,伺服器會執行不同人物的通訊端關聯不同的事件處理器,這些處理器定義了某個事件發生時伺服器該執行的動作。

2、I/O多路複用

Redis的多路複用程式的所有功能都是通過包裝select、epoll、evport、kqueue這些I/O多路複用函數庫來實現的

3、檔案事件型別

  • AE_READABLE事件
    當通訊端變得可讀時(使用者端執行write或者close操作)或者有新的可應答的通訊端出現時通訊端將會產生AE_READABLE事件。

  • AE_WAITABLE事件
    當通訊端變得可寫時(使用者端執行read操作)將產生AE_WAITABLE事件
    I/O多路複用程式會同時監聽AE_READABLE事件和AE_WAITABLE事件,如果一個通訊端同時產生了這兩種事件,那麼事件分派器會優先處理AE_READABLE事件,也即是說伺服器將先讀通訊端後寫通訊端。

4、檔案事件的處理器

  • 連線應答處理器
    連線應答處理器,這個處理器用於對連線伺服器監聽通訊端的使用者端進行應答,當Redis伺服器進行初始化的時候,程式會將這個連線應答處理器和伺服器監聽通訊端的AE_READABLE事件關聯起來,當用戶端連線伺服器監聽通訊端的時候,通訊端就會產生AE_READABLE事件,引發連線應答處理器執行,並執行相應的通訊端應答操作。
  • 命令請求處理器
    當一個使用者端通過連線應答處理器成功連線到伺服器之後,伺服器端會將通訊端的AE_READABLE事件和命令請求處理器關聯起來,當用戶端向伺服器端傳送命令請求的時候,通訊端就會產生AE_READABLE事件,引發命令請求處理器執行,並執行通訊端讀入等操作。在使用者端連線伺服器的整個過程中,伺服器都會一直為使用者端通訊端的AE_READABEL事件關聯命令請求處理器。
  • 命令回覆處理器
    當伺服器有命令回覆需要傳送給使用者端的時候,伺服器會將使用者端通訊端的AE_WRITABLE事件和命令回覆處理器關聯起來,當用戶端準備好接收伺服器回傳的命令回覆時,就會產生AE_WAITABEL事件,引發命令回覆處理器執行,並執行相應的通訊端寫操作。當命令回覆傳送完畢之後,伺服器就會解除命令回覆處理器與使用者端通訊端的AE_WAITABLE事件之間的關聯。

5、一次完成的使用者端與伺服器連線事件範例

首先Redis使用者端向伺服器發起連線,那麼監聽通訊端將產生AE_READABEL事件,觸發連線應答處理器執行,處理器會對使用者端的連線請求進行應答,然後建立使用者端通訊端,以及使用者端狀態,並將使用者端通訊端的AE_READABEL事件與命令請求處理器進行關聯,使得使用者端可以向主伺服器傳送命令請求。

之後假設使用者端向主伺服器傳送一個命令請求,那麼使用者端通訊端將產生AE_READABEL事件,引發命令請求處理器執行,處理器讀取使用者端的命令,然後傳給相關聯的程式去執行。

執行命令將產生相應的命令回覆,為了將這行命令回覆傳送回給使用者端,伺服器會將AE_WAITABLE事件和命令回覆處理器進行關聯。當用戶端嘗試讀取命令回覆的時候使用者端會產生AE_WAITABLE事件,觸發命令回覆處理器執行,當命令回覆處理器將命令回覆全服寫入通訊端之後,伺服器就會解除使用者端通訊端的AE_WAITABLE事件與命令回覆處理器執行的關聯。

二、時間事件

1、時間事件的組成

  • id
    伺服器為時間事件建立全域性唯一ID。ID號從小到大順序遞增。
  • when
    毫秒精確度的UNIX時間戳,記錄了時間事件的到達時間。
  • timeProc
    時間事件處理器,一個函數。當時間事件到達時,伺服器就會呼叫相應的處理器來處理事件。

一個時間事件是定時事件還是週期性事件取決於時間事件處理器的返回值,如果事件處理器返回ae.h/AE_NOMORE,那麼這個事件為定時事件,該事件在到達一次後就會被刪除,之後不再到達。如果事件處理器返回一個非AE_NOMORE的整數值,那麼這個事件為週期性事件,當一個時間事件達到後,伺服器會根據事件處理器的返回值,對事件的when屬性進行更新,讓這個事件在一段時間後再次到達,並以這種方式一直更新並執行下去。

實現

伺服器將所有時間事件都放在一個無序連結串列中(無序的並不是指id欄位,而是when欄位所以每次執行都要遍歷完真個連結串列。),每當時間事件執行器執行時,它就會遍歷整個連結串列,查詢到所有已經到達的事件,並呼叫相應的事件處理器。
這裡需要說明的是雖然是無序連結串列但是由於連結串列的長度不會很長正常模式下Redis伺服器只使用serverCron一個時間事件所以這個地方機會退化成了指標的作用,而benchmark模式下,伺服器也只使用兩個時間事件,所以全遍歷對效能影響可以忽略。

serverCron函數

持續執行的Redis伺服器需要定期對自身的資源和狀態進行檢查和調整,從而確保伺服器可以長期、穩定地執行,這些定期操作由redis.c/serverCron函數負責執行,它的主要工作包括:

  • 更新伺服器的各類統計資訊,比如時間、記憶體佔用、資料庫佔用等情況。
  • 清理資料庫中過期的鍵值對。
  • 關閉和清理連線失效的使用者端。
  • 嘗試進行AOF或者RDB持久化操作。
  • 如果伺服器是主伺服器,那麼對從伺服器進行定期同步
  • 如果處於叢集模式,對叢集進行定期同步和連線測試。

事件的排程與執行

因為伺服器中同時存在檔案事件和時間事件兩種事件型別,所以伺服器必須對這兩種事件進行排程,決定何時應該處理檔案事件,何時又應該處理時間事件,以及花多少時間來處理它們等等。
處理過程的虛擬碼如下:

def aeProcessEvents():
	# 獲取到達時間離當前最近的時間事件
	tem_event = aeSearchNearestTimer()
	
	# 計算上一步獲得到的事件 距離到達還有多少秒
	remaind_ms = time_event.when - unix_ts_now()
	
	# 如果事件已經到達, 那麼remaind_ms的值可能為負數,設定為0
	remaind_ms = max(remaind_ms, 0)
	
	# 阻塞並等待檔案事件產生,最大阻塞時間由timeval結構決定,
	# 如果remaind_ms的值為0,那麼aeAPiPoll呼叫之後馬上返回,不阻塞
	aeApiPoll(timeval)
	# 處理所有已經產生的檔案事件
	processFileEvents()
	# 處理所有已經到達的時間事件
	proccessTimeEvents()

事件的排程和執行規則:
1)aeApiPoll函數的最大阻塞時間由到達時間最接近當前時間的時間事件決定,這個方法既可以避免伺服器對時間事件進行頻繁的輪詢(忙等待),也可以確保aeApiPoll函數不會阻塞過長時間。

2)因為檔案事件是隨機出現的,如果等待並處理完一次檔案事件之後,仍未有任何時間事件到達,那麼伺服器將再次等待並處理檔案事件。隨著檔案事件的不斷執行,時間會逐漸向時間事件所設定的到達時間逼近,並最終來到到達時間,這時伺服器就可以開始處理到達的時間事件了。

3)對檔案事件和時間事件的處理都是同步、有序、原子地執行的,伺服器不會中途中斷事件處理,也不會對事件進行搶佔,因此,不管是檔案事件的處理器,還是時間事件的處理器,它們都會盡可地減少程式的阻塞時間,並在有需要時主動讓出執行權,從而降低造成事件飢餓的可能性。比如說,在命令回覆處理器將一個命令回覆寫人到使用者端通訊端時,如果寫人位元組數超過了一個預設常數的話,命令回覆處理器就會主動用break跳出寫入迴圈,將餘下的資料留到下次再寫;另外,時間事件也會將非常耗時的持久化操作放到子執行緒或者子程序執行。

4)因為時間事件在檔案事件之後執行,並且事件之間不會出現搶佔,所以時間事件的實際處理時間,通常會比時間事件設定的到達時間稍晚一些。

檔案事件和時間事件之間是合作關係,伺服器會輪流處理這兩種事件,並且處理事件的過程中也不會進行搶佔。時間事件的實際處理時間通常會比設定的到達時間晚一些。

推薦學習:

以上就是一起聊聊redis檔案事件和時間事件的詳細內容,更多請關注TW511.COM其它相關文章!