在前文中 讲了一下Tomcat默认IO,以及IO的优化.

在接下来的博客中,我将学习Netty(同样的框架还有MINA等)

Netty 是一个基于NIO的客户,服务器端编程框架.学习NIO前得好好理解下BIO.

Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

BIO:Blocking IO,同步阻塞线程.

关键点

采用BIO通信模型的服务端,接收客户端的请求之后为每一个客户端创建一个新的线程进行处理,处理完成后,通过输出流返回,线程销毁. 最大的问题在于,当用户并发访问量增加后,服务端的线程个数与客户端的访问数成1:1正相关. 线程是JVM非常宝贵的资源,当线程膨胀后,系统性能急剧下降,随着并发访问量继续增大,系统会发生线程堆栈溢出,创建新线程失败的问题,并最终导致服务器"挂"掉.

所以,在高并发的服务或者网络通信中BIO对于NIO是相对劣势的.

本文通过一个小例子,模拟Java容器在接收请求时是如何处理请求,来理解Tomcat默认的BIO模式的工作方式.

创建一个时间服务器为例子 话不多说上代码.

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 
 * [@author]() 余俊材 2016.2.24
 *
 */
public class TimeServer {

	public static void main(String[] args) throws IOException {
		ServerSocket server = null;
		try {
			int port = 8080;
			server = new ServerSocket(port);
			System.out.println("开始监听" + port + "端口~~~~~~~~~~~");
			Socket socket = null;
			while (true) {
				socket = server.accept();
				new Thread(new TimeServerHander(socket)).start();
			}
		} finally {
			if (server != null) {
				System.out.println("关闭TimeServer");
				server.close();
				server = null;

			}
		}

	}

}

看到 new Thread(new TimeServerHander(socket)).start(); 大家都懂了么...嘿嘿

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * 
 * [@author]() 余俊材 2016.2.24
 *
 */
public class TimeServerHander implements Runnable {

	private Socket socket;

	public TimeServerHander(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("接收到body为" + body);
				if ("time".equals(body)) {
					currentTime = "现在时间" + System.currentTimeMillis();
				} else {
					currentTime = "呵呵";
				}
				System.out.println("向客户端返回" + currentTime);
				out.println(currentTime);
			}

		} catch (Exception e) {
			if (in != null) {
				try {
					in.close();
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			}
			if (out != null) {
				out.close();
				out = null;
			}
			if (this.socket != null) {
				try {
					this.socket.close();
				} catch (Exception e2) {
					e2.printStackTrace();
				}
				this.socket=null;
			}
		}
	}
}

客户端代码如下:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * 
 * [@author]() 余俊材 2016.2.24
 *
 */
public class TimeClient {

	public static void main(String[] args) throws InterruptedException {

		while (true) {
			Thread.sleep(1000L);
			send();
		}
	}

	static void send() {
		int port = 8080;
		Socket socket = null;
		BufferedReader in = null;
		PrintWriter out = null;
		try {
			socket = new Socket("127.0.0.1", port);
			in = new BufferedReader(new InputStreamReader(
					socket.getInputStream()));
			out = new PrintWriter(socket.getOutputStream(), true);
			out.println("time");
			System.out.println("发送Time");
			String resp = in.readLine();
			System.out.println("接收时间" + resp);
		} catch (Exception e) {
			if (in != null) {
				try {
					in.close();
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			}
			if (out != null) {
				out.close();
				out = null;
			}
			if (socket != null) {
				try {
					socket.close();
				} catch (Exception e2) {
					e2.printStackTrace();
				}
				socket = null;
			}
		}
	}
}

运行server端结果如下:

java看线程阻塞的命令_netty

运行client端结果如下:

java看线程阻塞的命令_java_02

到此,一个典型的BIO时间服务器就完成了. Tomcat默认处理Reqeust就是这样的套路.

PS:今天,有个同事问我最初为什么做这一行,我说为了游戏,又问我为什么又没做,我只能"呵呵",现在大部分游戏服务是C++,还有一部分是兴起的Golang,还有一部分就是Java体系的Netty或者MINA等,这是学习的原因之一.在之后的博客中我会学习Netty,大家一起进步.谢谢大家的阅读.