一个简单的HTTPServer应该做些什么?

176 阅读2分钟

最好的掌握技术的方法还是动手。

GitHub:源代码 ( 🍭 )

本文介绍了自己写的一个简单的仅具有以下基本功能的HttpServer:

  1. 模拟处理GET请求访问网页
  2. 能处理请求异常
  3. 能持续处理请求

并对其功能进行了两点优化:

  1. 为GET请求建立LRU缓存(基于LinkedHashMap)
  2. 为服务器添加限流功能(基于Guava RateLimiter 令牌桶算法)。

HTTP生命周期

  1. read: 读取socket数据流
  2. parser: 解析数据流,分析报头,得到客户端的命令语义
  3. process: 处理客户端命令,返回结果
  4. reponse: 将处理结果打包,增加报头
  5. write: 写入socket数据流

一定要注意报文格式,头和体之间有一个 "\r\n"


HTTP 1.1 请求报文格式(图片来自网络)

image.png

HTTP 1.1 响应报文格式(图片来自网络)

response.png

改进

  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.

  1. 为服务端添加限流功能

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();
    }
如图可见,每秒仅会处理一次连接。

img_1.png

参考: