2、连接器Connector

这一篇章,我想介绍一下tomcat容器中的连接器部分,并将tomcat4、tomcat6及tomcat8他们的连接器对比一下,看看做了哪些改进。

1、首先我们再回顾一下模拟tomcat容器这个应用中连接器的代码

public class Connector implements Runnable {

    Container container;

    public Container getContainer() {
        return container;
    }

    public void setContainer(Container container) {
        this.container = container;
    }

    @Override
    public void run() {
        try {
            ServerSocket serverSocket = new ServerSocket(8080,
                    20,
                    InetAddress.getByName("localhost"));

            while (true) {
                Socket socket = serverSocket.accept();
                Request request = new ServletRequest();
                request.setInputStream(socket.getInputStream());
                Response response = new ServletResponse();
                response.setOutputStream(socket.getOutputStream());

                ((ServletRequest) request).parseRequest(request);

                container.invoke(request, response);
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        Thread t = new Thread(this);
        t.start();
    }
}

连接器的全部工作也就是上面这块代码完成的功能,创建

1、服务端套接字

2、监听端口

3、接收客户端连接

4、解析报文

5、调用容器

2、下面我们看看tomcat4连接器怎么处理的

我继续会处理掉一些此阶段不太关注的代码

public void initialize()
    throws LifecycleException {
        // 这个字段是防止该对象重复初始化
        if (initialized)
            throw new LifecycleException (
                sm.getString("httpConnector.alreadyInitialized"));

        this.initialized=true;

        // Establish a server socket on the specified port
        
       serverSocket = open();
}

// open创建一个ServerSocket
private ServerSocket open()
    throws IOException, KeyStoreException, NoSuchAlgorithmException,
           CertificateException, UnrecoverableKeyException,
           KeyManagementException
    {

        // 获取ServerSocket工厂
        ServerSocketFactory factory = getFactory();

        // If no address is specified, open a connection on all addresses
        if (address == null) {
            try {
                return (factory.createSocket(port, acceptCount));
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + port);
            }
        }
    }

可见初始化操作创建了一个ServerSocket套接字

@Override
    public void start() throws LifecycleException {

        // Validate and update our current state
        if (started)
            throw new LifecycleException
                (sm.getString("httpConnector.alreadyStarted"));
        threadName = "HttpConnector[" + port + "]";
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;

        // 创建一个后台线程,connector是一个Runnable实现类,这个函数就是启动了线程
        threadStart();

        // 此处创建了一个Processor对象池,每一个Processor是一个线程,newProcessor的时候顺便也启动了线程,所以我们接下来分别看看两个线程做的什么
        while (curProcessors < minProcessors) {
            if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
                break;
            HttpProcessor processor = newProcessor();
            recycle(processor);
        }

    }

我们来看看HttpConnector类定义,可以发现其实现了一个Runnable接口,threadStart这个函数就是启动了HttpConnector的线程

tomcat4这个时候jdk还没有实现线程池,这里是通过栈实现了一个对象池,对象池里存放的是Processor,newProcessor的时候,顺便启动了线程,接下来我们看看HttpConnector的run和HttpProcessor的run

HttpConnector

public void run() {
        // Loop until we receive a shutdown command
        while (!stopped) {
            // Accept the next incoming connection from the server socket
            Socket socket = null;
            try {
                
                socket = serverSocket.accept();

            // 分配一个空闲的HttpProcessor
            HttpProcessor processor = createProcessor();
           // 给processor分配工作
            processor.assign(socket);


        // Notify the threadStop() method that we have shut ourselves down
        //        if (debug >= 3)
        //            log("run: Notifying threadStop() that we have shut down");
        synchronized (threadSync) {
            threadSync.notifyAll();
        }

    }


synchronized void assign(Socket socket) {

        // Wait for the Processor to get the previous Socket
        while (available) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }

        // Store the newly available Socket and notify our thread
        this.socket = socket;
        available = true;
        notifyAll();
    }

HttpProcessor

public void run() {

        // Process requests until we receive a shutdown signal
        while (!stopped) {

            // Wait for the next socket to be assigned
            Socket socket = await();
            if (socket == null)
                continue;

            // Process the request from this socket
            try {
                process(socket);
            } catch (Throwable t) {
                log("process.invoke", t);
            }

            // Finish up this request
            connector.recycle(this);

        }

        // Tell threadStop() we have shut ourselves down successfully
        synchronized (threadSync) {
            threadSync.notifyAll();
        }

    }

private synchronized Socket await() {

        // Wait for the Connector to provide a new Socket
        while (!available) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }

        // Notify the Connector that we have received this Socket
        Socket socket = this.socket;
        available = false;
        notifyAll();

        return (socket);

    }

 connector接收到一个新的连接,从对象池中获取一个Processor对象,并将socket分配给对象,而Processor,如果没有新的要处理的业务,会阻塞在await等待一个新的socket,这里是典型的等待通知的应用。

Connector接收到新的请求,assign(Socket)将新请求的socket分配给processor,这里用到了synchronized同步处理,且synchronized保证了里面内存区域数据在connector线程与processor之间的可见性。将socket传给了processor之后,将available赋值为true,并通知,这时候processor取消阻塞,获取到新的socket,并调用process。

private void process(Socket socket) {
        boolean ok = true;
        boolean finishResponse = true;
        SocketInputStream input = null;
        OutputStream output = null;

        // Construct and initialize the objects we will need
        try {
            input = new SocketInputStream(socket.getInputStream(),
                                          connector.getBufferSize());
        } catch (Exception e) {
            log("process.create", e);
            ok = false;
        }

        keepAlive = true;

        while (!stopped && ok && keepAlive) {

            finishResponse = true;

            try {
                request.setStream(input);
                request.setResponse(response);
                output = socket.getOutputStream();
                response.setStream(output);
                response.setRequest(request);
                ((HttpServletResponse) response.getResponse()).setHeader
                    ("Server", SERVER_INFO);
            } catch (Exception e) {
                log("process.create", e);
                ok = false;
            }


            // Parse the incoming request
            try {
                if (ok) {

                    parseConnection(socket);
                    parseRequest(input, output);
                    if (!request.getRequest().getProtocol()
                        .startsWith("HTTP/0"))
                        parseHeaders(input);
                    if (http11) {
                        // Sending a request acknowledge back to the client if
                        // requested.
                        ackRequest(output);
                        // If the protocol is HTTP/1.1, chunking is allowed.
                        if (connector.isChunkingAllowed())
                            response.setAllowChunking(true);
                    }

                }
            } 

            // Ask our Container to process this request
            try {
                ((HttpServletResponse) response).setHeader
                    ("Date", FastHttpDateFormat.getCurrentDate());
                if (ok) {
                    connector.getContainer().invoke(request, response);
                }
            } 

            // Finish up the handling of the request
            if (finishResponse) {
                try {
                    response.finishResponse();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    request.finishRequest();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    if (output != null)
                        output.flush();
                } catch (IOException e) {
                    ok = false;
                }
            }

           
            if ( "close".equals(response.getHeader("Connection")) ) {
                keepAlive = false;
            }

            // End of request processing
            status = Constants.PROCESSOR_IDLE;

            // Recycling the request and the response objects
            request.recycle();
            response.recycle();

        }

        try {
            shutdownInput(input);
            socket.close();
        } catch (IOException e) {
            ;
        } catch (Throwable e) {
            log("process.invoke", e);
        }
        socket = null;

    }

process()就是接收数据,解析http请求,调用容器,这里没有做什么特殊设计。

所以我这里推荐大家读源码的话,版本选择tomcat4,这个版本没有过度设计,对于通过读源码深入了解tomcat再合适不过。

3、我们再来看看tomcat6的连接器

这里就不讲解代码了,可以按照这个思路去看下tomcat6的源码,我这里讲解一下tomcat6连接器的变化部分与目的。

这里针对连接的创建与监听,报文的处理及调用容器均做了重构,添加了EndPoint、Processor、Adapter三个模块。

EndPoint:负责与客户端的对接,在系统的设计中,这部分是变化的,tomcat6就有加入NIO非阻塞 I/O 多路复用,tomcat8中还加入了异步IO,

ProtocolHandler:负责协议的解析,Http11Protocol负责的是http协议,

Adapter:通过适配器调用容器,适应不同协议下输出到容器的数据格式是一致的。

上面三个模块的重构封装符合设计中的开闭原则(开放扩展,关闭修改)

后期我会针对tomcat6的连接器做一次详细的描述。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值