在写这篇博客之前我查了很久发现全网都没有一篇写httpserver源码解析的
所以今天就由我来为大家解析一下httpserver的源码。(这里我会去掉其中的https部分的源码,只讲http部分,对httpserver中https的实现感兴趣的读者可以尝试自己去阅读,这部分并不复杂)
第一次在没有参考资料的情况下写这么长一篇源码解析,可能会有很多错误和讲不清楚的地方,希望大家尽量指出来。
大家最好先跟着我构建这样一个小demo,跑起来之后再一步一步去看源码
/**
* @author 肥宅快乐码
*/
public class HttpServerSample {
private static void serverStart() throws IOException {
HttpServerProvider provider = HttpServerProvider.provider();
// 监听端口8080,连接排队队列,如果队列中的连接超过这个数的话就会拒绝连接
HttpServer httpserver =provider.createHttpServer(new InetSocketAddress(8080), 100);
// 监听路径为RestSample,请求处理后回调RestGetHandler里的handle方法
httpserver.createContext("/RestSample", new RestGetHandler());
// 管理工作线程池
ExecutorService executor = new ThreadPoolExecutor(10,200,60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
httpserver.setExecutor(executor);
httpserver.start();
System.out.println("server started");
}
public static void main(String[] args) throws IOException {
serverStart();
}
}
/**
* 回调类,里面的handle方法主要完成将包装好的请求头返回给客户端的功能
*/
class RestGetHandler implements HttpHandler {
@Override
public void handle(HttpExchange he) throws IOException {
String requestMethod = he.getRequestMethod();
// 如果是get方法
if ("GET".equalsIgnoreCase(requestMethod)) {
// 获取响应头,接下来我们来设置响应头信息
Headers responseHeaders = he.getResponseHeaders();
// 以json形式返回,其他还有text/html等等
responseHeaders.set("Content-Type", "application/json");
// 设置响应码200和响应body长度,这里我们设0,没有响应体
he.sendResponseHeaders(200, 0);
// 获取响应体
OutputStream responseBody = he.getResponseBody();
// 获取请求头并打印
Headers requestHeaders = he.getRequestHeaders();
Set<String> keySet = requestHeaders.keySet();
Iterator<String> iter = keySet.iterator();
while (iter.hasNext()) {
String key = iter.next();
List values = requestHeaders.get(key);
String s = key + " = " + values.toString() + "\r\n";
responseBody.write(s.getBytes());
}
// 关闭输出流
responseBody.close();
}
}
}
① 最开始我们通过 HttpServerProvider provider = HttpServerProvider.provider(); 创建了一个HttpServerProvider,也就是这里的DefaultHttpServerProvide
// HttpServerProvider.java
public static HttpServerProvider provider () {
// 这里我们删掉了其他部分,只留下172、173两行
// 这里创建了一个DefaultHttpServerProvide
provider = new sun.net.httpserver.DefaultHttpServerProvider();
return provider;
}
② 之后我们调用 HttpServer httpserver =provider.createHttpServer(new InetSocketAddress(8080), 100); ,
也就是调用了DefaultHttpServerProvider的createHttpServer创建一个HttpServerImpl,当然这里也可以用createHttpsServer创建一个HttpsServerImpl,但是前面说了我们这篇不分析https,所以这里忽略了createHttpsServer方法
还有这里创建ServerImpl的构造方法我们暂时不讲,留到后面再讲
// DefaultHttpServerProvider.java
public HttpServer createHttpServer (InetSocketAddress addr, int backlog) throws IOException {
return new HttpServerImpl (addr, backlog);
}
// HttpServerImpl.java
HttpServerImpl (
InetSocketAddress addr, int backlog
) throws IOException {
server = new ServerImpl (this, "http", addr, backlog);
}
③ 接下来我们创建了一个监听路径 httpserver.createContext("/RestSample", new RestGetHandler());
// HttpServer.java
public abstract HttpContext createContext (String path, HttpHandler handler) ;
// HttpContextImpl.java
public HttpContextImpl createContext (String path, HttpHandler handler) {
// 这里调用的server是ServerImpl类的对象
return server.createContext (path, handler);
}
这里成功返回了一个HttpContextImpl对象,这个我们后面会说,这里我们要知道的是,HttpServerImpl调用的是ServerImpl的实现
到这里我们差不多可以聊一下httpserver的主要结构了:
HttpServer是这里的祖先类,它是一个抽象类,抽象了一个HttpServer应该有的方法
而HttpsServer和我们想象的不一样,它和HttpServer不是平行关系,而是HttpServer的子类,它在HttpServer的基础上加了setHttpsConfigurator和getHttpsConfigurator这两个方法而已
HttpServerImpl和HttpsServerImpl虽然都是实现类,但是它们的方法都是调用ServerImpl的方法,都是围绕ServerImpl的
所以我们也可以把ServerImpl看做这个项目的核心类
④ 之后设置一下工作线程池,初始化任务就完成了
ExecutorService executor = new ThreadPoolExecutor(10,200,60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
httpserver.setExecutor(executor);
httpserver.start();
启动自然和我们刚刚聊的结构一样都是从HttpServer开始一层调一层调用到ServerImpl的方法的:
// HttpServer.java
public abstract void start () ;
// HttpServerImpl.java
public void start () {
server.start();
}
// ServerImpl.java
public void start () {
// server未绑定端口或处于已启动或已关闭状态
// 顺便先提一下,这里就可以留意到,ServerImpl作为一个核心类,管理了各种各样的状态(state)等
if (!bound || started || finished) {
throw new IllegalStateException ("server in wrong state");
}
// 如果没有设置线程池,那就用默认的,默认的话等于没有用线程池,是直接execute的,所以尽可能直接创建线程池
if (executor == null) {
executor = new DefaultExecutor();
}
// 创建了一个Dispatcher线程,用来分发任务,如Accept或者Readable
Thread t = new Thread (dispatcher);
// 设置一下状态
started = true;
// 运行线程
t.start();
}
前面我们说过,ServerImpl是这整个项目的核心部分,它管理了httpserver的状态,提供了各种接口以及通用的方法,它也负责了几个内部类线程的启动
所以,接下来我们会分为ServerImpl、Dispatcher、Exchange、ServerTimerTask与ServerTimerTask1四个部分来讲解
(https相关的我去掉了)
比较长,大家稍微过一眼有个印象,之后遇到的时候再回来看就行
// http或https
private String protocol;
private Executor executor;
// 负责接收连接用的类(这个本来在209行附近,我把它提上来了)
private Dispatcher dispatcher;
// ContextList这个类只是封装了一个List<HttpContextImpl>及一些方法,如限制监听的context(路径)的数目和查找context的方法
private ContextList contexts;
private InetSocketAddress address;
// nio相关的那些类
private ServerSocketChannel schan;
private Selector selector;
private SelectionKey listenerKey;
// 负责管理之前提到的idle连接,也就是长连接的set
// 长连接时,连接如果没有任务,就加进去. 如果超过一定时间没有任务,则主动断开长连接
private Set<HttpConnection> idleConnections;
// 管理所有的连接,方便在stop等情况下直接断开所有连接
private Set<HttpConnection> allConnections;
// 管理req连接和rsp连接,防止请求或响应超时,超时时由定时线程断开连接
private Set<HttpConnection> reqConnections;
private Set<HttpConnection> rspConnections;
// 这两个之后6.4的Exchange的addEvent方法部分我们再说
private List<Event> events;
private final Object loLock 在写这篇博客之前我查了很久发现全网都没有一篇写httpserver源码解析的
所以今天就由我来为大家解析一下httpserver的源码。(这里我会去掉其中的https部分的源码,只讲http部分,对httpserver中https的实现感兴趣的读者可以尝试自己去阅读,这部分并不复杂)
第一次在没有参考资料的情况下写这么长一篇源码解析,可能会有很多错误和讲不清楚的地方,希望大家尽量指出来。
本文链接 https://www.cnblogs.com/fatmanhappycode/p/12614428.html
大家最好先跟着我构建这样一个小demo,跑起来之后再一步一步去看源码
/**
* @author 肥宅快乐码
*/
public class HttpServerSample {
private static void serverStart() throws IOException {
HttpServerProvider provider = HttpServerProvider.provider();
// 监听端口8080,连接排队队列,如果队列中的连接超过这个数的话就会拒绝连接
HttpServer httpserver =provider.createHttpServer(new InetSocketAddress(8080), 100);
// 监听路径为RestSample,请求处理后回调RestGetHandler里的handle方法
httpserver.createContext("/RestSample", new RestGetHandler());
// 管理工作线程池
ExecutorService executor = new ThreadPoolExecutor(10,200,60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
httpserver.setExecutor(executor);
httpserver.start();
System.out.println("server started");
}
public static void main(String[] args) throws IOException {
serverStart();
}
}
/**
* 回调类,里面的handle方法主要完成将包装好的请求头返回给客户端的功能
*/
class RestGetHandler implements HttpHandler {
@Override
public void handle(HttpExchange he) throws IOException {
String requestMethod = he.getRequestMethod();
// 如果是get方法
if ("GET".equalsIgnoreCase(requestMethod)) {
// 获取响应头,接下来我们来设置响应头信息
Headers responseHeaders = he.getResponseHeaders();
// 以json形式返回,其他还有text/html等等
responseHeaders.set("Content-Type", "application/json");
// 设置响应码200和响应body长度,这里我们设0,没有响应体
he.sendResponseHeaders(200, 0);
// 获取响应体
OutputStream responseBody = he.getResponseBody();
// 获取请求头并打印
Headers requestHeaders = he.getRequestHeaders();
Set<String> keySet = requestHeaders.keySet();
Iterator<String> iter = keySet.iterator();
while (iter.hasNext()) {
String key = iter.next();
List values = requestHeaders.get(key);
String s = key + " = " + values.toString() + "\r\n";
responseBody.write(s.getBytes());
}
// 关闭输出流
responseBody.close();
}
}
}
① 最开始我们通过 HttpServerProvider provider = HttpServerProvider.provider(); 创建了一个HttpServerProvider,也就是这里的DefaultHttpServerProvide
// HttpServerProvider.java
public static HttpServerProvider provider () {
// 这里我们删掉了其他部分,只留下172、173两行
// 这里创建了一个DefaultHttpServerProvide
provider = new sun.net.httpserver.DefaultHttpServerProvider();
return provider;
}
② 之后我们调用 HttpServer httpserver =provider.createHttpServer(new InetSocketAddress(8080), 100); ,
也就是调用了DefaultHttpServerProvider的createHttpServer创建一个HttpServerImpl,当然这里也可以用createHttpsServer创建一个HttpsServerImpl,但是前面说了我们这篇不分析https,所以这里忽略了createHttpsServer方法
还有这里创建ServerImpl的构造方法我们暂时不讲,留到后面再讲
// DefaultHttpServerProvider.java
public HttpServer createHttpServer (InetSocketAddress addr, int backlog) throws IOException {
return new HttpServerImpl (addr, backlog);
}
// HttpServerImpl.java
HttpServerImpl (
InetSocketAddress addr, int backlog
) throws IOException {
server = new ServerImpl (this, "http", addr, backlog);
}
③ 接下来我们创建了一个监听路径 httpserver.createContext("/RestSample", new RestGetHandler());
// HttpServer.java
public abstract HttpContext createContext (String path, HttpHandler handler) ;
// HttpContextImpl.java
public HttpContextImpl createContext (String path, HttpHandler handler) {
// 这里调用的server是ServerImpl类的对象
return server.createContext (path, handler);
}
这里成功返回了一个HttpContextImpl对象,这个我们后面会说,这里我们要知道的是,HttpServerImpl调用的是ServerImpl的实现
到这里我们差不多可以聊一下httpserver的主要结构了:
HttpServer是这里的祖先类,它是一个抽象类,抽象了一个HttpServer应该有的方法
而HttpsServer和我们想象的不一样,它和HttpServer不是平行关系,而是HttpServer的子类,它在HttpServer的基础上加了setHttpsConfigurator和getHttpsConfigurator这两个方法而已
HttpServerImpl和HttpsServerImpl虽然都是实现类,但是它们的方法都是调用ServerImpl的方法,都是围绕ServerImpl的
所以我们也可以把ServerImpl看做这个项目的核心类
④ 之后设置一下工作线程池,初始化任务就完成了
ExecutorService executor = new ThreadPoolExecutor(10,200,60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.AbortPolicy());
httpserver.setExecutor(executor);
httpserver.start();
启动自然和我们刚刚聊的结构一样都是从HttpServer开始一层调一层调用到ServerImpl的方法的:
// HttpServer.java
public abstract void start () ;
// HttpServerImpl.java
public void start () {
server.start();
}
// ServerImpl.java
public void start () {
// server未绑定端口或处于已启动或已关闭状态
// 顺便先提一下,这里就可以留意到,ServerImpl作为一个核心类,管理了各种各样的状态(state)等
if (!bound || started || finished) {
throw new IllegalStateException ("server in wrong state");
}
// 如果没有设置线程池,那就用默认的,默认的话等于没有用线程池,是直接execute的,所以尽可能直接创建线程池
if (executor == null) {
executor = new DefaultExecutor();
}
// 创建了一个Dispatcher线程,用来分发任务,如Accept或者Readable
Thread t = new Thread (dispatcher);
// 设置一下状态
started = true;
// 运行线程
t.start();
}
前面我们说过,ServerImpl是这整个项目的核心部分,它管理了httpserver的状态,提供了各种接口以及通用的方法,它也负责了几个内部类线程的启动
所以,接下来我们会分为ServerImpl、Dispatcher、Exchange、ServerTimerTask与ServerTimerTask1四个部分来讲解
(https相关的我去掉了)
比较长,大家稍微过一眼有个印象,之后遇到的时候再回来看就行
// http或https
private String protocol;
private Executor executor;
// 负责接收连接用的类(这个本来在209行附近,我把它提上来了)
private Dispatcher dispatcher;
// ContextList这个类只是封装了一个List<HttpContextImpl>及一些方法,如限制监听的context(路径)的数目和查找context的方法
private ContextList contexts;
private InetSocketAddress address;
// nio相关的那些类
private ServerSocketChannel schan;
private Selector selector;
private SelectionKey listenerKey;
// 负责管理之前提到的idle连接,也就是长连接的set
// 长连接时,连接如果没有任务,就加进去. 如果超过一定时间没有任务,则主动断开长连接
private Set<HttpConnection> idleConnections;
// 管理所有的连接,方便在stop等情况下直接断开所有连接
private Set<HttpConnection> allConnections;
// 管理req连接和rsp连接,防止请求或响应超时,超时时由定时线程断开连接
private Set<HttpConnection> reqConnections;
private Set<HttpConnection> rspConnections;
// 这两个之后6.4的Exchange的addEvent方法部分我们再说
private List<Event> events;
private final Object loLock = new Object();
// 各种状态,相信大家看得懂是什么意思
private volatile boolean finished = false;
private volatile boolean terminating = false;
private boolean bound = false;
private boolean started = false;
// 系统时间,会由ServerTimerTask进行更新
private volatile long time;
// 这个似乎并没有任何用
private volatile long subticks = 0;
// 这个是用来记录一共更新了多少次time的,相当于时间戳一样的东西
private volatile long ticks;
// 把HttpServer包装进来,方便调用
private HttpServer wrapper;
// 这个的意思是ServerTimerTask每隔多长时间定期run一下,因为ServerTimerTask是一个定时任务线程
// 默认是10000ms也就是10秒一次
private final static int CLOCK_TICK = ServerConfig.getClockTick();
// 这个是允许长连接驻留的时间,默认是30秒
private final static long IDLE_INTERVAL = ServerConfig.getIdleInterval();
// 允许最大长连接数,默认200
private final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections();
// ServerTimerTask1的定期时间,默认是1秒
private final static long TIMER_MILLIS = ServerConfig.getTimerMillis ();
// 最后这两个默认为-1,至于为什么是-1后面ServerTimerTask部分我们会说
private final static long MAX_REQ_TIME = getTimeMillis(ServerConfig.getMaxReqTime());
private final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime());
private final static boolean REQ_RSP_CLEAN_ENABLED = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1;
// ServerTimerTask和ServerTimerTask1的对象,跑起来就是ServerTimerTask和ServerTimerTask1线程了
private Timer timer, timer1;
private Logger logger;
这就是刚刚2.1小节中提到的ServerImpl的构造方法,没什么要讲的,无非就是初始化了变量并启动了ServerTimerTask和ServerTimerTask1线程
ServerImpl (
HttpServer wrapper, String protocol, InetSocketAddress addr, int backlog
) throws IOException {
this.protocol = protocol;
this.wrapper = wrapper;
this.logger = Logger.getLogger ("com.sun.net.httpserver");
ServerConfig.checkLegacyProperties (logger);
this.address = addr;
contexts = new ContextList();
schan = ServerSocketChannel.open();
if (addr != null) {
ServerSocket socket = schan.socket();
socket.bind (addr, backlog);
bound = true;
}
selector = Selector.open ();
schan.configureBlocking (false);
listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT);
dispatcher = new Dispatcher();
idleConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
allConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
reqConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
rspConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
time = System.currentTimeMillis();
timer = new Timer ("server-timer", true);
// 可以看到,在初始化阶段两个定时任务就已经启动了
timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK);
if (timer1Enabled) {
timer1 = new Timer ("server-timer1", true);
timer1.schedule (new ServerTimerTask1(),TIMER_MILLIS,TIMER_MILLIS);
logger.config ("HttpServer timer1 enabled period in ms: "+TIMER_MILLIS);
logger.config ("MAX_REQ_TIME: "+MAX_REQ_TIME);
logger.config ("MAX_RSP_TIME: "+MAX_RSP_TIME);
}
events = new LinkedList<Event>(www.tengyueylzc.cn );
logger.config ("HttpServer created "+protocol+" "+ addr);
}
当然ServerImpl有很多通用的方法,但是这里我们不讲,等到用到它们的时候我们再讲,这样比较方便了解这些通用方法的具体用途
= new Object();
// 各种状态,相信大家看得懂是什么意思
private volatile boolean finished shentuylzc.cn= false;
private volatile boolean terminating www.shentuylgw.cn= false;
private boolean bound = false;
private boolean started = false;
// 系统时间,会由ServerTimerTask进行更新
private volatile long time;
// 这个似乎并没有任何用
private volatile long subticks = 0;
// 这个是用来记录一共更新了多少次time的,相当于时间戳一样的东西
private volatile long ticks;
// 把HttpServer包装进来,方便调用
private HttpServer wrapper;
// 这个的意思是ServerTimerTask每隔多长时间定期run一下,因为ServerTimerTask是一个定时任务线程
// 默认是10000ms也就是10秒一次
private final static int CLOCK_TICK www.laiyuefeng.com= ServerConfig.getClockTick();
// 这个是允许长连接驻留的时间,默认是30秒
private final static long IDLE_INTERVAL = ServerConfig.getIdleInterval();
// 允许最大长连接数,默认200
private final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections();
// ServerTimerTask1的定期时间,默认是1秒
private final static long TIMER_MILLIS www.51kunlunyule.com= ServerConfig.getTimerMillis ();
// 最后这两个默认为-1,至于为什么是-1后面ServerTimerTask部分我们会说
private final static long MAX_REQ_TIME = getTimeMillis(ServerConfig.getMaxReqTime());
private final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime());
private final static boolean REQ_RSP_CLEAN_ENABLED = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1;
// ServerTimerTask和ServerTimerTask1的对象,跑起来就是ServerTimerTask和ServerTimerTask1线程了
private Timer timer, timer1;
private Logger logger;
这就是刚刚2.1小节中提到的ServerImpl的构造方法,没什么要讲的,无非就是初始化了变量并启动了ServerTimerTask和ServerTimerTask1线程
ServerImpl (
HttpServer wrapper, String protocol, InetSocketAddress addr, int backlog
) throws IOException {
this.protocol = protocol;
this.wrapper = wrapper;
this.logger = Logger.getLogger ("com.sun.net.httpserver");
ServerConfig.checkLegacyProperties (logger);
this.address = addr;
contexts = new ContextList();
schan = ServerSocketChannel.open(www.leyouzaixan.cn);
if (addr != null) {
ServerSocket socket = schan.socket(www.javachenglei.com);
socket.bind (addr, backlog);
bound = true;
}
selector = Selector.open (www.51kunlunyule.com);
schan.configureBlocking (false);
listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT);
dispatcher = new Dispatcher();
idleConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
allConnections www.yixingylzc.cn= Collections.synchronizedSet (new HashSet<HttpConnection>());
reqConnections = www.baihua178.cn Collections.synchronizedSet (new HashSet<HttpConnection>());
rspConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
time = System.currentTimeMillis(www.51feiyuzc.cn);
timer = new Timer ("server-timer", true);
// 可以看到,在初始化阶段两个定时任务就已经启动了
timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK);
if (timer1Enabled) {
timer1 = new Timer ("server-timer1", true);
timer1.schedule (new ServerTimerTask1(),TIMER_MILLIS,TIMER_MILLIS);
logger.config ("HttpServer timer1 enabled period in ms: "+TIMER_MILLIS);
logger.config ("MAX_REQ_TIME: "+MAX_REQ_TIME);
logger.config ("MAX_RSP_TIME: "+MAX_RSP_TIME);
}
events = new LinkedList<Event>(www.51baishizc.cn );
logger.config ("HttpServer created "+protocol+" "+ addr);
}
当然ServerImpl有很多通用的方法,但是这里我们不讲,等到用到它们的时候我们再讲,这样比较方便了解这些通用方法的具体用途
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。