天天看点

Tomcat线程模型 BIO模型源码与调优

BIO模型:

一个请求创建一个工作线程,每个请求都是同步阻塞的,也就是顺序访问完成之后才会给出响应.

其中收到请求分为几步:1.收到请求,2创建工作线程,3.读取socket请求内容,4.执行业务逻辑,5.写socket响应.

这几步都是顺序执行的,同步调用,阻塞的.意思就是有一个步骤卡顿响应都收不回来.

好处,编码简单,

坏处:一个请求一个线程,线程过多会造成服务器压力过大.

一张图看到Tomcat的BIO

Tomcat线程模型 BIO模型源码与调优

用户请求到达acceptor 线程组, 其中一条线程 (acceptor)接收请求,之后会创建工作线程,封装出request,经过过滤器filter,去执行Servlet业务逻辑.之后返回响应.

涉及源码

1.Accept线程组:

Accept 类 线程组 acceptorThreadCount=== 决定了acceptor有多少个线程,下面列出创建acceptor线程的源码.

Tomcat线程模型 BIO模型源码与调优

这里数量不多,直接用的线程,并且把它设置为守护线程.

2.Acceptor extends Runnable

创建完成acceptor下面就是每个acceptor如何工作的.看源码如下:

Tomcat线程模型 BIO模型源码与调优

执行逻辑:如果达到最大线程数,就加入等待队列进行等待(这里有个调优的参数acceptCount), 最大线程数默认是10000, 下面用工厂方法创建一个socket,本质就是 serverSocket 执行accept( )方法, 如果没有socket就一直阻塞在这句.再下面就是跑异常或者执行完成,减少运行线程计数,之后关闭socket. 真正执行工作线程的代码是 if(!processSocket(socket)) 这一句代码.

3.工作线程 SocketProcessor extends Runnable

Tomcat线程模型 BIO模型源码与调优

封装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 = ;//毫秒

    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 = ;
    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 = ;
    private int threadNum ;
    private int port ;
    private static final int PORT = ;

    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 "请输入正确命令,输入时间";
        }
    }
}