BIO模型:
一个请求创建一个工作线程,每个请求都是同步阻塞的,也就是顺序访问完成之后才会给出响应.
其中收到请求分为几步:1.收到请求,2创建工作线程,3.读取socket请求内容,4.执行业务逻辑,5.写socket响应.
这几步都是顺序执行的,同步调用,阻塞的.意思就是有一个步骤卡顿响应都收不回来.
好处,编码简单,
坏处:一个请求一个线程,线程过多会造成服务器压力过大.
一张图看到Tomcat的BIO
用户请求到达acceptor 线程组, 其中一条线程 (acceptor)接收请求,之后会创建工作线程,封装出request,经过过滤器filter,去执行Servlet业务逻辑.之后返回响应.
涉及源码
1.Accept线程组:
Accept 类 线程组 acceptorThreadCount=== 决定了acceptor有多少个线程,下面列出创建acceptor线程的源码.
这里数量不多,直接用的线程,并且把它设置为守护线程.
2.Acceptor extends Runnable
创建完成acceptor下面就是每个acceptor如何工作的.看源码如下:
执行逻辑:如果达到最大线程数,就加入等待队列进行等待(这里有个调优的参数acceptCount), 最大线程数默认是10000, 下面用工厂方法创建一个socket,本质就是 serverSocket 执行accept( )方法, 如果没有socket就一直阻塞在这句.再下面就是跑异常或者执行完成,减少运行线程计数,之后关闭socket. 真正执行工作线程的代码是 if(!processSocket(socket)) 这一句代码.
3.工作线程 SocketProcessor extends Runnable
封装socket.编程wrapper
先判断是否达到最大时间,到了就强制关闭,
再判断是否打开SSH连接
再进行创建工作线程,new SocketProcessor(wrapper);
maxThread就是指的这个线程数,同时业务逻辑有慢查询或者夯住了,死锁了都是引起这个线程出问题 .
工作线程池就是 getExecutor()获得的线程池.
调优参数
acceptCount === 等待最大队列
address === 绑定客户端特定地址,127.0.0.1
bufferSize === 每个请求的缓冲区大小,缓冲区总大小是bufferSize * maxThreads
compression === 是否启用文档
compressableMimeTypes === text/html,text/xml,text/plain
connectionTimeout === 客户发起链接 到 服务端接收为止,中间最大的等待时间
connectionUploadTimeout === upload 情况下连接超时时间
disableUploadTimeout === true 则使用connectionTimeout
enableLookups === 禁用DNS查询 true
keepAliveTimeout === 当长链接闲置 指定时间主动关闭 链接 ,前提是客户端请 求头 带上这个 head”connection” ” keep-alive”
maxKeepAliveRequests === 最大的 长连接数
maxHttpHeaderSize === 最大请求头大小
maxSpareThreads === BIO 模式下 最多线闲置线程数
maxThreads === 最大执行线程数 默认是10000
minSpareThreads === BIO 模式下 最小线闲置线程数
自己实现简单的BIO
/**
* 客户端
*/
public class Client {
private String host ;
private int port ;
private int timeout = 6000;//毫秒
public Client(String host, int port) {
this.host = host;
this.port = port;
}
public void start(){
//创建socket
Socket socket = new Socket();
BufferedWriter bw ;
BufferedReader br ;
try {
//链接服务器
SocketAddress socketAddress = new InetSocketAddress(host,port);
socket.connect(socketAddress,timeout);
//通过输出流向服务器写入请求
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//通过输入流读取服务器响应
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//通过控制台输入作为请求输入
Scanner scanner = new Scanner(System.in);
//循环不间断接收请求输入
while (true){
System.out.println("等待请求来临");
String request = scanner.nextLine();
//请求发送服务器
bw.write(request);
bw.newLine();//加个换行
bw.flush();//写入服务器
System.out.println("消息已经发送给服务器:"+request);
//接收服务器响应
String response = br.readLine(); //会阻塞在这句代码上
System.out.println("收到服务器响应:"+response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端
package wow.tomcat.bio;
import wow.tomcat.IServer;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server implements IServer {
private static final int PORT = 8080;
private int port;
public Server(int port) {
this.port = port;
}
public Server() {
this.port = PORT;
}
public void start(){
try {
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("服务器启动");
while (true){
//监听
System.out.println("开启监听");
Socket socket = serverSocket.accept();
//接收请求
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String request = br.readLine();
System.out.println("收到请求:"+request);
//servlet
String response = servletWork(request);
// 响应
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(response);
bw.newLine();
bw.flush();
System.out.println("返回响应:"+response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 这是模拟的逻辑
* @param request
* @return
*/
private String servletWork(String request) {
if("时间".equals(request)) {
return "当前时间是:" + System.currentTimeMillis();
}
return "请输入正确命令,输入时间";
}
}
线程版的BIO
package wow.tomcat.bio;
import wow.tomcat.IServer;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
*真实的Tomcat中,服务器还有超时时间,读超时,写超时,线程超时等.
*/
public class BioServer implements IServer {
private ExecutorService executorService ;
//tomcat源码中就是并发10000
private static final int maxThread = 10000;
private int threadNum ;
private int port ;
private static final int PORT =8080 ;
public BioServer(int port,int threadNum) {
this.port = port;
this.executorService = Executors.newFixedThreadPool(threadNum);
}
public BioServer(){
this.port = PORT;
this.executorService = Executors.newFixedThreadPool(maxThread);
}
public void start(){
try {
System.out.println("服务器启动");
ServerSocket serverSocket = new ServerSocket(port);
while (true){
Socket socket = serverSocket.accept();
executorService.submit(new WorkThread(socket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
private class WorkThread implements Runnable{
private Socket socket;
public WorkThread(Socket socket) {
this.socket = socket;
}
public void run() {
try {
//接收请求
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String request = br.readLine();
System.out.println("收到请求:"+request);
//servlet
String response = servletWork(request);
// 响应
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(response);
bw.newLine();
bw.flush();
System.out.println("返回响应:"+response);
}catch (IOException e){
e.printStackTrace();
}finally {
}
}
/**
* 这是模拟的逻辑
* @param request
* @return
*/
private String servletWork(String request) {
if("时间".equals(request)) {
return "当前时间是:" + System.currentTimeMillis();
}
return "请输入正确命令,输入时间";
}
}
}