为了解决同步阻塞I/O一个链路需要一个线程处理问题,对BIO模型做了优化——后端通过一个线程池处理多个客户端的请求接入,设置线程最大值,防止线程并发接入导致的线程耗尽。
当有新的客户端接入时,将客户端Socket封装成一个Task(该任务实现java.lang.Runnable接口)投递到后端的线程池中进行处理,JDK的线程池维护一个消息队列和N个活跃线程,对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此资源占用是可控的,无论多少客户端并发访问都不会导致资源耗尽和宕机。
伪异步I/O通信框架采用了线程池实现,避免了每次请求创建一个独立的线程造成的资源耗尽问题,底层通信依然采用BIO同步阻塞模型,无法从根本上解决问题。当对方发送请求或者应答消息比较缓慢(如数据大),或者网路传输较慢时(网速差)读取输入流一方的通信线程将被长时间阻塞,阻塞期间,其他接入消息只能在消息队列中排队。阻塞结束情况:1)有数据可读,2)可用数据已经读取完毕,3)发生空指针或IO异常
package com.hjp.netty.pseudoasynio; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class TimeServerHandlerExecutePool { private ExecutorService executor; public TimeServerHandlerExecutePool(int maxPoolSize, int queueSize) { executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), maxPoolSize, 120L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize)); } public void execute(Runnable task) { executor.execute(task); } }
package com.hjp.netty.pseudoasynio; import sun.awt.windows.ThemeReader; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class TimeServer { public static void main(String[] args) throws IOException { int port = 8080; if (args != null && args.length > 0) { try { port = Integer.valueOf(port); } catch (NumberFormatException e) { e.printStackTrace(); } } ServerSocket server = null; try { server = new ServerSocket(port); System.out.println("The time server is start in port : " + port); Socket socket = null; TimeServerHandlerExecutePool singleExecutor = new TimeServerHandlerExecutePool(50, 10000); while (true) { socket = server.accept(); singleExecutor.execute(new TimeServerHandler(socket)); } } finally { if (server != null) { System.out.println("The time server close"); server.close(); server = null; } } } }
package com.hjp.netty.pseudoasynio; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Date; public class TimeServerHandler implements Runnable { private Socket socket; public TimeServerHandler(Socket socket) { this.socket = socket; } @Override public void run() { BufferedReader in = null; PrintWriter out = null; try { in = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); out = new PrintWriter(this.socket.getOutputStream(), true); String currentTime = null; String body = null; while (true) { body = in.readLine(); if (body == null) { break; } System.out.println("The time server receive order : " + body); currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER"; out.println(currentTime); } } catch (Exception e) { if (in != null) { try { in.close(); } catch (IOException e1) { e1.printStackTrace(); } } if (out != null) { out.close(); out = null; } if (this.socket != null) { try { this.socket.close(); } catch (IOException e1) { e1.printStackTrace(); } this.socket = null; } } } }
客户端代码和BIO客户端代码一样