摘要:當(dāng)被監(jiān)聽(tīng)的準(zhǔn)備好執(zhí)行連接應(yīng)答讀取等等操作時(shí),與操作相對(duì)應(yīng)的文件事件就會(huì)產(chǎn)生,根據(jù)文件事件來(lái)為關(guān)聯(lián)對(duì)應(yīng)的事件處理器,從而實(shí)現(xiàn)功能。服務(wù)器使用單線(xiàn)程單進(jìn)程的方式處理命令請(qǐng)求。
前言
只有光頭才能變強(qiáng)
好的,今天我們要上黃金段位了,如果還沒(méi)經(jīng)歷過(guò)青銅和白銀階段的,可以先去蹭蹭經(jīng)驗(yàn)再回來(lái):
從零單排學(xué)Redis【青銅】
從零單排學(xué)Redis【白銀】
看過(guò)相關(guān)Redis基礎(chǔ)的同學(xué)可以知道Redis是單線(xiàn)程的,很多面試題也很可能會(huì)問(wèn)到“為什么Redis是單線(xiàn)程的還那么快”。
這篇文章來(lái)講講單線(xiàn)程的內(nèi)部的原理。
文本力求簡(jiǎn)單講清每個(gè)知識(shí)點(diǎn),希望大家看完能有所收獲
一、基礎(chǔ)鋪墊在講解Redis之前,我們先來(lái)一些基礎(chǔ)的鋪墊,有更好的閱讀體驗(yàn)。
1.1網(wǎng)路編程我們?cè)诔鯇W(xué)Java的時(shí)候肯定會(huì)學(xué)過(guò)網(wǎng)絡(luò)編程這一章節(jié)的,當(dāng)時(shí)學(xué)完寫(xiě)的應(yīng)用可能就是“網(wǎng)絡(luò)聊天室”。
寫(xiě)出來(lái)的效果可能就是在console噼里啪啦的輸入數(shù)據(jù),然后噼里啪啦的返回?cái)?shù)據(jù),就完事了..(扎心了)
網(wǎng)絡(luò)編程可簡(jiǎn)單分為T(mén)CP和UPD兩種,一般我們更多關(guān)注的是TCP。TCP網(wǎng)絡(luò)編程在Java中封裝成Socket和SocketServer,我們來(lái)回顧一下最簡(jiǎn)單的TCP網(wǎng)絡(luò)編程吧:
TCP客戶(hù)端
public class ClientDemo { public static void main(String[] args) throws IOException { //創(chuàng)建發(fā)送端的Socket對(duì)象 Socket s = new Socket("192.168.1.106",8888); //Socket對(duì)象可以獲取輸出流 OutputStream os = s.getOutputStream(); os.write("hello,tcp,我來(lái)了".getBytes()); s.close(); } }
TCP服務(wù)端:
public class ServerDemo { public static void main(String[] args) throws IOException { //創(chuàng)建接收端的Socket對(duì)象 ServerSocket ss = new ServerSocket(8888); //監(jiān)聽(tīng)客戶(hù)端連接,返回一個(gè)對(duì)應(yīng)的Socket對(duì)象 //偵聽(tīng)并接受到此套接字的連接,此方法會(huì)阻塞 Socket s = ss.accept(); //獲取輸入流,讀取數(shù)據(jù) InputStream is = s.getInputStream(); byte[] bys = new byte[1024]; int len = is.read(bys); String str = new String (bys,0,len); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip + " ---" +str); //釋放資源 s.close(); //ss.close(); } }
上面的代碼就可以實(shí)現(xiàn):客戶(hù)端向服務(wù)器發(fā)送數(shù)據(jù),服務(wù)端能夠接收客戶(hù)端發(fā)送過(guò)來(lái)的數(shù)據(jù)。
1.2IO多路復(fù)用之前我已經(jīng)寫(xiě)過(guò)Java NIO的文章了,Java的NIO也是基于IO多路復(fù)用模型的,建議先去看一下再回來(lái),文章寫(xiě)得挺詳細(xì)和通俗的了:JDK10都發(fā)布了,nio你了解多少?
這里就簡(jiǎn)單回顧一下吧:
I/O多路復(fù)用的特點(diǎn)是通過(guò)一種機(jī)制一個(gè)進(jìn)程能同時(shí)等待多個(gè)文件描述符,而這些文件描述符其中的任意一個(gè)進(jìn)入讀就緒狀態(tài)、等等,select()函數(shù)就可以返回。
select/epoll的優(yōu)勢(shì)并不是對(duì)于單個(gè)連接能處理得更快,而是在于能處理更多的連接。
說(shuō)白了,使用IO多路復(fù)用機(jī)制的,一般自己會(huì)有一套事件機(jī)制,使用一個(gè)線(xiàn)程或者進(jìn)程監(jiān)聽(tīng)這些事件,如果這些事件被觸發(fā)了,則調(diào)用對(duì)應(yīng)的函數(shù)來(lái)處理。
二、Redis事件Redis服務(wù)器是一個(gè)事件驅(qū)動(dòng)程序,主要處理以下兩類(lèi)事件:
文件事件:文件事件其實(shí)就是對(duì)Socket操作的抽象,Redis服務(wù)器與Redis客戶(hù)端的通信會(huì)產(chǎn)生文件事件,服務(wù)器通過(guò)監(jiān)聽(tīng)并處理這些事件來(lái)完成一系列的網(wǎng)絡(luò)操作
時(shí)間事件:時(shí)間事件其實(shí)就是對(duì)定時(shí)操作的抽象,前面我們已經(jīng)講了RDB、AOF、定時(shí)刪除鍵這些操作都可以由服務(wù)端去定時(shí)或者周期去完成,底層就是通過(guò)觸發(fā)時(shí)間事件來(lái)實(shí)現(xiàn)的!
2.1文件事件Redis開(kāi)發(fā)了自己的網(wǎng)絡(luò)事件處理器,這個(gè)處理器被稱(chēng)為文件事件處理器。
文件事件處理器由四部分組成:
文件事件處理器使用I/O多路復(fù)用程序來(lái)同時(shí)監(jiān)聽(tīng)多個(gè)Socket。當(dāng)被監(jiān)聽(tīng)的Socket準(zhǔn)備好執(zhí)行連接應(yīng)答(accept)、讀取(read)等等操作時(shí),與操作相對(duì)應(yīng)的文件事件就會(huì)產(chǎn)生,根據(jù)文件事件來(lái)為Socket關(guān)聯(lián)對(duì)應(yīng)的事件處理器,從而實(shí)現(xiàn)功能。
要值得注意的是:Redis中的I/O多路復(fù)用程序會(huì)將所有產(chǎn)生事件的Socket放到一個(gè)隊(duì)列里邊,然后通過(guò)這個(gè)隊(duì)列以有序、同步、每次一個(gè)Socket的方式向文件事件分派器傳送套接字。也就是說(shuō):當(dāng)上一個(gè)Socket處理完畢后,I/O多路復(fù)用程序才會(huì)向文件事件分派器傳送下一個(gè)Socket。
首先,IO多路復(fù)用程序首先會(huì)監(jiān)聽(tīng)著Socket的AE_READABLE事件,該事件對(duì)應(yīng)著連接應(yīng)答處理器
可以理解簡(jiǎn)單成SocketServet.accpet()
此時(shí),一個(gè)名字叫做3y的Socket要連接服務(wù)器啦。服務(wù)器會(huì)用連接應(yīng)答處理器處理。創(chuàng)建出客戶(hù)端的Socket,并將客戶(hù)端的Socket與命令請(qǐng)求處理器進(jìn)行關(guān)聯(lián),使得客戶(hù)端可以向服務(wù)器發(fā)送命令請(qǐng)求。
相當(dāng)于Socket s = ss.accept();,創(chuàng)建出客戶(hù)端的Socket,然后將該Socket關(guān)聯(lián)命令請(qǐng)求處理器
此時(shí)客戶(hù)端就可以向主服務(wù)器發(fā)送命令請(qǐng)求了
假設(shè)現(xiàn)在客戶(hù)端發(fā)送一個(gè)命令請(qǐng)求set Java3y "關(guān)注、點(diǎn)贊、評(píng)論" ,客戶(hù)端Socket將產(chǎn)生AE_READABLE事件,引發(fā)命令請(qǐng)求處理器執(zhí)行。處理器讀取客戶(hù)端的命令內(nèi)容,然后傳給對(duì)應(yīng)的程序去執(zhí)行。
客戶(hù)端發(fā)送完命令請(qǐng)求后,服務(wù)端總得給客戶(hù)端回應(yīng)的。此時(shí)服務(wù)端會(huì)將客戶(hù)端的Scoket的AE_WRITABLE事件與命令回復(fù)處理器關(guān)聯(lián)。
最后客戶(hù)端嘗試讀取命令回復(fù)時(shí),客戶(hù)端Socket產(chǎn)生AE_WRITABLE事件,觸發(fā)命令回復(fù)處理器執(zhí)行。當(dāng)把所有的回復(fù)數(shù)據(jù)寫(xiě)入到Socket之后,服務(wù)器就會(huì)解除客戶(hù)端Socket的AE_WRITABLE事件與命令回復(fù)處理器的關(guān)聯(lián)。
最后以《Redis設(shè)計(jì)與實(shí)現(xiàn)》的一張圖來(lái)概括:
2.2時(shí)間事件持續(xù)運(yùn)行的Redis服務(wù)器會(huì)定期對(duì)自身的資源和狀態(tài)進(jìn)行檢查和調(diào)整,這些定期的操作由serverCron函數(shù)負(fù)責(zé)執(zhí)行,它的主要工作包括:
更新服務(wù)器的統(tǒng)計(jì)信息(時(shí)間、內(nèi)存占用、數(shù)據(jù)庫(kù)占用)
清理數(shù)據(jù)庫(kù)的過(guò)期鍵值對(duì)
AOF、RDB持久化
如果是主從服務(wù)器,對(duì)從服務(wù)器進(jìn)行定期同步
如果是集群模式,對(duì)進(jìn)群進(jìn)行定期同步和連接
...
Redis服務(wù)器將時(shí)間事件放在一個(gè)鏈表中,當(dāng)時(shí)間事件執(zhí)行器運(yùn)行時(shí),會(huì)遍歷整個(gè)鏈表。時(shí)間事件包括:
周期性事件(Redis一般只執(zhí)行serverCron時(shí)間事件,serverCron時(shí)間事件是周期性的)
定時(shí)事件
2.3時(shí)間事件和文件事件文件事件和時(shí)間事件之間是合作關(guān)系,服務(wù)器會(huì)輪流處理這兩種事件,并且處理事件的過(guò)程中不會(huì)發(fā)生搶占。
時(shí)間事件的實(shí)際處理事件通常會(huì)比設(shè)定的到達(dá)時(shí)間晚一些
三、Redis多線(xiàn)程為什么快?1)純內(nèi)存操作
2)核心是基于非阻塞的IO多路復(fù)用機(jī)制
3)單線(xiàn)程避免了多線(xiàn)程的頻繁上下文切換問(wèn)題
四、客戶(hù)端與服務(wù)器在《Redis設(shè)計(jì)與實(shí)現(xiàn)》中各用了一章節(jié)來(lái)寫(xiě)客戶(hù)端與服務(wù)器,我看完覺(jué)得比較底層的東西,也很難記得住,所以我決定總結(jié)一下比較重要的知識(shí)。如果以后真的遇到了,再來(lái)補(bǔ)坑~
服務(wù)器使用clints鏈表連接多個(gè)客戶(hù)端狀態(tài),新添加的客戶(hù)端狀態(tài)會(huì)被放到鏈表的末尾
一個(gè)服務(wù)器可以與多個(gè)客戶(hù)端建立網(wǎng)絡(luò)連接,每個(gè)客戶(hù)端可以向服務(wù)器發(fā)送命令請(qǐng)求,而服務(wù)器則接收并處理客戶(hù)端發(fā)送的命令請(qǐng)求,并向客戶(hù)端返回命令回復(fù)。
Redis服務(wù)器使用單線(xiàn)程單進(jìn)程的方式處理命令請(qǐng)求。在數(shù)據(jù)庫(kù)中保存客戶(hù)端執(zhí)行命令所產(chǎn)生的數(shù)據(jù),并通過(guò)資源管理來(lái)維持服務(wù)器自身的運(yùn)轉(zhuǎn)。
4.1客戶(hù)端客戶(hù)端章節(jié)中主要講解了Redis客戶(hù)端的屬性(客戶(hù)端狀態(tài)、輸入/輸出緩沖區(qū)、命令參數(shù)、命令函數(shù)等等)
typedef struct redisClient{ //客戶(hù)端狀態(tài)的輸入緩沖區(qū)用于保存客戶(hù)端發(fā)送的命令請(qǐng)求,最大1GB,否則服務(wù)器將關(guān)閉這個(gè)客戶(hù)端 sds querybuf; //負(fù)責(zé)記錄argv數(shù)組的長(zhǎng)度。 int argc; // 命令的參數(shù) robj **argv; // 客戶(hù)端要執(zhí)行命令的實(shí)現(xiàn)函數(shù) struct redisCommand *cmd, *lastcmd; //記錄了客戶(hù)端的角色(role),以及客戶(hù)端所處的狀態(tài)。 (REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI) int flags; //記錄客戶(hù)端是否通過(guò)了身份驗(yàn)證 int authenticated; //時(shí)間相關(guān)的屬性 time_t ctime; /* Client creation time */ time_t lastinteraction; /* time of the last interaction, used for timeout */ time_t obuf_soft_limit_reached_time; //固定大小的緩沖區(qū)用于保存那些長(zhǎng)度比較小的回復(fù) /* Response buffer */ int bufpos; char buf[REDIS_REPLY_CHUNK_BYTES]; //可變大小的緩沖區(qū)用于保存那些長(zhǎng)度比較大的回復(fù) list *reply; //可變大小緩沖區(qū)由reply 鏈表和一個(gè)或多個(gè)字符串對(duì)象組成 //... }4.2服務(wù)端
服務(wù)器章節(jié)中主要講解了Redis服務(wù)器讀取客戶(hù)端發(fā)送過(guò)來(lái)的命令是如何解析,以及初始化的過(guò)程。
服務(wù)器從啟動(dòng)到能夠處理客戶(hù)端的命令請(qǐng)求需要執(zhí)行以下的步驟:
初始化服務(wù)器狀態(tài)
載入服務(wù)器配置
初始化服務(wù)器的數(shù)據(jù)結(jié)構(gòu)
還原數(shù)據(jù)庫(kù)狀態(tài)
執(zhí)行事件循環(huán)
總的來(lái)說(shuō)是這樣子的:
def main(): init_server(); while server_is_not_shutdown(); aeProcessEvents() clean_server();
從客戶(hù)端發(fā)送命令道完成主要包括的步驟:
客戶(hù)端將命令請(qǐng)求發(fā)送給服務(wù)器
服務(wù)器讀取命令請(qǐng)求,分析出命令參數(shù)
命令執(zhí)行器根據(jù)參數(shù)查找命令的實(shí)現(xiàn)函數(shù),執(zhí)行實(shí)現(xiàn)函數(shù)并得出命令回復(fù)
服務(wù)器將命令回復(fù)返回給客戶(hù)端
五、最后現(xiàn)在臨近雙十一買(mǎi)阿里云服務(wù)器就特別省錢(qián)!之前我買(mǎi)學(xué)生機(jī)也要9.8塊錢(qián)一個(gè)月,現(xiàn)在最低價(jià)只需要8.3一個(gè)月!
無(wú)論是Nginx/Elasticsearch/Redis這些技術(shù)都是在Linux下完美運(yùn)行的,如果還是程序員新手,買(mǎi)一個(gè)學(xué)習(xí)Linux基礎(chǔ)命令,學(xué)習(xí)搭建環(huán)境也是不錯(cuò)的選擇。
如果有要買(mǎi)服務(wù)器的同學(xué)可通過(guò)我的鏈接直接享受最低價(jià):https://m.aliyun.com/act/team1111/#/share?params=N.FF7yxCciiM.pfn5xpli
本來(lái)也想把“復(fù)制”(主從)在這邊一起寫(xiě)的,但寫(xiě)完可能就很長(zhǎng)了,所以留到下一篇吧。
如果大家有更好的理解方式或者文章有錯(cuò)誤的地方還請(qǐng)大家不吝在評(píng)論區(qū)留言,大家互相學(xué)習(xí)交流~~~
參考資料:
《Redis設(shè)計(jì)與實(shí)現(xiàn)》
《Redis實(shí)戰(zhàn)》
一個(gè)堅(jiān)持原創(chuàng)的Java技術(shù)公眾號(hào):Java3y,歡迎大家關(guān)注
3y所有的原創(chuàng)文章:
文章的目錄導(dǎo)航(腦圖+海量視頻資源):https://github.com/ZhongFuCheng3y/3y
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/72111.html
摘要:前言只有光頭才能變強(qiáng)好的,今天我們要上鉑金段位了,如果還沒(méi)經(jīng)歷過(guò)青銅和白銀和黃金階段的,可以先去蹭蹭經(jīng)驗(yàn)再回來(lái)從零單排學(xué)青銅從零單排學(xué)白銀從零單排學(xué)黃金這篇文章主要講的是主從復(fù)制。 前言 只有光頭才能變強(qiáng) 好的,今天我們要上鉑金段位了,如果還沒(méi)經(jīng)歷過(guò)青銅和白銀和黃金階段的,可以先去蹭蹭經(jīng)驗(yàn)再回來(lái): 從零單排學(xué)Redis【青銅】 從零單排學(xué)Redis【白銀】 從零單排學(xué)Redis【黃金...
摘要:可以通過(guò)以下兩個(gè)配置盡量減少數(shù)據(jù)丟失的可能從零單排學(xué)鉑金三,敬請(qǐng)期待參考資料設(shè)計(jì)與實(shí)現(xiàn)實(shí)戰(zhàn)如果你覺(jué)得我寫(xiě)得還不錯(cuò),了解一下堅(jiān)持原創(chuàng)的技術(shù)公眾號(hào)。 前言 只有光頭才能變強(qiáng) 好的,今天我們要上【鉑金二】了,如果還沒(méi)有上鉑金的,趕緊先去蹭蹭經(jīng)驗(yàn)再回來(lái)(不然不帶你上分了): 從零單排學(xué)Redis【青銅】 從零單排學(xué)Redis【白銀】 從零單排學(xué)Redis【黃金】 從零單排學(xué)Redis【鉑金一...
閱讀 855·2021-11-15 17:58
閱讀 3658·2021-11-12 10:36
閱讀 3794·2021-09-22 16:06
閱讀 969·2021-09-10 10:50
閱讀 1333·2019-08-30 11:19
閱讀 3317·2019-08-29 16:26
閱讀 942·2019-08-29 10:55
閱讀 3349·2019-08-26 13:48