如何在百度做網(wǎng)站推廣疫情防控最新通告
前言:
小弟能力不足,認(rèn)知有限,難免考慮不全面,希望大佬能給出更好的建議,指出存在的問(wèn)題和不足,在此跪謝。
IO發(fā)展史
Java中對(duì)于I/O能力的支持主要分為三個(gè)比較關(guān)鍵的階段:
BIO
第一個(gè)階段是起步階段JDK1.0 ~ JDK1.3,這個(gè)階段JDK是處于BIO階段的,也就是同步阻塞模式,該階段的類庫(kù)還非常的初級(jí),對(duì)系統(tǒng)層面的一些網(wǎng)絡(luò)編程的API都沒(méi)有進(jìn)行實(shí)現(xiàn),因此這個(gè)階段的很多大型應(yīng)用服務(wù)器都采用C或者C++語(yǔ)言來(lái)進(jìn)行開(kāi)發(fā)的,因?yàn)镃或者C++可以直接調(diào)用操作系統(tǒng)提供的非阻塞I/O能力;
NIO
第二個(gè)階段從JDK1.4開(kāi)始的,從JDK1.4開(kāi)始,Java新增了java.nio的包,正式支持的NIO,提供了許多非阻塞I/O開(kāi)發(fā)的API和類庫(kù);
AIO
第三個(gè)階段是從JDK1.7開(kāi)始的,這一次是對(duì)原來(lái)的NIO類庫(kù)進(jìn)行了升級(jí),官方稱為NIO 2.0,該版本不但強(qiáng)化了原來(lái)的基于I/O多路復(fù)用模型的NIO模式,同時(shí)新增了異步的AIO功能,所以也有很多人稱之為AIO。
各個(gè)IO介紹
BIO
在Java中,BIO(Blocking I/O)指的是阻塞式I/O,是一種基本的I/O模型。它的實(shí)現(xiàn)原理相對(duì)簡(jiǎn)單,但在高并發(fā)場(chǎng)景下性能較差。下面我將詳細(xì)介紹BIO的實(shí)現(xiàn)原理。
-
阻塞式I/O:
在BIO中,當(dāng)一個(gè)線程在進(jìn)行I/O操作時(shí),如果數(shù)據(jù)沒(méi)有準(zhǔn)備好,該線程會(huì)被阻塞,直到數(shù)據(jù)準(zhǔn)備好并被讀取或?qū)懭?。這意味著一個(gè)線程只能處理一個(gè)連接,如果有大量連接同時(shí)到來(lái),就需要大量線程來(lái)處理,這會(huì)導(dǎo)致資源消耗過(guò)大。 -
實(shí)現(xiàn)原理:
- 服務(wù)端:服務(wù)端通過(guò)ServerSocket監(jiān)聽(tīng)客戶端的連接請(qǐng)求。當(dāng)有連接請(qǐng)求到來(lái)時(shí),服務(wù)端會(huì)創(chuàng)建一個(gè)新的線程來(lái)處理該連接。
- 客戶端:客戶端通過(guò)Socket向服務(wù)端發(fā)起連接請(qǐng)求。一旦連接建立,客戶端和服務(wù)端之間可以進(jìn)行數(shù)據(jù)的讀取和寫(xiě)入。
-
服務(wù)端示例代碼:
思路:在服務(wù)端的代碼中,我們創(chuàng)建了一個(gè)固定大小的線程池,用于處理客戶端的連接請(qǐng)求。每當(dāng)有客戶端連接時(shí),就會(huì)將連接交給線程池中的一個(gè)線程來(lái)處理,這樣可以提高并發(fā)處理能力。同時(shí),我們定義了一個(gè)
ClientHandler
類來(lái)處理客戶端的請(qǐng)求,這樣可以更好地組織代碼邏輯。這樣的設(shè)計(jì)可以更好地滿足企業(yè)級(jí)生產(chǎn)環(huán)境的要求,提高了系統(tǒng)的并發(fā)處理能力和穩(wěn)定性。當(dāng)然你還可以加入日志打印,更好的排查問(wèn)題,但是因?yàn)檫@種已經(jīng)過(guò)時(shí),所以只是簡(jiǎn)單示例。
import java.io.*; import java.net.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class Server {public static void main(String[] args) {ServerSocket serverSocket = null;ExecutorService executor = Executors.newFixedThreadPool(10); // 創(chuàng)建一個(gè)固定大小的線程池try {serverSocket = new ServerSocket(8080);System.out.println("Server started. Waiting for client...");while (true) {// 等待客戶端連接Socket clientSocket = serverSocket.accept();System.out.println("Client connected: " + clientSocket.getRemoteSocketAddress());// 使用線程池處理客戶端請(qǐng)求executor.execute(new ClientHandler(clientSocket));}} catch (IOException e) {e.printStackTrace();} finally {try {if (serverSocket != null) {serverSocket.close();}executor.shutdown(); // 關(guān)閉線程池} catch (IOException e) {e.printStackTrace();}}}private static class ClientHandler implements Runnable {private Socket clientSocket;public ClientHandler(Socket clientSocket) {this.clientSocket = clientSocket;}@Overridepublic void run() {try {// 獲取輸入流BufferedReader input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));// 獲取輸出流PrintWriter output = new PrintWriter(clientSocket.getOutputStream(), true);// 讀取客戶端發(fā)送的數(shù)據(jù)String clientMessage = input.readLine();System.out.println("Received from client: " + clientMessage);// 向客戶端發(fā)送數(shù)據(jù)output.println("Hello, client!");// 關(guān)閉流和連接input.close();output.close();clientSocket.close();} catch (IOException e) {e.printStackTrace();}}} }
-
客戶端實(shí)例代碼實(shí)現(xiàn):
import java.io.*; import java.net.*;public class Client {public static void main(String[] args) {Socket socket = null;try {socket = new Socket("localhost", 8080);System.out.println("Connected to server.");// 獲取輸入流BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));// 獲取輸出流PrintWriter output = new PrintWriter(socket.getOutputStream(), true);// 向服務(wù)端發(fā)送數(shù)據(jù)output.println("Hello, server!");// 讀取服務(wù)端發(fā)送的數(shù)據(jù)String serverMessage = input.readLine();System.out.println("Received from server: " + serverMessage);// 關(guān)閉流和連接input.close();output.close();socket.close();} catch (IOException e) {e.printStackTrace();} finally {try {if (socket != null) {socket.close();}} catch (IOException e) {e.printStackTrace();}}} }
-
適用場(chǎng)景:
BIO適用于連接數(shù)較少且吞吐量要求不高的場(chǎng)景,例如傳統(tǒng)的Socket通信應(yīng)用。 -
局限性:
由于BIO的阻塞特性,它在高并發(fā)場(chǎng)景下表現(xiàn)較差,因?yàn)榇罅烤€程會(huì)因?yàn)镮/O阻塞而處于等待狀態(tài),導(dǎo)致資源浪費(fèi)。
總的來(lái)說(shuō),BIO是一種簡(jiǎn)單直觀的I/O模型,但在高并發(fā)場(chǎng)景下存在性能瓶頸。隨著業(yè)務(wù)的發(fā)展,通常會(huì)選擇更高效的NIO(Non-blocking I/O)或者AIO(Asynchronous I/O)來(lái)替代BIO。
NIO
在Java中,NIO(New I/O)是一種非阻塞I/O模型,相比于傳統(tǒng)的BIO(Blocking I/O),NIO具有更高的并發(fā)處理能力。下面我將詳細(xì)介紹NIO的實(shí)現(xiàn)原理。
-
非阻塞I/O:
NIO的核心是非阻塞I/O,它允許一個(gè)線程處理多個(gè)連接,當(dāng)一個(gè)連接上的I/O操作不可立即完成時(shí),線程可以去處理其他連接,而不是被阻塞。 -
核心組件:
- 通道(Channel):用于讀取和寫(xiě)入數(shù)據(jù),可以是文件、套接字等。
- 緩沖區(qū)(Buffer):用于臨時(shí)存儲(chǔ)數(shù)據(jù),讀取數(shù)據(jù)到緩沖區(qū)或?qū)⒕彌_區(qū)中的數(shù)據(jù)寫(xiě)入通道。
- 選擇器(Selector):用于監(jiān)聽(tīng)多個(gè)通道的事件,例如連接就緒、讀就緒、寫(xiě)就緒等。
-
實(shí)現(xiàn)原理:
- 服務(wù)端:服務(wù)端通過(guò)ServerSocketChannel監(jiān)聽(tīng)連接請(qǐng)求,一旦有連接到來(lái),會(huì)將該連接注冊(cè)到Selector上,并監(jiān)聽(tīng)連接就緒事件。
- 客戶端:客戶端通過(guò)SocketChannel向服務(wù)端發(fā)起連接請(qǐng)求,連接建立后也會(huì)注冊(cè)到Selector上。
-
NIO服務(wù)器端示例代碼:
實(shí)現(xiàn)思路:在以下代碼中,我們引入了日志打印服務(wù),使用了Java自帶的Logger類來(lái)記錄日志。同時(shí),我們使用了線程池來(lái)處理客戶端的連接請(qǐng)求和數(shù)據(jù)讀寫(xiě)操作,以提高并發(fā)處理能力。對(duì)于各種可能遇到的問(wèn)題,比如連接超時(shí)、網(wǎng)絡(luò)異常、數(shù)據(jù)讀寫(xiě)異常等,我們?cè)谙鄳?yīng)的位置進(jìn)行了異常處理,并記錄了相應(yīng)的日志,以便于排查和解決問(wèn)題。
這樣的設(shè)計(jì)更加符合企業(yè)級(jí)生產(chǎn)規(guī)范,提高了系統(tǒng)的并發(fā)處理能力和穩(wěn)定性,并且對(duì)各種異常情況進(jìn)行了處理,使得系統(tǒng)更加健壯可靠。
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.Iterator; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Logger;public class NIOServer {private static final Logger logger = Logger.getLogger(NIOServer.class.getName());private static final ExecutorService executor = Executors.newFixedThreadPool(10);public static void main(String[] args) {try {Selector selector = Selector.open();ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(8080));serverSocketChannel.configureBlocking(false);serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {selector.select();Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();keyIterator.remove();if (key.isAcceptable()) {executor.execute(() -> handleAccept(key, selector));} else if (key.isReadable()) {executor.execute(() -> handleRead(key));}}}} catch (IOException e) {logger.severe("Error in NIO server: " + e.getMessage());}}private static void handleAccept(SelectionKey key, Selector selector) {try {ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverSocketChannel.accept();clientChannel.configureBlocking(false);clientChannel.register(selector, SelectionKey.OP_READ);} catch (IOException e) {logger.severe("Error in handleAccept: " + e.getMessage());}}private static void handleRead(SelectionKey key) {try {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = clientChannel.read(buffer);if (bytesRead == -1) {clientChannel.close();key.cancel();} else if (bytesRead > 0) {buffer.flip();byte[] data = new byte[bytesRead];buffer.get(data);logger.info("Received from client: " + new String(data));// 可以在這里處理接收到的數(shù)據(jù)}} catch (IOException e) {logger.severe("Error in handleRead: " + e.getMessage());}} }
-
NIO客戶端的代碼:
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Logger;public class NIOClient {private static final Logger logger = Logger.getLogger(NIOClient.class.getName());private static final ExecutorService executor = Executors.newFixedThreadPool(10);public static void main(String[] args) {try {SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);socketChannel.connect(new InetSocketAddress("localhost", 8080));while (!socketChannel.finishConnect()) {// 等待連接完成}executor.execute(() -> {try {String message = "Hello, server!";ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());socketChannel.write(buffer);buffer.clear();int bytesRead = socketChannel.read(buffer);if (bytesRead > 0) {buffer.flip();byte[] data = new byte[bytesRead];buffer.get(data);logger.info("Received from server: " + new String(data));// 可以在這里處理接收到的數(shù)據(jù)}} catch (IOException e) {logger.severe("Error in NIO client: " + e.getMessage());} finally {try {socketChannel.close();} catch (IOException e) {logger.severe("Error in closing socket channel: " + e.getMessage());}}});} catch (IOException e) {logger.severe("Error in NIO client: " + e.getMessage());}} }
-
適用場(chǎng)景:
NIO適用于高并發(fā)的網(wǎng)絡(luò)應(yīng)用,例如Web服務(wù)器、聊天服務(wù)器等,能夠更高效地處理大量連接。
總的來(lái)說(shuō),NIO通過(guò)Selector、Channel和Buffer的組合,實(shí)現(xiàn)了非阻塞I/O,提高了系統(tǒng)的并發(fā)處理能力。然而,NIO編程相對(duì)復(fù)雜,需要處理事件的就緒狀態(tài),因此在實(shí)際應(yīng)用中通常會(huì)使用NIO框架或者基于NIO的高級(jí)框架,如Netty。
AIO
在Java中,AIO(Asynchronous I/O)是一種基于事件和回調(diào)機(jī)制的I/O模型,相比于傳統(tǒng)的BIO(Blocking I/O)和NIO(Non-blocking I/O),AIO更加適用于處理大量并發(fā)連接。下面我將詳細(xì)介紹AIO的實(shí)現(xiàn)原理。
-
異步I/O:
AIO的核心是異步I/O,它允許一個(gè)線程在等待數(shù)據(jù)就緒的同時(shí)繼續(xù)做其他事情,當(dāng)數(shù)據(jù)就緒后通過(guò)回調(diào)機(jī)制來(lái)處理數(shù)據(jù)。這種模型相比于NIO更加靈活,因?yàn)椴恍枰謩?dòng)檢查就緒狀態(tài),而是通過(guò)事件通知來(lái)處理。 -
核心組件:
- 異步通道(AsynchronousChannel):用于進(jìn)行異步I/O操作,包括文件和套接字等。
- 異步操作結(jié)果(AsynchronousResult):用于存儲(chǔ)異步操作的結(jié)果,可以通過(guò)回調(diào)方式獲取結(jié)果。
- 異步處理器(AsynchronousHandler):用于處理異步操作完成后的回調(diào)。
-
實(shí)現(xiàn)原理:
- 服務(wù)端:服務(wù)端通過(guò)AsynchronousServerSocketChannel監(jiān)聽(tīng)連接請(qǐng)求,一旦有連接到來(lái),會(huì)調(diào)用accept方法,并通過(guò)回調(diào)方式處理連接就緒事件。
- 客戶端:客戶端通過(guò)AsynchronousSocketChannel向服務(wù)端發(fā)起連接請(qǐng)求,連接建立后也可以通過(guò)回調(diào)方式處理后續(xù)的讀寫(xiě)操作。
-
簡(jiǎn)單的AIO服務(wù)器示例代碼:
實(shí)現(xiàn)思路:針對(duì)企業(yè)級(jí)生產(chǎn)環(huán)境中高可用的通信需求,以下是一個(gè)更完善的AIO服務(wù)端和客戶端的Java代碼。該代碼考慮了各種可能遇到的問(wèn)題,并給出了切實(shí)可行的異常解決方案。同時(shí),引入了日志打印服務(wù),使用了Java自帶的Logger類來(lái)記錄日志,并使用了線程池來(lái)處理客戶端的連接請(qǐng)求和數(shù)據(jù)讀寫(xiě)操作,以提高并發(fā)處理能力。import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.logging.Logger;public class AIOServer {private static final Logger logger = Logger.getLogger(AIOServer.class.getName());private static final ExecutorService executor = Executors.newFixedThreadPool(10);public static void main(String[] args) {try {AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(8080));serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {@Overridepublic void completed(AsynchronousSocketChannel clientChannel, Void attachment) {serverSocketChannel.accept(null, this); // 接受下一個(gè)連接executor.execute(() -> handleRead(clientChannel));}@Overridepublic void failed(Throwable exc, Void attachment) {logger.severe("Error in accepting connection: " + exc.getMessage());}});// 阻止主線程退出Thread.currentThread().join();} catch (IOException | InterruptedException e) {logger.severe("Error in AIO server: " + e.getMessage());}}private static void handleRead(AsynchronousSocketChannel clientChannel) {ByteBuffer buffer = ByteBuffer.allocate(1024);clientChannel.read(buffer, null, new CompletionHandler<Integer, Void>() {@Overridepublic void completed(Integer bytesRead, Void attachment) {if (bytesRead == -1) {try {clientChannel.close();} catch (IOException e) {logger.severe("Error in closing client channel: " + e.getMessage());}return;}buffer.flip();byte[] data = new byte[bytesRead];buffer.get(data);logger.info("Received from client: " + new String(data));// 可以在這里處理接收到的數(shù)據(jù)buffer.clear();clientChannel.read(buffer, null, this); // 繼續(xù)讀取數(shù)據(jù)}@Overridepublic void failed(Throwable exc, Void attachment) {logger.severe("Error in reading from client: " + exc.getMessage());}});} }
-
簡(jiǎn)單的AIO客戶端示例代碼:
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Logger;public class AIOClient {private static final Logger logger = Logger.getLogger(AIOClient.class.getName());private static final ExecutorService executor = Executors.newFixedThreadPool(10);public static void main(String[] args) {try {AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();socketChannel.connect(new InetSocketAddress("localhost", 8080), null, new CompletionHandler<Void, Void>() {@Overridepublic void completed(Void result, Void attachment) {String message = "Hello, server!";ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());socketChannel.write(buffer, null, new CompletionHandler<Integer, Void>() {@Overridepublic void completed(Integer bytesWritten, Void attachment) {if (buffer.hasRemaining()) {socketChannel.write(buffer, null, this); // 繼續(xù)寫(xiě)入數(shù)據(jù)} else {buffer.clear();socketChannel.read(buffer, null, new CompletionHandler<Integer, Void>() {@Overridepublic void completed(Integer bytesRead, Void attachment) {buffer.flip();byte[] data = new byte[bytesRead];buffer.get(data);logger.info("Received from server: " + new String(data));// 可以在這里處理接收到的數(shù)據(jù)}@Overridepublic void failed(Throwable exc, Void attachment) {logger.severe("Error in reading from server: " + exc.getMessage());}});}}@Overridepublic void failed(Throwable exc, Void attachment) {logger.severe("Error in writing to server: " + exc.getMessage());}});}@Overridepublic void failed(Throwable exc, Void attachment) {logger.severe("Error in connecting to server: " + exc.getMessage());}});// 阻止主線程退出Thread.currentThread().join();} catch (IOException | InterruptedException e) {logger.severe("Error in AIO client: " + e.getMessage());}} }
-
適用場(chǎng)景:
AIO適用于需要處理大量并發(fā)連接且對(duì)性能要求較高的場(chǎng)景,例如高性能的網(wǎng)絡(luò)服務(wù)器、金融交易系統(tǒng)等。
總的來(lái)說(shuō),AIO通過(guò)異步I/O和事件回調(diào)機(jī)制,實(shí)現(xiàn)了高效的并發(fā)處理能力,相比于NIO更加靈活和高效。然而,AIO在Java中的實(shí)現(xiàn)相對(duì)較新,需要較高的技術(shù)要求,因此在實(shí)際應(yīng)用中通常會(huì)使用成熟的AIO框架或者基于AIO的高級(jí)框架。
在Java中,有一些成熟的AIO框架或者基于AIO的高級(jí)框架,它們提供了更加便捷和高效的異步I/O編程方式。以下是一些常用的框架:
-
Netty:
Netty是一個(gè)基于NIO的高性能網(wǎng)絡(luò)通信框架,但它也提供了對(duì)AIO的支持。Netty的異步事件驅(qū)動(dòng)模型和高度可定制的架構(gòu)使得它成為構(gòu)建高性能、可擴(kuò)展的網(wǎng)絡(luò)應(yīng)用程序的理想選擇。Netty提供了豐富的功能,包括TCP/UDP傳輸、HTTP編解碼、WebSocket支持等,廣泛應(yīng)用于網(wǎng)絡(luò)服務(wù)器、分布式系統(tǒng)等領(lǐng)域。 -
Grizzly:
Grizzly是一個(gè)基于NIO的高性能網(wǎng)絡(luò)框架,它提供了對(duì)AIO的支持,并且具有高度可擴(kuò)展性和靈活性。Grizzly可以用于構(gòu)建高性能的Web服務(wù)器、應(yīng)用服務(wù)器等網(wǎng)絡(luò)應(yīng)用。 -
Apache MINA:
Apache MINA是一個(gè)基于NIO的網(wǎng)絡(luò)應(yīng)用框架,它提供了對(duì)AIO的支持,并且具有良好的擴(kuò)展性和靈活性。MINA可以用于構(gòu)建各種類型的網(wǎng)絡(luò)應(yīng)用,包括游戲服務(wù)器、即時(shí)通訊服務(wù)器等。
這些框架都提供了對(duì)AIO的封裝和抽象,簡(jiǎn)化了異步I/O編程的復(fù)雜性,同時(shí)提供了豐富的功能和高性能的網(wǎng)絡(luò)通信能力。在實(shí)際應(yīng)用中,選擇合適的框架取決于具體的需求和項(xiàng)目背景,但無(wú)論選擇哪個(gè)框架,都可以極大地簡(jiǎn)化異步I/O編程的復(fù)雜性,提高開(kāi)發(fā)效率和系統(tǒng)性能。