在前文中 讲了一下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端结果如下:
运行client端结果如下:
到此,一个典型的BIO时间服务器就完成了. Tomcat默认处理Reqeust就是这样的套路.
PS:今天,有个同事问我最初为什么做这一行,我说为了游戏,又问我为什么又没做,我只能"呵呵",现在大部分游戏服务是C++,还有一部分是兴起的Golang,还有一部分就是Java体系的Netty或者MINA等,这是学习的原因之一.在之后的博客中我会学习Netty,大家一起进步.谢谢大家的阅读.