這篇文章為大家帶來了關於Redis的相關知識,其中主要介紹了文件事件與時間事件的相關問題,文件事件就是伺服器對套接字操作的抽象,時間事件就是伺服器對這類定時操作的抽象,希望對大家有幫助。
推薦學習:Redis教學
#Redis在6.0以前是單執行緒的,在6.0之後可以透過設定檔開啟多線程,6.0之後的多線程是指在io方面使用多線程來執行以加快I/O的速度。
檔案事件處理器是由四個部分組成的分別是套接字、I/O多工程式、檔案事件分派器、事件處理器。
I/O多工程式負責監聽多個套接字,並向檔案事件分派器傳送那些產生了事件的套接字。儘管多個文件事件可能並發的出現,單I/O多路復用程序總是會將所有產的事件的套接字都放到一個隊列裡面,然後通過這個隊列,以有序的、同步的、每次一個套接字的方式向文件事件分派傳送套接字。當上一個套接字產生的事件被處理完畢之後(此套接字所關聯的事件處理執行完畢),I/O多路復用程式才會繼續傳送下一個套接字,檔案事件分派器接收I/O多工程式傳來的套接字,並根據套接字產生的事件的類型條用對應的事件處理器,伺服器會執行不同人物的套接字關聯不同的事件處理器,這些處理器定義了某個事件發生時伺服器該執行的動作。
Redis的多路復用程式的所有功能都是透過包裝select、epoll、evport、kqueue這些I/O多路復用函數庫來實現的
AE_READABLE事件
當套接字變得可讀時(客戶端執行write或close操作)或有新的可應答的套接字出現時套接字將會產生AE_READABLE事件。
AE_WAITABLE事件
當套接字變成可寫時(客戶端執行read操作)將產生AE_WAITABLE事件
I/O多路復用程式會同時監聽AE_READABLE事件和AE_WAITABLE事件,如果一個套接字同時產生了這兩種事件,那麼事件分派器會優先處理AE_READABLE事件,也即是說伺服器將先讀套接字後寫套接字。
首先Redis客戶端向伺服器發起連接,那麼監聽套接字將產生AE_READABEL事件,觸發連線應答處理器執行,處理器會對客戶端的連接請求進行應答,然後創建客戶端套接字,以及客戶端狀態,並將客戶端套接字的AE_READABEL事件與命令請求處理器進行關聯,使得客戶端可以向主伺服器發送命令請求。
之後假設客戶端向主伺服器發送一個命令請求,那麼客戶端套接字將產生AE_READABEL事件,引發命令請求處理器執行,處理器讀取客戶端的命令,然後傳給相關聯的程式去執行。
執行指令將產生對應的指令回复,為了將這行指令回復傳送回給客戶端,伺服器會將AE_WAITABLE事件和指令回復處理器進行關聯。當客戶端嘗試讀取命令回覆的時候客戶端會產生AE_WAITABLE事件,觸發命令回復處理器執行,當命令回復處理器將命令回復全服寫入套接字之後,伺服器就會解除客戶端套接字的AE_WAITABLE事件與命令回覆處理器執行的關聯。
一個時間事件是定時事件還是週期性事件取決於時間事件處理器的回傳值,如果事件處理器回傳ae.h/AE_NOMORE,那麼這個事件為定時事件,該事件在到達一次後就會刪除,之後不再到達。如果事件處理器回傳一個非AE_NOMORE的整數值,那麼這個事件就是週期性事件,當一個時間事件達到後,伺服器會根據事件處理器的回傳值,對事件的when屬性進行更新,讓這個事件在一段時間後再次到達,並以這種方式一直更新並運行下去。
伺服器將所有時間事件都放在一個無序鍊錶中(無序的並不是指id字段,而是when字段所以每次執行都要遍歷完真個鍊錶。),每當時間事件執行器運作時,它就會遍歷整個鍊錶,查找到所有已經到達的事件,並呼叫對應的事件處理器。
這裡要說明的是雖然是無序鍊錶但是由於鍊錶的長度不會很長正常模式下Redis伺服器只使用serverCron一個時間事件所以這個地方機會退化成了指針的作用,而benchmark模式下,伺服器也只使用兩個時間事件,所以全遍歷對效能影響可以忽略。
持續運行的Redis伺服器需要定期對自身的資源和狀態進行檢查和調整,從而確保伺服器可以長期、穩定地運行,這些定期操作由redis.c/ serverCron函式負責執行,它的主要工作包括:
因為伺服器中同時存在檔案事件和時間事件兩種事件類型,所以伺服器必須對這兩個事件進行調度,決定何時應該處理文件事件,何時又應該處理時間事件,以及花多少時間來處理它們等等。
處理過程的偽代碼如下:
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學習教學
#以上是一起聊聊redis文件事件和時間事件的詳細內容。更多資訊請關注PHP中文網其他相關文章!