在实际的工作开发中,传统的模型有client/service模型。client端和service端要进行通信的话,有一种套接字的方式。传统的socket编程,包含一个socket服务端和一到多个socket客户端。在连接过程中,sokcetServer 绑定一个端口进行监听。client端通过ip地址和端口号对服务进行访问。在服务进入到service端后,service端新建一个线程。线程对请求进行相应的处理,处理完毕后,将处理结果返回给client端。在处理过程中,连接client阻塞。
代码实现:
server端代码:
Server 类
ServerHandler类,业务处理类
client端代码:
Client类
优点:传统的BIO模型进行通信,代码可读性比较强,理解容易。运行中,server端通过主线程监听accpet()方法阻塞式获取server的消息。
缺点:该模型最大的问题是,每一个client端访问都对应一个后台的线程,使得系统不具备弹性伸缩能力。一旦client请求过多,线程数会迅速膨胀,系统性能会急剧下降,导致堆栈溢出,进程宕机等问题的发生。
为了改进线程数过多,导致系统宕机的问题,同时也为了解决创建线程的消耗问题。后来又演进出了一种通过线程池或者消息队列实现1个或者多个线程处理N个客户端的模型,由于它的底层通信机制依然使用同步阻塞IO,所以被称为 “伪异步”。其原理就是在原来的service上新增一个线程池,将处理业务的线程通过线程池进行管理。如果线程数量到达上限时,将请求先存入queue中进行缓冲。具体代码如下:
client端代码不变。
server端:
1 import java.io.BufferedReader; 2 import java.io.PrintWriter; 3 import java.net.ServerSocket; 4 import java.net.Socket; 5 6 public class Server { 7 8 final static int PORT = 8765; 9 10 public static void main(String[] args) { 11 ServerSocket server = null; 12 BufferedReader in = null; 13 PrintWriter out = null; 14 try { 15 server = new ServerSocket(PORT); 16 System.out.println("server start"); 17 Socket socket = null; 18 HandlerExecutorPool executorPool = new HandlerExecutorPool(50, 50); 19 while(true){ 20 socket = server.accept(); 21 22 executorPool.execute(new ServerHandler(socket)); 23 24 } 25 26 } catch (Exception e) { 27 e.printStackTrace(); 28 } finally { 29 if(in != null){ 30 try { 31 in.close(); 32 } catch (Exception e1) { 33 e1.printStackTrace(); 34 } 35 } 36 if(out != null){ 37 try { 38 out.close(); 39 } catch (Exception e2) { 40 e2.printStackTrace(); 41 } 42 } 43 if(server != null){ 44 try { 45 server.close(); 46 } catch (Exception e3) { 47 e3.printStackTrace(); 48 } 49 } 50 server = null; 51 } 52 53 54 55 } 56 57 58 }
1 import java.util.concurrent.ArrayBlockingQueue; 2 import java.util.concurrent.ThreadPoolExecutor; 3 import java.util.concurrent.TimeUnit; 4 5 6 public class HandlerExecutorPool { 7 private ArrayBlockingQueue queue ; 8 private ThreadPoolExecutor executor; 9 public HandlerExecutorPool(int maxPoolSize, int queueSize){ 10 queue = new ArrayBlockingQueue<Runnable>(queueSize); 11 this.executor = new ThreadPoolExecutor( 12 5, 13 maxPoolSize, 14 120L, 15 TimeUnit.SECONDS, 16 queue); 17 } 18 19 public void execute(Runnable task){ 20 System.out.println("corePoolSize== "+executor.getCorePoolSize()); 21 System.out.println("queuesize=="+queue.size()); 22 this.executor.execute(task); 23 24 System.out.println("当前运行线程== "+executor.getLargestPoolSize()); 25 System.out.println("queuesize=="+queue.size()); 26 } 27 28 29 30 }
1 import java.io.BufferedReader; 2 import java.io.InputStreamReader; 3 import java.io.PrintWriter; 4 import java.net.Socket; 5 import java.util.concurrent.TimeUnit; 6 7 public class ServerHandler implements Runnable { 8 9 private Socket socket; 10 public ServerHandler (Socket socket){ 11 this.socket = socket; 12 } 13 14 @Override 15 public void run() { 16 BufferedReader in = null; 17 PrintWriter out = null; 18 try { 19 Thread.currentThread().sleep(1000); 20 in = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); 21 out = new PrintWriter(this.socket.getOutputStream(), true); 22 String body = null; 23 while(true){ 24 body = in.readLine(); 25 if(body == null) break; 26 System.out.println("Server:" + body); 27 out.println("Server response"); 28 } 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } finally { 32 if(in != null){ 33 try { 34 in.close(); 35 } catch (Exception e1) { 36 e1.printStackTrace(); 37 } 38 } 39 if(out != null){ 40 try { 41 out.close(); 42 } catch (Exception e2) { 43 e2.printStackTrace(); 44 } 45 } 46 if(socket != null){ 47 try { 48 socket.close(); 49 } catch (Exception e3) { 50 e3.printStackTrace(); 51 } 52 } 53 socket = null; 54 } 55 56 57 } 58 59 }
这样改造后,有效的缓解了server端线程数过多时宕机的问题,但是依然没有解决阻塞的问题。依然会出现请求过多时,前台等待超时的问题。