java Bio Demo

刚开始学的时候我们看到的就是Bio模式的socket、serverSocket 这也是java最早提供的,其实早期的tomcat对请求的处理方式也是这种多线程模式的,下面看看代码……


一、客户端

package com.test;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.Charset;

public class Client {
	private static Socket socket;
	private static InputStream inputStream;
	private static OutputStream outputStream;

	public Client() {
	}

	public static void main(String[] args) throws IOException, InterruptedException {
		long begin = System.currentTimeMillis();
		for (int i = 0; i < 500; i++) {
			socket = new Socket("192.168.0.103", 3608);
			outputStream = socket.getOutputStream();
			String str = "client -" + i;
			Charset charset = Charset.forName("utf-8");
			byte[] buffer = str.getBytes(charset);
			outputStream.write(buffer);
			outputStream.flush();
			inputStream = socket.getInputStream();
			byte[] fromServer = new byte[1024];
			int ren = inputStream.read(fromServer);
			int len = fromServer.length;
			String stssr = new String(fromServer, "utf-8");
			System.out.println(" ren:" + ren + " len:" + len + " stssr:" + stssr);
			outputStream.close();
			inputStream.close();
			inputStream = null;
			outputStream = null;
			socket.close();
			socket = null;
			fromServer = null;
		}
		long end = System.currentTimeMillis();
		long total = end - begin;
		System.out.println("Client-total:" + total);
	}

}

二、服务端


package server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Servers {

	private static ServerSocket serverSocket;
	private static Socket socket;
	private static ExecutorService executeService = Executors.newCachedThreadPool();

	public Servers() {
	}

	static {
		System.out.println("server begin……");
		try {
			serverSocket = new ServerSocket(3608); // 等待客户端连接
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws IOException, InterruptedException {
		while (true) {
			socket = serverSocket.accept(); // 这就是bio同步阻塞
			Handler handler = new Handler(socket);// 创建一个任务
			executeService.execute(handler);// 任务交给线程池
		}
	}

}


三、服务端处理器


package server;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;

public class Handler implements Runnable {
	private Socket socket;
	private InputStream inputStream;
	private OutputStream outputStream;

	public Handler() {
	}

	public Handler(Socket socket) {
		this.socket = socket;
	}

	@Override
	public void run() {
		try {
			inputStream = socket.getInputStream();
			outputStream = socket.getOutputStream();
			byte[] buffer = new byte[1024];
			int result = inputStream.read(buffer);
			String str = new String(buffer, "utf-8");
			System.err.println("from client info:" + str + "thread:" + Thread.currentThread().getId());
			String server = new String("from server:" + str);
			Charset cs = Charset.forName("utf-8");
			TimeUnit.MILLISECONDS.sleep(100);
			byte[] bytes = server.getBytes(cs);
			outputStream.write(bytes);
			outputStream.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				outputStream.close();
				inputStream.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

	}

}

ok 这就是javaBio的伪异步实现方式,在请求并发量一般的情况下其实这种方式是能够应付的,其实早期的tomcat的请求处理也是这种思想方式所以在一般的并发量下这种方式还是能应付的。

虽然这种方式能应付一般请求而且编写简单,但是在并发量大的请求下就有点吃力了,下面来分析分析这种方式主要的瓶颈。


在分析瓶颈时我们就使用非伪异步的socket、serverSocket 来说事,这样感觉更容易说明……

1、当我们创建一个socket的时候其实是会先进行服务器、端口的查找以及socket客户端和serverSocket服务端之间tcp的链接,我们知道tcp的链接是要三次握手的,如果客户端socket获得了和服务端serverSocket的联系即三次握手成功。这样客户端和服务端就建立了tcp链接,当我们通过这个链接从客户端发送请求到服务端时,服务端不一定能立即获得客户端的请求,服务端获得客户端的请求时serverSocket.accept()方法,但是我们知道,服务端每当获得一个客户端请求后,就会去处理这个客户端的请求,所以在这时如果有其他客户端的请求,则服务端没法处理,因为上一个客户端的请求还在处理中,所以客户端来的请求就会先缓存在服务端的队列中,当上一个请求处理完之后再来处理服务端队列中的请求,但问题是客户端发送请求后一直在同步等待服务端的相应,如果服务端处理的比较吗,则客户端就会出现超时异常。

2、客户端请求处理是在一个线程中,如果某个请求处理出现了异常情况则后面的请求基本上都是超时等异常。

总的来说,在java的bio中客户端要一个个等待来执行,因为请求的接收在accept()方法,他是阻塞的所以请求的接收很慢,效率很低……

3、请求处理效率低的问题解决就是处理过程使用多线程,但他的消息返回还是同步的,还是对accept()的接收速度有影响。

4、其实当客户端请求量大的时候,如果tcp链接成功而服务端的任务等待队列满了则有可能会爆出队列溢栈威胁

5、在请求的时候tcp服务端可以设置最多的请求参数,如果请求数大于这个数则直接返回给客户端,从而减少服务端压力,

6、如果很多客户端tcp链接处理要很长世间,在所有客户端请求处理完之前tcp是不会关闭的,这样很快就能达到tcp请求上限,之后的请求会直接返回,也就是限流。

7、客户端请求被限流返回的前提条件是客户端和服务端还没建立tcp链接即还没三次握手成功,如果三次握手成功则客户端的请求还是会给执行的,只是有可能会慢一些……


总结:总的来说java Bio主要的缺陷就是accept的阻塞同步,多个客户端请求线程的管理,以及释放线程资源的处理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章