最好的掌握技术的方法还是动手。
GitHub:源代码 ( 🍭 )
本文介绍了自己写的一个简单的仅具有以下基本功能的HttpServer:
- 模拟处理GET请求访问网页
- 能处理请求异常
- 能持续处理请求
并对其功能进行了两点优化:
- 为GET请求建立LRU缓存(基于LinkedHashMap)
- 为服务器添加限流功能(基于Guava RateLimiter 令牌桶算法)。
HTTP生命周期
- read: 读取socket数据流
- parser: 解析数据流,分析报头,得到客户端的命令语义
- process: 处理客户端命令,返回结果
- reponse: 将处理结果打包,增加报头
- write: 写入socket数据流
一定要注意报文格式,头和体之间有一个 "\r\n"
HTTP 1.1 请求报文格式(图片来自网络)
HTTP 1.1 响应报文格式(图片来自网络)
改进
- 为get方法添加LRU缓存 基于LinkedHashMap, 重写removeEldestEntry 即可,每次put 或 putAll 会调用该方法。
Returns true if this map should remove its eldest entry. This method is invoked by put and putAll after inserting a new entry into the map. It provides the implementor with the opportunity to remove the eldest entry each time a new one is added. This is useful if the map represents a cache: it allows the map to reduce memory consumption by deleting stale entries.
- 为服务端添加限流功能
2.1 使用Guava RateLimiter 控制连接产生的速度。
RateLimiter limiter = RateLimiter.create(1.0);
while ((socket = serverSocket.accept()) != null){
limiter.acquire();
threadPool.execute(new HttpServer(socket));
}
2.2 使用CountdownLatch 保证服务端同时发送多个连接。
//在所有线程准备好之前阻塞消息
countDownLatch.await();
output.flush();
for(int i=0; i<10; i++){
ClientConn clientConn = new ClientConn();
clientConn.start();
//每准备好一个线程,计数减一
countDownLatch.countDown();
}
如图可见,每秒仅会处理一次连接。
参考:
- Java 从零开始手撸一个 HTTP 服务器
- 《Java 并发开发的艺术》:一个基于线程池技术的简单服务器