crazyjohn / crazyjohn.github.io

crazyjohn's blog
9 stars 3 forks source link

Sun HttpServer分析 #16

Open crazyjohn opened 9 years ago

crazyjohn commented 9 years ago

这篇分析一下sun的jdk自带的http服务器,HttpServer。

1. 掀起头帘:api

首先来看看如何使用它,看码:

package com.stone.core.http;

import java.io.IOException;
import java.net.InetSocketAddress;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

/**
 * Test sun's http server;
 * 
 * @author crazyjohn
 *
 */
public class HttpServerApi {

    public static void main(String[] args) throws IOException {
        // create server
        HttpServer server = HttpServer.create(new InetSocketAddress("localhost", 8888), 100);
        // create context
        server.createContext("/test", new HttpHandler() {
            @Override
            public void handle(HttpExchange exchange) throws IOException {
                String body = "This is sun's htttp server.";
                response(exchange, body.getBytes("UTF-8"));
                System.out.println("Test handler.");
            }
        });
        // start server
        server.start();
    }

    /**
     * Do response;
     * 
     * @param exchange
     * @param datas
     * @throws IOException
     */
    protected static void response(HttpExchange exchange, byte[] datas) throws IOException {
        exchange.sendResponseHeaders(200, datas.length);
        exchange.getResponseBody().write(datas);
        exchange.getResponseBody().close();
    }

}

启动以后,进程进入工作状态。在浏览器中输入:http://localhost:8888/test然后就会看到网页端的输出:

这样它的工作就奏效了。

2. 仔细打量:结构分析

看结构图:

  1. HttpServer模块。它本身对外的接口很少。

    • create方法。这是一个静态的工厂方法create用来创建HttpServer本身。
    • createContext方法。用来创建上下文对象HttpContext,其实是把指定的路径path和业务处理器HttpHandler关联起来。
    • start,stop方法。这都是生命周期相关的方法。

      它内部正真在工作的其实是一个ServerImpl对象,外部对httpServer接口的调用都会委托给ServerImpl来实现,所以从这个意义上来说,HttpServer只是一个facade门面,用来封装出一组容易操作的接口。

  2. ServerImpl。上头说到的HttpServer的几乎所有工作都会委托给这个类来进行,HttpServer聚合一个ServerImpl。它内部有两个核心组件,一个是selector,用来通过select方法来收集需要进行read和write的连接。其二是dispatcher,这是一个Runnable,它封装了ServerImpl的核心工作流。

  3. HttpExchange。这个对象是对http请求的封装,可以通过getRequestURI接口来获取URI对象,进而进一步获取path和query查询信息。通过getResponseBody接口获取输出流,进行响应内容的设置等。

  4. HttpHandler。http请求的处理器接口。只有一个接口handle(request:HttpExchange),具体的业务请求处理器可以实现此接口,然后再通过createContext(path:String, handler:HttpHandler):HttpContext来实现注册,为以后请求分发做基础准备。
  5. HttpContext。通过HttpServer的public abstract HttpContext createContext(String paramString, HttpHandler paramHttpHandler);接口创建。是个很简单的上下文类,接口也很简单,可以从其中获取路径path和Handler。

  6. Filter。过滤器模式,没有多看。

    3. 仔细打量:时序分析

这里说的是httpserver的核心工作流程,看这里就很了然了。

4. 线程结构

研究下看看httpserver的线程结构。

  1. HttpServer内部,真正的工作会委托给ServerImpl来进行。然后ServerImpl内部聚合一个Executor线程池,这个通过HttpServer对外暴露setExecutor来进行设置。
  2. Dispatcher。这个是ServerImpl内部的一个核心组件,它实现了Runnable,然后ServerImpl内部会new一个线程来作为Dispatcher的载体,Dispatcher的run也是Server内部的核心逻辑。
  3. handleEvent(event)。这是Dispatcher内部处理网络事件的接口,其中Event对象内部聚合一个ExchangeImpl,其实就是一个http请求的封装。在处理请求的时候会使用步骤1说到的线程池executor来执行。
  4. 总结来说,可供外部应用设置的Executor线程池保证的是业务的并发性,但是并发的安全性需要coder自己去控制。