手写web server: 11- tomcat的Connector

43 阅读3分钟

1、Connector的构成:

Connecttor内部有两大组件:

组件作用
ProtocolHandler:完成网络通信 && 按照通信协议解析字节流
Adapter将Request转换成HttpServletRequest(Response同样),并调用Container。封装影响的Response
image.png

ProtocolHandler 又分为两个组件:

组件功能
Endpoint主要解决网络通信
Processor解析协议

2、BIO模式下的类图(tomcat7)

Tomcat的Connector.drawio.png

3、Connector整体运行过程:

3.1 JioEndPoint在开始启动过程中,调用startInternal()时 创建Acceptor[]数组,每个acceptor都开启一个专门的线程,用来接收请求。Acceptorrun()方法,用来执行 socket.accept()。接收客户端的请求。

    protected class Acceptor extends AbstractEndpoint.Acceptor {
        @Override
        public void run() {
            while (running) {
                //if we have reached max connections, wait
                // 达到了最大连接数限制则等待
                //countUpOrAwaitConnection();
                Socket socket = null;
                try {
                    logger.info(" await socket 开始等待客户端socket链接!!! .....");
                    socket = serverSocketFactory.acceptSocket(serverSocket);
                    logger.info("接收到一个socket请求!");
                } catch (IOException ioe) {

                }
                // accept之后,开始处理请求
                if (!processSocket(socket)) {
                    //countDownConnection();
                    // Close socket right away
                    closeSocket(socket);
                }
            }
            state = AcceptorState.ENDED;
        }
    }

3.2 Acceptor接收到客户端的请求后,于是开始处理请求 processSocket(socket),将socket放入包装类socketWrapper中,创建SocketProcessor对象。放入线程池中执行。

    protected boolean processSocket(Socket socket) {
        try {
            logger.info("processSocket " + socket.getInetAddress());
            if (!running) {
                return false;
            }
            //包装
            SocketWrapper<Socket> socketWrapper = new SocketWrapper<>(socket);
            socketWrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
            //bio,一个socket连接对应一个线程
            getExecutor().execute(new SocketProcessor(socketWrapper));
        } catch (Exception e) {
            logger.error("JioEndpoint processSocket failed . msg ={}", e.getMessage(), e);
            return false;
        }
        return true;
    }

3.3 SocketProcessor的执行,核心是调用Processor来解析解析。

        @Override
        public void run() {
            synchronized (socket) {
                //处理SSL(证书验证,todo)
                Handler.SocketState state = Handler.SocketState.OPEN;

                // 当前socket没有关闭则处理socket
                if ((state != Handler.SocketState.CLOSED)) {
                    logger.info(" handler process socket");
                    // SocketState是Tomcat定义的一个状态,这个状态需要处理一下socket才能确定,因为跟客户端,跟具体的请求信息有关系
                    if (status == null) {
                        state = handler.process(socket, SocketStatus.OPEN_READ);
                    } else {
                        // status表示应该读数据还是应该写数据
                        // state表示处理完socket后socket的状态
                        state = handler.process(socket, status);
                    }
                }
            }
            socket = null;
        }

3.4 ProtocolHandler主要是 创建具体协议对应的Processor来解析协议。

        public SocketState process(SocketWrapper<S> wrapper, SocketStatus status) {
            if (wrapper == null) {
                return SocketState.CLOSED;
            }

            S socket = wrapper.getSocket();
            if (socket == null) {
                return SocketState.CLOSED;
            }

            // 设置为非异步,就是同步
            wrapper.setAsync(false);
            Processor<S> processor = connections.get(socket);
            SocketState state = SocketState.CLOSED;
            try {
                if (processor == null) {
                    processor = createProcessor();
                }
                //真正处理请求的地方
                state = processor.process(wrapper);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return state;
        }
    

3.5 解析协议的过程,将socket中读出的字节流,按照对应的协议(eg:http1.1) 来进行解析。

   @Override
    public AbstractEndpoint.Handler.SocketState process(SocketWrapper<S> socketWrapper) throws Exception {

        // setting up the I/O
        setSocketWrapper(socketWrapper);
        // 获取了socket中的inputStream
        getInputBuffer().init(socketWrapper, endpoint);
        getOutputBuffer().init(socketWrapper, endpoint);


        // Flags
        keepAlive = true;

        // 如果当前活跃的线程数占线程池最大线程数的比例大于75%,那么关闭keepAlive,不再支持长连接
//        if (disableKeepAlive()) {
//            socketWrapper.setKeepAliveLeft(0);
//        }

        // 循环响应结果
        // 解析请求
        if (!getInputBuffer().parseRequestLine(keptAlive)) {

        }

       ···
       解析http请求

       ···

        // Process the Protocol component of the request line
        // Need to know if this is an HTTP 0.9 request before trying to
        // parse headers.
        prepareRequestProtocol();

        // process the request in the adapter
        adapter.service(request, response);
        return AbstractEndpoint.Handler.SocketState.CLOSED;
    }

3.6 调用adater,封装HttpServletRequest和HttpServletResponse对象,并调用Container。

    @Override
    public void service(com.jerry.catalina.coyote.Request req, com.jerry.catalina.coyote.Response res) throws Exception {
        System.out.println("CoyoteAdapter.service 开始处理请求");

        // 生成HttpServletRequest对象
        Request request = (Request) req.getNote(ADAPTER_NOTES);
        Response response = (Response) res.getNote(ADAPTER_NOTES);
        if (request == null) {
        } else {
        }
        boolean postParseSuccess = false;

        // 转换请求对象,parse成功后,开始调用Container
        postParseSuccess = postParseRequest(req, request, res, response);
        if (postParseSuccess) {
            // Calling the container
            // todo 待完成相应的部分
//                connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
            System.out.println("待完成 pipeline");

            ByteChunk outputChunk = new ByteChunk();
            String info = respInfo();
            outputChunk.append(info.getBytes(), 0, info.length());

            res.getOutputBuffer().doWrite(outputChunk, res);
        }
    }