基于事件的 NIO 多线程服务器--转载
JDK1.4 的 NIO 有效解決了原有流式 IO 存在的線程開(kāi)銷(xiāo)的問(wèn)題,在 NIO 中使用多線程,主要目的已不是為了應(yīng)對(duì)每個(gè)客戶端請(qǐng)求而分配獨(dú)立的服務(wù)線程,而是通過(guò)多線程充分使用用多個(gè) CPU 的處理能力和處理中的等待時(shí)間,達(dá)到提高服務(wù)能力的目的。?
多線程的引入,容易為本來(lái)就略顯復(fù)雜的 NIO 代碼進(jìn)一步降低可讀性和可維護(hù)性。引入良好的設(shè)計(jì)模型,將不僅帶來(lái)高性能、高可靠的代碼,也將帶來(lái)一個(gè)愜意的開(kāi)發(fā)過(guò)程。
線程模型
NIO 的選擇器采用了多路復(fù)用(Multiplexing)技術(shù),可在一個(gè)選擇器上處理多個(gè)套接字, 通過(guò)獲取讀寫(xiě)通道來(lái)進(jìn)行 IO 操作。由于網(wǎng)絡(luò)帶寬等原因,在通道的讀、寫(xiě)操作中是容易出現(xiàn)等待的, 所以在讀、寫(xiě)操作中引入多線程,對(duì)性能提高明顯,而且可以提高客戶端的感知服務(wù)質(zhì)量。所以本文的模型將主要通過(guò)使用讀、寫(xiě)線程池 來(lái)提高與客戶端的數(shù)據(jù)交換能力。
如下圖所示,服務(wù)端接受客戶端請(qǐng)求后,控制線程將該請(qǐng)求的讀通道交給讀線程池,由讀線程池分配線程完成對(duì)客戶端數(shù)據(jù)的讀取操作;當(dāng)讀線程完成讀操作后,將數(shù)據(jù)返回控制線程,進(jìn)行服務(wù)端的業(yè)務(wù)處理;完成 業(yè)務(wù)處理后,將需回應(yīng)給客戶端的數(shù)據(jù)和寫(xiě)通道提交給寫(xiě)線程池,由寫(xiě)線程完成向客戶端發(fā)送回應(yīng)數(shù)據(jù)的操作。
(NIO 多線程服務(wù)器模型)
同時(shí)整個(gè)服務(wù)端的流程處理,建立于事件機(jī)制上。在?[接受連接->讀->業(yè)務(wù)處理->寫(xiě)?->關(guān)閉連接?]這個(gè) 過(guò)程中,觸發(fā)器將觸發(fā)相應(yīng)事件,由事件處理器對(duì)相應(yīng)事件分別響應(yīng),完成服務(wù)器端的業(yè)務(wù)處理。?
下面我們就來(lái)詳細(xì)看一下這個(gè)模型的各個(gè)組成部分。
回頁(yè)首
相關(guān)事件定義 在這個(gè)模型中,我們定義了一些基本的事件:
(1)onAccept:當(dāng)服務(wù)端收到客戶端連接請(qǐng)求時(shí),觸發(fā)該事件。通過(guò)該事件我們可以知道有新的客戶端呼入。該事件可用來(lái)控制服務(wù)端的負(fù)載。例如,服務(wù)器可設(shè)定同時(shí)只為一定數(shù)量客戶端提供服務(wù),當(dāng)同時(shí)請(qǐng)求數(shù)超出數(shù)量時(shí),可在響應(yīng)該事件時(shí)直接拋出異常,以拒絕新的連接。?
(2)onAccepted:當(dāng)客戶端請(qǐng)求被服務(wù)器接受后觸發(fā)該事件。該事件表明一個(gè)新的客戶端與服務(wù)器正式建立連接。?
(3)onRead:當(dāng)客戶端發(fā)來(lái)數(shù)據(jù),并已被服務(wù)器控制線程正確讀取時(shí),觸發(fā)該事件 。該事件通知各事件處理器可以對(duì)客戶端發(fā)來(lái)的數(shù)據(jù)進(jìn)行實(shí)際處理了。需要注意的是,在本模型中,客戶端的數(shù)據(jù)讀取是由控制線程交由讀線程完成的,事件處理器不需要在該事件中進(jìn)行專(zhuān)門(mén)的讀操作,而只需將控制線程傳來(lái)的數(shù)據(jù)進(jìn)行直接處理即可。?
(4)onWrite:當(dāng)客戶端可以開(kāi)始接受服務(wù)端發(fā)送數(shù)據(jù)時(shí)觸發(fā)該事件,通過(guò)該事件,我們可以向客戶端發(fā)送回應(yīng)數(shù)據(jù)。 在本模型中,事件處理器只需要在該事件中設(shè)置?
(5)onClosed:當(dāng)客戶端與服務(wù)器斷開(kāi)連接時(shí)觸發(fā)該事件。?
(6)onError:當(dāng)客戶端與服務(wù)器從連接開(kāi)始到最后斷開(kāi)連接期間發(fā)生錯(cuò)誤時(shí)觸發(fā)該事件。通過(guò)該事件我們可以知道有什么錯(cuò)誤發(fā)生。
回頁(yè)首
事件回調(diào)機(jī)制的實(shí)現(xiàn)
在這個(gè)模型中,事件采用廣播方式,也就是所有在冊(cè)的事件處理器都能獲得事件通知。這樣可以將不同性質(zhì)的業(yè)務(wù)處理,分別用不同的處理器實(shí)現(xiàn),使每個(gè)處理器的業(yè)務(wù)功能盡可能單一。?
如下圖:整個(gè)事件模型由監(jiān)聽(tīng)器、事件適配器、事件觸發(fā)器、事件處理器組成。
(事件模型)
public interface Serverlistener { public void onError(String error); public void onAccept() throws Exception; public void onAccepted(Request request) throws Exception; public void onRead(Request request) throws Exception; public void onWrite(Request request, Response response) throws Exception; public void onClosed(Request request) throws Exception; }
public abstract class EventAdapter implements Serverlistener { public EventAdapter() { } public void onError(String error) {} public void onAccept() throws Exception {} public void onAccepted(Request request) throws Exception {} public void onRead(Request request) throws Exception {} public void onWrite(Request request, Response response) throws Exception {} public void onClosed(Request request) throws Exception {} }
public class Notifier { private static Arraylist listeners = null; private static Notifier instance = null; private Notifier() { listeners = new Arraylist(); } /** * 獲取事件觸發(fā)器* @return 返回事件觸發(fā)器*/ public static synchronized Notifier getNotifier() { if (instance == null) { instance = new Notifier(); return instance; } else return instance; } /** * 添加事件監(jiān)聽(tīng)器* @param l 監(jiān)聽(tīng)器*/ public void addlistener(Serverlistener l) { synchronized (listeners) { if (!listeners.contains(l)) listeners.add(l); } } public void fireOnAccept() throws Exception { for (int i = listeners.size() - 1; i >= 0; i--) ( (Serverlistener) listeners.get(i)).onAccept(); } ....// other fire method }
public class ServerHandler extends EventAdapter { public ServerHandler() { } public void onRead(Request request) throws Exception { System.out.println("Received: " + new String(data)); } }
ServerHandler handler = new ServerHandler(); Notifier.addlistener(handler);
回頁(yè)首
實(shí)現(xiàn) NIO 多線程服務(wù)器
NIO 多線程服務(wù)器主要由主控服務(wù)線程、讀線程和寫(xiě)線程組成。
(線程模型)
回頁(yè)首
具體應(yīng)用
NIO 多線程模型的實(shí)現(xiàn)告一段落,現(xiàn)在我們可以暫且將 NIO 的各個(gè) API 和煩瑣的調(diào)用方法拋于腦后,專(zhuān)心于我們的實(shí)際應(yīng)用中。?
我們用一個(gè)簡(jiǎn)單的 TimeServer(時(shí)間查詢服務(wù)器)來(lái)看看該模型能帶來(lái)多么簡(jiǎn)潔的開(kāi)發(fā)方式。?
在這個(gè) TimeServer 中,將提供兩種語(yǔ)言(中文、英文)的時(shí)間查詢服務(wù)。我們將讀取客戶端的查詢命令(GB/EN),并回應(yīng)相應(yīng)語(yǔ)言格式的當(dāng)前時(shí)間。在應(yīng)答客戶的請(qǐng)求的同時(shí),服務(wù)器將進(jìn)行日志記錄。做為示例,對(duì)日志記錄,我們只是簡(jiǎn)單地將客戶端的訪問(wèn)時(shí)間和 IP 地址輸出到服務(wù)器的終端上。
public class TimeHandler extends EventAdapter { public TimeHandler() { } public void onWrite(Request request, Response response) throws Exception { String command = new String(request.getDataInput()); String time = null; Date date = new Date(); // 判斷查詢命令if (command.equals("GB")) { // 中文格式DateFormat cnDate = DateFormat.getDateTimeInstance(DateFormat.FulL, DateFormat.FulL, Locale.CHINA); time = cnDate.format(date); } else { // 英文格式DateFormat enDate = DateFormat.getDateTimeInstance(DateFormat.FulL, DateFormat.FulL, Locale.US); time = enDate.format(date); } response.send(time.getBytes()); } }
回頁(yè)首
小結(jié)
通過(guò)例子我們可以看到,基于事件回調(diào)的 NIO 多線程服務(wù)器模型,提供了清晰直觀的實(shí)現(xiàn)方式,可讓開(kāi)發(fā)者從 NIO 及多線程的技術(shù)細(xì)節(jié)中擺脫出來(lái),集中精力關(guān)注具體的業(yè)務(wù)實(shí)現(xiàn)。
原文:http://www.ibm.com/developerworks/cn/java/l-niosvr/
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/3797811.html
總結(jié)
以上是生活随笔為你收集整理的基于事件的 NIO 多线程服务器--转载的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: SonarQube代码质量管理平台安装与
- 下一篇: JAVA 上加密算法的实现用例---转载