1 源于生活的設(shè)計(jì)模式
我們?cè)谏钪杏幸粋€(gè)非常不愿意去但是卻又不得不去的地方就是醫(yī)院乒验,我們?nèi)メt(yī)院看病需要經(jīng)歷一下流程:
第1步:掛號(hào)
第2步:等待叫號(hào)
第3步:找對(duì)應(yīng)的醫(yī)生講述病情惯退,醫(yī)生根據(jù)癥狀開(kāi)化驗(yàn)單
第4步:我們帶上化驗(yàn)單使用設(shè)備化驗(yàn)
第5步:等待化驗(yàn)結(jié)果出來(lái)之后我們要拿上化驗(yàn)單再去找醫(yī)生
第6步:醫(yī)生根據(jù)化驗(yàn)結(jié)果對(duì)癥下藥。
每個(gè)人都不愿意去醫(yī)院虑鼎,因?yàn)槲覀兏杏X(jué)去醫(yī)院非常麻煩菌羽,經(jīng)歷上面的流程一天的時(shí)間就過(guò)去了赌莺,在這所有的流程中等待叫號(hào)和等化驗(yàn)結(jié)果時(shí)間是最長(zhǎng)的泰讽,有的化驗(yàn)單可能好幾天才能出來(lái)抢韭,化驗(yàn)單出來(lái)之后也需要排隊(duì)讓醫(yī)生看化驗(yàn)結(jié)果薪贫。
作為病人希望到醫(yī)院之后有一對(duì)一的服務(wù),到醫(yī)院之后有對(duì)應(yīng)的醫(yī)生給我們?nèi)谈櫩床 ?/p>
但是如果我們作為醫(yī)院的管理者會(huì)怎么思考這個(gè)問(wèn)題呢刻恭,醫(yī)院的醫(yī)生和設(shè)備數(shù)量是有限的瞧省,但是每天的病人卻很多,基本上是醫(yī)生的幾十倍鳍贾,如何使用有限的醫(yī)生和設(shè)備資源解決更多病人的問(wèn)題呢鞍匾。
假設(shè)醫(yī)院有10個(gè)醫(yī)生,20臺(tái)設(shè)備骑科,醫(yī)生執(zhí)行第3步需要10分鐘橡淑,執(zhí)行第6步需要10分鐘,設(shè)備執(zhí)行第4步需要20分鐘咆爽,第5步需要20分鐘梁棠。如果醫(yī)生接到病人之后給病人開(kāi)化驗(yàn)單,然后等著病人出結(jié)果之后給病人開(kāi)藥斗埂,再送走符糊,就是從第3步到第6步一直有醫(yī)生跟著,這樣會(huì)非常耗時(shí)呛凶,一個(gè)醫(yī)生看一個(gè)病人需要花費(fèi) 10+20+20+10=60分鐘 的時(shí)間男娄,一天8小時(shí)只能給8病人看病,一個(gè)醫(yī)院每天只能接待108=80個(gè)病人漾稀。但是如果醫(yī)生開(kāi)完化驗(yàn)單之后病人去化驗(yàn)這段時(shí)間給下個(gè)掛過(guò)號(hào)的病人看病模闲,即執(zhí)行完第3步之后,醫(yī)生可以繼續(xù)叫號(hào)執(zhí)行第2步崭捍,病人拿到化驗(yàn)單之后即執(zhí)行完第5步之后可以再去找醫(yī)生執(zhí)行第6步尸折,這樣就把資源充分利用起來(lái)了,一個(gè)醫(yī)生看一個(gè)病人只需要精力第3步和第6不缕贡,花費(fèi)10+10=20分鐘翁授,每天能夠給860/20=24個(gè)人看病,整個(gè)醫(yī)院每天能接待10*24=240個(gè)病人晾咪。這就是Reactor設(shè)計(jì)模式最核心的思想收擦,解決了一對(duì)多的問(wèn)題,大大提升了資源的使用率谍倦。
【文章福利】需要C/C++ Linux服務(wù)器架構(gòu)師學(xué)習(xí)資料加群1106747042(資料包括C/C++塞赂,Linux,golang技術(shù)昼蛀,Nginx宴猾,ZeroMQ圆存,MySQL,Redis仇哆,fastdfs沦辙,MongoDB,ZK讹剔,流媒體油讯,CDN,P2P延欠,K8S陌兑,Docker,TCP/IP由捎,協(xié)程兔综,DPDK,ffmpeg等)
2 Reactor設(shè)計(jì)模式實(shí)現(xiàn)
NIO是使用reactor設(shè)計(jì)模式非常經(jīng)典的案例狞玛,傳統(tǒng)的IO客戶(hù)端過(guò)來(lái)一個(gè)連接软驰,服務(wù)端就需要專(zhuān)門(mén)一個(gè)線程來(lái)處理,每一個(gè)線程會(huì)將一次交互操作全部處理完成心肪,包括讀取和返回碌宴,表面上似乎連接不在線程里面,但是如果線程不夠蒙畴,新連接將無(wú)法得到處理。所以一個(gè)線程就肩負(fù)了連接呜象、讀取膳凝、寫(xiě)入的責(zé)任。
這種處理模式海量并發(fā)的情況下即使引入線程池也不能滿(mǎn)足需要恭陡,這個(gè)時(shí)候考慮將一次完整的請(qǐng)求切分為幾個(gè)小的任務(wù)蹬音,就像醫(yī)院把醫(yī)生問(wèn)診和儀器檢查分開(kāi)一樣,每個(gè)小任務(wù)都是非阻塞的休玩,對(duì)于讀寫(xiě)操作使用NIO對(duì)其進(jìn)行讀寫(xiě)著淆。所以我們把一次連接操作拆分為多個(gè)任務(wù),每個(gè)任務(wù)都是非阻塞的拴疤,這樣就大大提升了效率永部,但是這樣做線程池中的線程數(shù)量業(yè)也會(huì)大大增長(zhǎng),但是線程更加簡(jiǎn)單且任務(wù)單一呐矾,有點(diǎn)像我們微服務(wù)的思想苔埋。
Reactor從線程池和Reactor的選擇上可以細(xì)分為:Reactor單線程模型,Reactor多線程模型蜒犯,Reactor主從模型组橄。
2.1 Reactor單線程模型
單線程模型是針對(duì)客戶(hù)端請(qǐng)求使用一個(gè)專(zhuān)門(mén)的線程去處理荞膘,這個(gè)線程循環(huán)監(jiān)聽(tīng)是否有客戶(hù)端的請(qǐng)求抵達(dá),一旦收到客戶(hù)端請(qǐng)求玉工,將其分發(fā)給相應(yīng)處理線程進(jìn)行處理羽资。這種模式采用了基于事件驅(qū)動(dòng)的設(shè)計(jì),當(dāng)有事件觸發(fā)時(shí)才會(huì)調(diào)用處理器進(jìn)行數(shù)據(jù)處理遵班。使用Reactor模式可以對(duì)線程的數(shù)量進(jìn)行控制屠升,可以使用一個(gè)線程去處理大量的事件。
2.2 Reactor多線程模型
使用一個(gè)線程可以支持所有的IO處理费奸,但是瓶頸也是顯而易見(jiàn)的弥激,如果客戶(hù)端多次請(qǐng)求時(shí)在業(yè)務(wù)線程中處理較慢,后續(xù)的客戶(hù)端會(huì)被積壓愿阐,導(dǎo)致響應(yīng)變慢微服,所以需要引入Reactor多線程模型。
可以將工作線程引入線程池缨历,將處理器的執(zhí)行放入線程池以蕴,并使用多線程處理業(yè)務(wù)邏輯。
2.3 Reactor主從模型
對(duì)于多個(gè)CPU的機(jī)器辛孵,為了充分利用系統(tǒng)資源會(huì)將Reactor拆分為兩部分丛肮。
1)Main Reactor 負(fù)責(zé)監(jiān)聽(tīng)連接,將監(jiān)聽(tīng)到的連接交給Sub Reactor處理魄缚,主Reactor用于響應(yīng)連接請(qǐng)求宝与。
2)Sub Reactor 處理連接,從Reactor用于處理IO操作請(qǐng)求冶匹。
3 架構(gòu)模型
Handle:操作系統(tǒng)的句柄习劫,可以是打開(kāi)的文件、一個(gè)Socket連接嚼隘、Timer定時(shí)器等诽里。
Synchronous Event Demultiplexer :同步(多路)事件分離器,阻塞等待Handles中的事件發(fā)生飞蛹。
Initiation Dispatcher :初始事件分發(fā)器谤狡,提供了注冊(cè)、刪除卧檐、轉(zhuǎn)發(fā)Event Handler的方法
Event Handler :事件處理器的接口
Concrete Event Handler :事件處理器的實(shí)際實(shí)現(xiàn)墓懂,而且綁定了一個(gè)Handle。因?yàn)樵趯?shí)際情況中泄隔,我們往往不止一種事件處理器拒贱,因此這里將事件處理器接口和實(shí)現(xiàn)分開(kāi),與C++、Java這些高級(jí)語(yǔ)言中的多態(tài)類(lèi)似逻澳。
————————————————