一、核心架构与协议帧
1. HTTP/2 帧结构
java
复制
下载
/**
* HTTP/2 帧基础结构
* 所有帧都遵循9字节的帧头格式
*/
public class Http2Frame {
// RFC 7540 定义的帧结构
// +-----------------------------------------------+
// | Length (24) |
// +---------------+---------------+---------------+
// | Type (8) | Flags (8) |
// +-+-------------+---------------+-------------------------------+
// |R| Stream Identifier (31) |
// +-+-------------------------------------------------------------+
// | Frame Payload (0...) ...
// +---------------------------------------------------------------+
/**
* 帧类型枚举
*/
public enum FrameType {
DATA(0x0), // 数据帧
HEADERS(0x1), // 头部帧
PRIORITY(0x2), // 优先级帧
RST_STREAM(0x3), // 流终止帧
SETTINGS(0x4), // 设置帧
PUSH_PROMISE(0x5), // 推送承诺帧
PING(0x6), // PING帧
GOAWAY(0x7), // 连接关闭帧
WINDOW_UPDATE(0x8), // 窗口更新帧
CONTINUATION(0x9); // 延续帧
private final int value;
FrameType(int value) { this.value = value; }
public int getValue() { return value; }
}
/**
* 帧头
*/
public static class FrameHeader {
private int length; // 24位,负载长度
private FrameType type; // 8位,帧类型
private byte flags; // 8位,标志位
private int streamId; // 31位,流标识符(0表示连接控制帧)
// 标志位常量
public static final byte END_STREAM = 0x1;
public static final byte END_HEADERS = 0x4;
public static final byte PADDED = 0x8;
public static final byte PRIORITY = 0x20;
public byte[] toBytes() {
ByteBuffer buffer = ByteBuffer.allocate(9);
buffer.put((byte) (length >>> 16)); // 长度高位
buffer.put((byte) (length >>> 8)); // 长度中位
buffer.put((byte) length); // 长度低位
buffer.put((byte) type.getValue()); // 类型
buffer.put(flags); // 标志位
buffer.putInt(streamId & 0x7FFFFFFF); // 流ID(最高位保留)
return buffer.array();
}
}
}
2. 多路复用核心:流(Stream)
java
复制
下载
/**
* HTTP/2 流(Stream)实现
* 每个流代表一个独立的请求-响应交换
*/
public class Http2Stream implements Comparable<Http2Stream> {
private final int streamId;
private volatile StreamState state;
private final PriorityTreeNode priorityNode;
private final ConcurrentLinkedDeque<Http2Frame> sendQueue;
private final ConcurrentLinkedDeque<Http2Frame> receiveQueue;
// 流状态机
public enum StreamState {
IDLE, // 空闲
RESERVED_LOCAL, // 本地保留
RESERVED_REMOTE, // 远程保留
OPEN, // 打开
HALF_CLOSED_LOCAL, // 本地半关闭
HALF_CLOSED_REMOTE, // 远程半关闭
CLOSED // 关闭
}
/**
* 优先级树节点
* HTTP/2使用优先级树管理流的发送顺序
*/
public static class PriorityTreeNode {
private final Http2Stream stream;
private int weight; // 权重 1-256
private boolean exclusive; // 独占标志
private PriorityTreeNode parent; // 父节点
private List<PriorityTreeNode> children; // 子节点
public PriorityTreeNode(Http2Stream stream, int weight) {
this.stream = stream;
this.weight = Math.max(1, Math.min(256, weight));
this.children = new ArrayList<>();
}
/**
* 插入新的优先级节点
* @param newChild 新节点
* @param exclusive 是否独占(插入为唯一直接子节点)
*/
public void insertChild(PriorityTreeNode newChild, boolean exclusive) {
if (exclusive) {
// 独占模式:新节点成为父节点的唯一直接子节点
// 原来的子节点成为新节点的子节点
for (PriorityTreeNode child : children) {
newChild.addChild(child);
}
children.clear();
children.add(newChild);
newChild.parent = this;
} else {
children.add(newChild);
newChild.parent = this;
}
}
}
/**
* 流状态转换(根据RFC 7540 5.1节)
*/
public boolean transition(StreamState newState, FrameType frameType) {
synchronized (this) {
boolean valid = isValidTransition(state, newState, frameType);
if (valid) {
state = newState;
log.debug("Stream {}: {} -> {}", streamId, state, newState);
}
return valid;
}
}
private boolean isValidTransition(StreamState current, StreamState next, FrameType frame) {
// 实现RFC 7540 5.1节的状态转换表
switch (current) {
case IDLE:
return (frame == FrameType.HEADERS || frame == FrameType.PUSH_PROMISE) &&
(next == StreamState.OPEN || next == StreamState.RESERVED_LOCAL ||
next == StreamState.RESERVED_REMOTE);
case OPEN:
return (frame == FrameType.RST_STREAM && next == StreamState.CLOSED) ||
(frame == FrameType.HEADERS && next == StreamState.HALF_CLOSED_LOCAL) ||
((frame == FrameType.DATA || frame == FrameType.HEADERS) &&
next == StreamState.HALF_CLOSED_REMOTE);
// ... 其他状态转换
default:
return false;
}
}
@Override
public int compareTo(Http2Stream other) {
// 基于优先级的比较
return Integer.compare(
this.priorityNode.weight * getDepth(this.priorityNode),
other.priorityNode.weight * getDepth(other.priorityNode)
);
}
private int getDepth(PriorityTreeNode node) {
int depth = 0;
while (node.parent != null) {
depth++;
node = node.parent;
}
return depth;
}
}
二、多路复用(Multiplexing)实现
1. 连接管理器(Connection Handler)
java
复制
下载
/**
* HTTP/2 连接管理器
* 管理所有流的多路复用
*/
public class Http2ConnectionHandler {
// 连接级设置
private final Http2Settings settings;
// 流管理
private final AtomicInteger nextStreamId = new AtomicInteger(1); // 客户端从奇数开始
private final ConcurrentMap<Integer, Http2Stream> streams = new ConcurrentHashMap<>();
private final PriorityTree priorityTree = new PriorityTree();
// 流量控制
private final FlowController flowController;
// 发送队列和调度器
private final PriorityBlockingQueue<Http2Frame> sendQueue;
private final FrameScheduler frameScheduler;
/**
* 初始化HTTP/2连接
*/
public void initializeConnection(SocketChannel channel) throws IOException {
// 1. 发送连接前言(Connection Preface)
sendConnectionPreface(channel);
// 2. 交换SETTINGS帧
exchangeSettings(channel);
// 3. 启动帧调度器
frameScheduler.start();
log.info("HTTP/2连接初始化完成");
}
/**
* 发送连接前言
* 必须是"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
*/
private void sendConnectionPreface(SocketChannel channel) throws IOException {
String preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
ByteBuffer buffer = ByteBuffer.wrap(preface.getBytes(StandardCharsets.UTF_8));
channel.write(buffer);
}
/**
* 创建新流
*/
public Http2Stream createStream(int dependencyId, int weight, boolean exclusive) {
int streamId = nextStreamId.getAndAdd(2); // 客户端流ID为奇数
Http2Stream stream = new Http2Stream(streamId);
streams.put(streamId, stream);
// 设置优先级
if (dependencyId == 0) {
priorityTree.addRootStream(stream, weight);
} else {
Http2Stream parentStream = streams.get(dependencyId);
if (parentStream != null) {
priorityTree.insertStream(stream, parentStream, weight, exclusive);
}
}
log.debug("创建新流: {}", streamId);
return stream;
}
/**
* 接收并处理帧
*/
public void handleFrame(Http2Frame frame) {
int streamId = frame.getStreamId();
if (streamId == 0) {
// 连接级帧
handleConnectionFrame(frame);
} else {
// 流级帧
Http2Stream stream = streams.get(streamId);
if (stream != null) {
handleStreamFrame(stream, frame);
} else if (streamId >= nextStreamId.get()) {
// 未知流,发送RST_STREAM
sendRstStream(streamId, ErrorCode.PROTOCOL_ERROR);
}
}
}
/**
* 帧调度器 - 基于优先级发送帧
*/
private class FrameScheduler extends Thread {
private volatile boolean running = true;
@Override
public void run() {
while (running) {
try {
// 1. 基于优先级选择要发送的流
Http2Stream stream = selectNextStream();
if (stream != null) {
// 2. 从流中获取待发送的帧
Http2Frame frame = stream.pollSendFrame();
if (frame != null) {
// 3. 应用流量控制
if (flowController.canSend(stream, frame)) {
// 4. 发送帧
sendFrame(frame);
// 5. 更新流量控制窗口
flowController.updateWindow(stream, frame);
}
}
} else {
Thread.sleep(1); // 避免忙等待
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
/**
* 选择下一个要发送的流(基于优先级和权重)
*/
private Http2Stream selectNextStream() {
return priorityTree.getNextStream();
}
public void shutdown() {
running = false;
interrupt();
}
}
/**
* 优先级树实现
*/
private class PriorityTree {
private final Http2Stream rootStream; // 虚拟根流(stream 0)
private final Map<Integer, PriorityTreeNode> nodeMap;
public PriorityTree() {
this.rootStream = new Http2Stream(0);
this.nodeMap = new ConcurrentHashMap<>();
}
/**
* 获取下一个要处理的流(加权轮询)
*/
public Http2Stream getNextStream() {
List<Http2Stream> candidates = new ArrayList<>();
collectReadyStreams(rootStream, candidates);
if (candidates.isEmpty()) {
return null;
}
// 加权随机选择
return weightedRandomSelect(candidates);
}
private void collectReadyStreams(Http2Stream stream, List<Http2Stream> result) {
if (stream.hasDataToSend()) {
result.add(stream);
}
for (PriorityTreeNode child : stream.getPriorityNode().getChildren()) {
collectReadyStreams(child.getStream(), result);
}
}
private Http2Stream weightedRandomSelect(List<Http2Stream> streams) {
int totalWeight = streams.stream()
.mapToInt(s -> s.getPriorityNode().getWeight())
.sum();
int random = ThreadLocalRandom.current().nextInt(totalWeight);
int cumulative = 0;
for (Http2Stream stream : streams) {
cumulative += stream.getPriorityNode().getWeight();
if (random < cumulative) {
return stream;
}
}
return streams.get(0);
}
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
2. 流量控制(Flow Control)
java
复制
下载
/**
* HTTP/2 流量控制器
* 实现RFC 7540 6.9节的流量控制机制
*/
public class FlowController {
// 连接级窗口大小(默认65535字节)
private volatile int connectionWindowSize = 65535;
private final Object connectionWindowLock = new Object();
// 流级窗口大小
private final ConcurrentMap<Integer, Integer> streamWindows = new ConcurrentHashMap<>();
// 初始窗口大小(SETTINGS_INITIAL_WINDOW_SIZE)
private volatile int initialWindowSize = 65535;
/**
* 检查是否可以发送数据
*/
public boolean canSend(Http2Stream stream, Http2Frame frame) {
if (frame.getType() != FrameType.DATA) {
return true; // 非DATA帧不受流量控制
}
int dataLength = frame.getPayloadLength();
int streamId = stream.getStreamId();
synchronized (connectionWindowLock) {
// 检查连接级窗口
if (dataLength > connectionWindowSize) {
return false;
}
// 检查流级窗口
int streamWindow = streamWindows.getOrDefault(streamId, initialWindowSize);
if (dataLength > streamWindow) {
return false;
}
return true;
}
}
/**
* 更新窗口大小
*/
public void updateWindow(Http2Stream stream, Http2Frame frame) {
if (frame.getType() != FrameType.DATA) {
return;
}
int dataLength = frame.getPayloadLength();
int streamId = stream.getStreamId();
synchronized (connectionWindowLock) {
// 更新连接级窗口
connectionWindowSize -= dataLength;
// 更新流级窗口
streamWindows.compute(streamId, (k, v) -> {
int current = (v != null) ? v : initialWindowSize;
return current - dataLength;
});
// 如果窗口过小,发送WINDOW_UPDATE帧
if (connectionWindowSize < initialWindowSize / 2) {
sendWindowUpdate(0, initialWindowSize - connectionWindowSize);
}
int streamWindow = streamWindows.get(streamId);
if (streamWindow < initialWindowSize / 2) {
sendWindowUpdate(streamId, initialWindowSize - streamWindow);
}
}
}
/**
* 处理WINDOW_UPDATE帧
*/
public void handleWindowUpdate(int streamId, int windowSizeIncrement) {
synchronized (connectionWindowLock) {
if (streamId == 0) {
// 连接级窗口更新
connectionWindowSize += windowSizeIncrement;
log.debug("连接窗口增加 {} 到 {}", windowSizeIncrement, connectionWindowSize);
} else {
// 流级窗口更新
streamWindows.compute(streamId, (k, v) -> {
int current = (v != null) ? v : initialWindowSize;
int newSize = current + windowSizeIncrement;
// 防止窗口溢出(RFC 7540 6.9.1)
if (newSize > 0x7FFFFFFF) {
throw new Http2Exception(ErrorCode.FLOW_CONTROL_ERROR,
"窗口大小溢出: " + newSize);
}
return newSize;
});
log.debug("流 {} 窗口增加 {} 到 {}", streamId, windowSizeIncrement,
streamWindows.get(streamId));
}
}
}
}
三、头部压缩(HPACK)实现
1. HPACK编码器/解码器
java
复制
下载
/**
* HPACK头部压缩实现(RFC 7541)
*/
public class HpackCodec {
// 静态表(RFC 7541 附录A)
private static final List<HeaderField> STATIC_TABLE = createStaticTable();
// 动态表(先进先出)
private final EvictingRingBuffer<HeaderField> dynamicTable;
// 动态表大小限制
private int dynamicTableSize = 4096; // 默认4KB
private int maxDynamicTableSize = 4096;
/**
* HPACK头部字段
*/
public static class HeaderField {
private final String name;
private final String value;
private final int size; // 计算大小:name.length + value.length + 32
public HeaderField(String name, String value) {
this.name = name;
this.value = value;
this.size = name.length() + value.length() + 32;
}
public int getSize() { return size; }
}
/**
* HPACK编码器
*/
public static class Encoder {
private final HpackCodec codec;
public Encoder(HpackCodec codec) {
this.codec = codec;
}
/**
* 编码头部列表
*/
public byte[] encodeHeaders(List<HeaderField> headers) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
for (HeaderField header : headers) {
// 1. 尝试在静态表中查找完全匹配
int staticIndex = findInStaticTable(header);
if (staticIndex > 0) {
encodeIndexedField(staticIndex, output);
continue;
}
// 2. 尝试在动态表中查找完全匹配
int dynamicIndex = codec.findInDynamicTable(header);
if (dynamicIndex > 0) {
encodeIndexedField(dynamicIndex + STATIC_TABLE.size(), output);
continue;
}
// 3. 尝试查找名称匹配(使用索引的Literal表示)
int nameIndex = findNameInTables(header.name);
if (nameIndex > 0) {
encodeLiteralWithIndexing(nameIndex, header.value, output);
} else {
// 4. 全新的头部字段
encodeLiteralNewName(header.name, header.value, output);
}
// 5. 添加到动态表
codec.addToDynamicTable(header);
}
return output.toByteArray();
}
/**
* 编码索引字段(6.1节)
* 格式:1xxxxxxx
*/
private void encodeIndexedField(int index, OutputStream out) throws IOException {
out.write(0x80 | (index & 0x7F));
}
/**
* 编码Literal头部字段,带索引(6.2.1节)
* 格式:01xxxxxx
*/
private void encodeLiteralWithIndexing(int nameIndex, String value,
OutputStream out) throws IOException {
// 前两位01表示Literal with Incremental Indexing
out.write(0x40 | (nameIndex & 0x3F));
encodeString(value, out);
}
/**
* 编码Literal头部字段,新名称(6.2.2节)
*/
private void encodeLiteralNewName(String name, String value,
OutputStream out) throws IOException {
// 前两位01,名称索引为0
out.write(0x40);
encodeString(name, out);
encodeString(value, out);
}
/**
* 编码字符串(5.2节)
*/
private void encodeString(String str, OutputStream out) throws IOException {
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
// Huffman编码
byte[] huffmanEncoded = Huffman.encode(bytes);
if (huffmanEncoded.length < bytes.length) {
// 使用Huffman编码
out.write(0x80 | (huffmanEncoded.length & 0x7F));
out.write(huffmanEncoded);
} else {
// 不使用Huffman编码
out.write(bytes.length & 0x7F);
out.write(bytes);
}
}
}
/**
* HPACK解码器
*/
public static class Decoder {
private final HpackCodec codec;
public Decoder(HpackCodec codec) {
this.codec = codec;
}
/**
* 解码头部块
*/
public List<HeaderField> decodeHeaders(byte[] data) throws IOException {
ByteArrayInputStream input = new ByteArrayInputStream(data);
List<HeaderField> headers = new ArrayList<>();
while (input.available() > 0) {
int firstByte = input.read() & 0xFF;
if ((firstByte & 0x80) != 0) {
// 索引头部字段(6.1节)
int index = decodeInteger(firstByte & 0x7F, input);
HeaderField header = codec.getHeaderField(index);
headers.add(header);
} else if ((firstByte & 0xC0) == 0x40) {
// Literal with Incremental Indexing(6.2.1节)
int index = decodeInteger(firstByte & 0x3F, input);
String name = (index == 0) ? decodeString(input) :
codec.getHeaderField(index).getName();
String value = decodeString(input);
HeaderField header = new HeaderField(name, value);
headers.add(header);
codec.addToDynamicTable(header);
} else if ((firstByte & 0xF0) == 0x00) {
// Literal without Indexing(6.2.2节)
int index = decodeInteger(firstByte & 0x0F, input);
String name = (index == 0) ? decodeString(input) :
codec.getHeaderField(index).getName();
String value = decodeString(input);
headers.add(new HeaderField(name, value));
} else if ((firstByte & 0xF0) == 0x10) {
// Literal never Indexed(6.2.3节)
int index = decodeInteger(firstByte & 0x0F, input);
String name = (index == 0) ? decodeString(input) :
codec.getHeaderField(index).getName();
String value = decodeString(input);
headers.add(new HeaderField(name, value));
} else if (firstByte == 0x20) {
// 动态表大小更新(6.3节)
int newSize = decodeInteger(0x1F, input);
codec.setDynamicTableSize(newSize);
}
}
return headers;
}
/**
* 解码整数(5.1节)
*/
private int decodeInteger(int prefixMask, InputStream input) throws IOException {
int value = prefixMask;
if (value < prefixMask) {
return value; // 值就在前缀中
}
int m = 0;
int b;
do {
b = input.read();
value += (b & 0x7F) << m;
m += 7;
} while ((b & 0x80) != 0);
return value;
}
/**
* 解码字符串(5.2节)
*/
private String decodeString(InputStream input) throws IOException {
int firstByte = input.read();
boolean huffmanEncoded = (firstByte & 0x80) != 0;
int length = decodeInteger(firstByte & 0x7F, input);
byte[] data = new byte[length];
input.read(data);
if (huffmanEncoded) {
return new String(Huffman.decode(data), StandardCharsets.UTF_8);
} else {
return new String(data, StandardCharsets.UTF_8);
}
}
}
/**
* 创建静态表(RFC 7541 附录A)
*/
private static List<HeaderField> createStaticTable() {
List<HeaderField> table = new ArrayList<>();
table.add(null); // 索引从1开始,所以0位置为空
// 添加RFC 7541附录A定义的61个条目
table.add(new HeaderField(":authority", ""));
table.add(new HeaderField(":method", "GET"));
table.add(new HeaderField(":method", "POST"));
table.add(new HeaderField(":path", "/"));
table.add(new HeaderField(":path", "/index.html"));
table.add(new HeaderField(":scheme", "http"));
table.add(new HeaderField(":scheme", "https"));
table.add(new HeaderField(":status", "200"));
table.add(new HeaderField(":status", "204"));
table.add(new HeaderField(":status", "206"));
// ... 继续添加其他58个条目
return Collections.unmodifiableList(table);
}
/**
* 在动态表中查找头部字段
*/
public int findInDynamicTable(HeaderField header) {
for (int i = 0; i < dynamicTable.size(); i++) {
HeaderField entry = dynamicTable.get(i);
if (entry.name.equals(header.name) && entry.value.equals(header.value)) {
return i + 1; // 动态表索引从静态表之后开始
}
}
return -1;
}
/**
* 添加头部字段到动态表
*/
public synchronized void addToDynamicTable(HeaderField header) {
int headerSize = header.getSize();
// 确保动态表大小不超过限制
while (dynamicTableSize + headerSize > maxDynamicTableSize) {
if (dynamicTable.isEmpty()) {
break;
}
HeaderField removed = dynamicTable.remove();
dynamicTableSize -= removed.getSize();
}
if (headerSize <= maxDynamicTableSize) {
dynamicTable.add(header);
dynamicTableSize += headerSize;
}
}
}
2. Huffman编码实现
java
复制
下载
/**
* HPACK Huffman编码(RFC 7541 附录B)
*/
public class Huffman {
// Huffman编码表(RFC 7541 附录B)
private static final HuffmanCode[] HUFFMAN_CODES = createHuffmanTable();
/**
* Huffman编码条目
*/
private static class HuffmanCode {
final int code; // 编码值
final int length; // 编码长度(位)
HuffmanCode(int code, int length) {
this.code = code;
this.length = length;
}
}
/**
* 编码字节数组
*/
public static byte[] encode(byte[] input) {
BitOutputStream bitStream = new BitOutputStream();
for (byte b : input) {
int unsignedByte = b & 0xFF;
HuffmanCode hc = HUFFMAN_CODES[unsignedByte];
// 写入Huffman编码
bitStream.writeBits(hc.code, hc.length);
}
// 添加EOS(End of String)标记
bitStream.writeBits(0x3FFFFFFF, 30);
return bitStream.toByteArray();
}
/**
* 解码字节数组
*/
public static byte[] decode(byte[] encoded) throws IOException {
BitInputStream bitStream = new BitInputStream(encoded);
ByteArrayOutputStream output = new ByteArrayOutputStream();
int currentCode = 0;
int bitCount = 0;
while (true) {
int bit = bitStream.readBit();
if (bit == -1) {
break; // 输入结束
}
currentCode = (currentCode << 1) | bit;
bitCount++;
// 查找匹配的Huffman编码
int symbol = lookupHuffmanCode(currentCode, bitCount);
if (symbol != -1) {
if (symbol == 256) { // EOS
break;
}
output.write(symbol);
currentCode = 0;
bitCount = 0;
}
if (bitCount > 30) {
throw new IOException("无效的Huffman编码");
}
}
// 检查是否有未使用的位(应该是0)
int remainingBits = bitStream.getRemainingBits();
if (remainingBits > 7) {
throw new IOException("填充位过多");
}
return output.toByteArray();
}
/**
* 位输出流
*/
private static class BitOutputStream {
private int currentByte = 0;
private int bitCount = 0;
private final ByteArrayOutputStream output = new ByteArrayOutputStream();
public void writeBits(int code, int length) {
for (int i = length - 1; i >= 0; i--) {
int bit = (code >> i) & 1;
currentByte = (currentByte << 1) | bit;
bitCount++;
if (bitCount == 8) {
output.write(currentByte);
currentByte = 0;
bitCount = 0;
}
}
}
public byte[] toByteArray() {
if (bitCount > 0) {
// 填充剩余位为1(RFC 7541 5.2节)
currentByte <<= (8 - bitCount);
currentByte |= (1 << (8 - bitCount)) - 1;
output.write(currentByte);
}
return output.toByteArray();
}
}
/**
* 位输入流
*/
private static class BitInputStream {
private final byte[] data;
private int byteIndex = 0;
private int bitIndex = 0;
public BitInputStream(byte[] data) {
this.data = data;
}
public int readBit() {
if (byteIndex >= data.length) {
return -1;
}
int bit = (data[byteIndex] >> (7 - bitIndex)) & 1;
bitIndex++;
if (bitIndex == 8) {
byteIndex++;
bitIndex = 0;
}
return bit;
}
public int getRemainingBits() {
return (data.length - byteIndex) * 8 - bitIndex;
}
}
}
四、完整HTTP/2客户端实现
java
复制
下载
/**
* 完整的HTTP/2客户端实现
*/
public class Http2Client {
private final SocketChannel channel;
private final Http2ConnectionHandler connectionHandler;
private final HpackCodec hpackCodec;
private final ExecutorService executor;
// 连接状态
private volatile boolean connected = false;
public Http2Client(String host, int port) throws IOException {
this.channel = SocketChannel.open();
this.channel.connect(new InetSocketAddress(host, port));
this.channel.configureBlocking(false);
this.hpackCodec = new HpackCodec();
this.connectionHandler = new Http2ConnectionHandler(hpackCodec);
this.executor = Executors.newCachedThreadPool();
initializeHttp2Connection();
}
/**
* 初始化HTTP/2连接
*/
private void initializeHttp2Connection() throws IOException {
// 1. 发送连接前言
sendConnectionPreface();
// 2. 发送初始SETTINGS帧
sendInitialSettings();
// 3. 启动接收线程
startReceiverThread();
connected = true;
}
/**
* 发送HTTP/2请求
*/
public CompletableFuture<Http2Response> sendRequest(Http2Request request) {
CompletableFuture<Http2Response> future = new CompletableFuture<>();
executor.submit(() -> {
try {
// 1. 创建新流
Http2Stream stream = connectionHandler.createStream(
request.getDependencyId(),
request.getWeight(),
request.isExclusive()
);
// 2. 编码请求头部
byte[] encodedHeaders = hpackCodec.getEncoder().encodeHeaders(
request.getHeaders()
);
// 3. 发送HEADERS帧
Http2Frame headersFrame = new Http2Frame.Builder()
.type(FrameType.HEADERS)
.streamId(stream.getStreamId())
.flags(Http2Frame.FrameHeader.END_HEADERS)
.payload(encodedHeaders)
.build();
connectionHandler.sendFrame(headersFrame);
// 4. 如果有请求体,发送DATA帧
if (request.getBody() != null && request.getBody().length > 0) {
sendRequestBody(stream, request.getBody());
}
// 5. 等待响应
Http2Response response = awaitResponse(stream, request.getTimeout());
future.complete(response);
} catch (Exception e) {
future.completeExceptionally(e);
}
});
return future;
}
/**
* 发送请求体
*/
private void sendRequestBody(Http2Stream stream, byte[] body) {
int offset = 0;
int maxFrameSize = connectionHandler.getMaxFrameSize();
while (offset < body.length) {
int chunkSize = Math.min(maxFrameSize, body.length - offset);
byte[] chunk = Arrays.copyOfRange(body, offset, offset + chunkSize);
byte flags = 0;
if (offset + chunkSize >= body.length) {
flags = Http2Frame.FrameHeader.END_STREAM;
}
Http2Frame dataFrame = new Http2Frame.Builder()
.type(FrameType.DATA)
.streamId(stream.getStreamId())
.flags(flags)
.payload(chunk)
.build();
connectionHandler.sendFrame(dataFrame);
offset += chunkSize;
}
}
/**
* 等待响应
*/
private Http2Response awaitResponse(Http2Stream stream, long timeout)
throws InterruptedException, TimeoutException {
long deadline = System.currentTimeMillis() + timeout;
List<HeaderField> responseHeaders = new ArrayList<>();
ByteArrayOutputStream responseBody = new ByteArrayOutputStream();
while (System.currentTimeMillis() < deadline) {
Http2Frame frame = stream.pollReceivedFrame(100, TimeUnit.MILLISECONDS);
if (frame != null) {
switch (frame.getType()) {
case HEADERS:
// 解码响应头部
List<HeaderField> headers = hpackCodec.getDecoder()
.decodeHeaders(frame.getPayload());
responseHeaders.addAll(headers);
break;
case DATA:
// 收集响应体
responseBody.write(frame.getPayload());
if ((frame.getFlags() & Http2Frame.FrameHeader.END_STREAM) != 0) {
// 响应结束
return buildResponse(responseHeaders, responseBody.toByteArray());
}
break;
case RST_STREAM:
throw new IOException("流被重置: " + frame.getPayloadAsInt());
default:
// 忽略其他帧
break;
}
}
}
throw new TimeoutException("等待响应超时");
}
/**
* 并行发送多个请求(多路复用演示)
*/
public List<CompletableFuture<Http2Response>> sendParallelRequests(
List<Http2Request> requests) {
return requests.stream()
.map(this::sendRequest)
.collect(Collectors.toList());
}
/**
* 演示多路复用优势
*/
public void demonstrateMultiplexing() throws Exception {
List<Http2Request> requests = Arrays.asList(
createRequest("/api/users", "GET", null),
createRequest("/api/products", "GET", null),
createRequest("/api/orders", "POST", "{\"item\": \"book\"}"),
createRequest("/api/images/1.jpg", "GET", null)
);
// 设置不同的优先级
requests.get(0).setPriority(0, 256, true); // 最高优先级
requests.get(1).setPriority(0, 128, false);
requests.get(2).setPriority(1, 64, false); // 依赖第一个请求
requests.get(3).setPriority(0, 32, false); // 最低优先级
long startTime = System.currentTimeMillis();
// 并行发送所有请求
List<CompletableFuture<Http2Response>> futures = sendParallelRequests(requests);
// 等待所有响应
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get();
long endTime = System.currentTimeMillis();
System.out.printf("完成 %d 个请求,耗时 %dms\n",
requests.size(), endTime - startTime);
// 与HTTP/1.1对比
System.out.println("相比HTTP/1.1(需要4个TCP连接或串行),性能提升显著");
}
private Http2Request createRequest(String path, String method, String body) {
Http2Request request = new Http2Request();
List<HeaderField> headers = Arrays.asList(
new HeaderField(":method", method),
new HeaderField(":path", path),
new HeaderField(":scheme", "https"),
new HeaderField(":authority", "api.example.com"),
new HeaderField("user-agent", "Http2Client/1.0"),
new HeaderField("accept", "application/json")
);
request.setHeaders(headers);
if (body != null) {
request.setBody(body.getBytes(StandardCharsets.UTF_8));
}
return request;
}
}
五、性能优化与最佳实践
1. 帧打包优化
java
复制
下载
/**
* 帧打包器 - 优化帧发送
*/
public class FramePacker {
/**
* 打包多个小帧为一个TCP数据包(避免Nagle算法影响)
*/
public List<ByteBuffer> packFrames(List<Http2Frame> frames, int mtu) {
List<ByteBuffer> packets = new ArrayList<>();
ByteBuffer currentPacket = ByteBuffer.allocate(mtu);
for (Http2Frame frame : frames) {
byte[] frameBytes = frame.toBytes();
if (currentPacket.remaining() < frameBytes.length) {
// 当前包已满,发送并创建新包
currentPacket.flip();
packets.add(currentPacket);
currentPacket = ByteBuffer.allocate(mtu);
}
currentPacket.put(frameBytes);
}
if (currentPacket.position() > 0) {
currentPacket.flip();
packets.add(currentPacket);
}
return packets;
}
/**
* 头部压缩优化策略
*/
public static class HeaderCompressionStrategy {
// 预定义常用头部组合
private final Map<String, Integer> commonHeaderPatterns = new HashMap<>();
public HeaderCompressionStrategy() {
// 注册常见API请求头部模式
registerCommonPattern("api-get", Arrays.asList(
new HeaderField(":method", "GET"),
new HeaderField(":scheme", "https"),
new HeaderField("accept", "application/json"),
new HeaderField("user-agent", "Http2Client/1.0")
));
registerCommonPattern("api-post", Arrays.asList(
new HeaderField(":method", "POST"),
new HeaderField(":scheme", "https"),
new HeaderField("content-type", "application/json"),
new HeaderField("accept", "application/json")
));
}
private void registerCommonPattern(String name, List<HeaderField> headers) {
// 计算模式哈希
String hash = calculateHeaderHash(headers);
commonHeaderPatterns.put(hash, headers.size());
}
/**
* 智能选择编码策略
*/
public EncodingStrategy chooseStrategy(List<HeaderField> headers) {
String hash = calculateHeaderHash(headers);
if (commonHeaderPatterns.containsKey(hash)) {
return EncodingStrategy.PATTERN_BASED;
}
// 分析头部特征
long distinctNames = headers.stream()
.map(HeaderField::getName)
.distinct()
.count();
if (distinctNames < headers.size() * 0.3) {
// 大量重复的头部名称,适合名称索引
return EncodingStrategy.NAME_INDEXING;
}
return EncodingStrategy.DEFAULT;
}
}
}
2. 连接复用优化
java
复制
下载
/**
* HTTP/2连接池
*/
public class Http2ConnectionPool {
private final Map<String, List<Http2Client>> connections = new ConcurrentHashMap<>();
private final int maxConnectionsPerHost;
private final int maxStreamsPerConnection;
/**
* 获取连接(智能选择)
*/
public Http2Client acquireConnection(String host, int port) {
String key = host + ":" + port;
List<Http2Client> pool = connections.computeIfAbsent(key, k -> new ArrayList<>());
synchronized (pool) {
// 1. 查找可用的连接
for (Http2Client client : pool) {
if (client.isConnected() &&
client.getActiveStreams() < maxStreamsPerConnection) {
return client;
}
}
// 2. 创建新连接
if (pool.size() < maxConnectionsPerHost) {
try {
Http2Client newClient = new Http2Client(host, port);
pool.add(newClient);
return newClient;
} catch (IOException e) {
throw new RuntimeException("创建连接失败", e);
}
}
// 3. 等待连接可用
return waitForAvailableConnection(pool);
}
}
/**
* 连接健康检查
*/
private class HealthChecker extends Thread {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
for (List<Http2Client> pool : connections.values()) {
for (Http2Client client : pool) {
if (!isConnectionHealthy(client)) {
// 移除不健康的连接
pool.remove(client);
try {
client.close();
} catch (IOException e) {
// 忽略关闭异常
}
}
}
}
try {
Thread.sleep(30000); // 30秒检查一次
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
private boolean isConnectionHealthy(Http2Client client) {
try {
// 发送PING帧检查连接
return client.ping(1000); // 1秒超时
} catch (Exception e) {
return false;
}
}
}
}
六、监控与调试
1. 性能监控
java
复制
下载
/**
* HTTP/2连接监控器
*/
public class Http2Monitor {
private final MeterRegistry meterRegistry;
// 关键性能指标
private final Timer requestTimer;
private final DistributionSummary frameSizeDistribution;
private final Gauge activeStreamsGauge;
private final Counter compressionRatioCounter;
public Http2Monitor() {
this.meterRegistry = new SimpleMeterRegistry();
this.requestTimer = Timer.builder("http2.request.duration")
.publishPercentiles(0.5, 0.95, 0.99)
.register(meterRegistry);
this.frameSizeDistribution = DistributionSummary
.builder("http2.frame.size")
.baseUnit("bytes")
.register(meterRegistry);
this.activeStreamsGauge = Gauge.builder("http2.streams.active",
() -> getActiveStreamsCount())
.register(meterRegistry);
}
/**
* 记录请求性能
*/
public void recordRequest(long duration, int streamId, boolean success) {
requestTimer.record(duration, TimeUnit.MILLISECONDS);
Tags tags = Tags.of(
"stream", String.valueOf(streamId),
"success", String.valueOf(success)
);
meterRegistry.counter("http2.requests.total", tags).increment();
}
/**
* 记录头部压缩效果
*/
public void recordHeaderCompression(int originalSize, int compressedSize) {
double ratio = (double) compressedSize / originalSize;
meterRegistry.gauge("http2.header.compression.ratio", ratio);
if (ratio < 0.5) {
meterRegistry.counter("http2.header.compression.excellent").increment();
}
}
/**
* 生成性能报告
*/
public PerformanceReport generateReport() {
PerformanceReport report = new PerformanceReport();
// 收集关键指标
report.setAverageRequestLatency(requestTimer.mean(TimeUnit.MILLISECONDS));
report.setP95Latency(requestTimer.percentile(0.95, TimeUnit.MILLISECONDS));
report.setActiveStreams(activeStreamsGauge.value());
// 计算多路复用效率
double multiplexingEfficiency = calculateMultiplexingEfficiency();
report.setMultiplexingEfficiency(multiplexingEfficiency);
return report;
}
private double calculateMultiplexingEfficiency() {
// 计算相对于HTTP/1.1的性能提升
// 基于实际并行处理的请求数量
return 0.0; // 实际实现需要收集更多数据
}
}
2. 调试工具
java
复制
下载
/**
* HTTP/2帧分析器(调试用)
*/
public class Http2FrameAnalyzer {
/**
* 分析帧序列
*/
public void analyzeFrameSequence(List<Http2Frame> frames) {
System.out.println("=== HTTP/2 Frame Sequence Analysis ===");
System.out.printf("Total frames: %d\n", frames.size());
Map<FrameType, Integer> frameCounts = new EnumMap<>(FrameType.class);
int totalBytes = 0;
for (Http2Frame frame : frames) {
frameCounts.merge(frame.getType(), 1, Integer::sum);
totalBytes += frame.getLength();
// 检测潜在问题
detectPotentialIssues(frame);
}
// 输出统计信息
System.out.println("\nFrame Type Distribution:");
frameCounts.forEach((type, count) -> {
double percentage = (double) count / frames.size() * 100;
System.out.printf(" %-15s: %d (%.1f%%)\n", type, count, percentage);
});
System.out.printf("\nTotal bytes: %d\n", totalBytes);
System.out.printf("Average frame size: %.1f bytes\n",
(double) totalBytes / frames.size());
}
private void detectPotentialIssues(Http2Frame frame) {
// 检测队头阻塞
if (frame.getType() == FrameType.DATA && frame.getStreamId() == 1) {
// 检查流1是否阻塞了其他流
detectHeadOfLineBlocking(frame);
}
// 检测流量控制问题
if (frame.getType() == FrameType.WINDOW_UPDATE) {
analyzeFlowControl(frame);
}
// 检测优先级问题
if (frame.getType() == FrameType.PRIORITY) {
analyzePriority(frame);
}
}
/**
* 头部压缩效果分析
*/
public void analyzeHeaderCompression(List<HeaderField> original,
List<HeaderField> decoded) {
int originalSize = calculateHeaderSize(original);
int decodedSize = calculateHeaderSize(decoded);
System.out.println("\n=== Header Compression Analysis ===");
System.out.printf("Original size: %d bytes\n", originalSize);
System.out.printf("Decoded size: %d bytes\n", decodedSize);
System.out.printf("Compression ratio: %.1f%%\n",
(double) decodedSize / originalSize * 100);
// 分析哪些头部被压缩得最好
analyzeIndividualHeaders(original, decoded);
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
七、生产环境部署建议
1. 服务器配置(Nginx示例)
nginx
复制
下载
# nginx.conf
http {
# 启用HTTP/2
server {
listen 443 ssl http2;
# SSL配置(HTTP/2必须使用HTTPS)
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# HTTP/2优化参数
http2_max_field_size 16k;
http2_max_header_size 32k;
http2_max_requests 1000;
http2_max_concurrent_streams 128;
http2_recv_timeout 30s;
# HPACK表大小
http2_hpack_table_size 4096;
# 连接超时设置
http2_idle_timeout 3h;
http2_chunk_size 8k;
location / {
# 反向代理配置
proxy_http_version 1.1;
proxy_set_header Connection "";
# 保持与后端的长连接
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 重要的HTTP/2头部
proxy_set_header :authority $host;
proxy_set_header :scheme $scheme;
}
}
}
2. 客户端配置建议
java
复制
下载
/**
* 生产环境HTTP/2客户端配置
*/
public class ProductionHttp2ClientConfig {
public Http2Client createOptimizedClient() {
Http2Settings settings = new Http2Settings();
// 优化设置
settings.set(Setting.MAX_CONCURRENT_STREAMS, 100); // 最大并发流
settings.set(Setting.INITIAL_WINDOW_SIZE, 65535 * 2); // 增大初始窗口
settings.set(Setting.MAX_FRAME_SIZE, 16384); // 最大帧大小
settings.set(Setting.HEADER_TABLE_SIZE, 4096); // HPACK表大小
settings.set(Setting.ENABLE_PUSH, 0); // 禁用服务器推送
// 连接参数
settings.setConnectionTimeout(5000); // 5秒连接超时
settings.setReadTimeout(30000); // 30秒读取超时
settings.setWriteTimeout(30000); // 30秒写入超时
// 重试策略
settings.setMaxRetries(3);
settings.setRetryDelay(1000); // 1秒重试延迟
return new Http2Client(settings);
}
/**
* 监控配置
*/
public void setupMonitoring(Http2Client client) {
// 启用详细日志
client.enableFrameLogging(true);
client.enableHeaderCompressionLogging(true);
// 设置性能监控
Http2Monitor monitor = new Http2Monitor();
client.setMonitor(monitor);
// 定期生成报告
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> {
PerformanceReport report = monitor.generateReport();
logReport(report);
// 根据报告调整参数
if (report.getMultiplexingEfficiency() < 0.7) {
adjustClientParameters(client);
}
}, 1, 5, TimeUnit.MINUTES); // 每5分钟
}
}
八、性能对比与基准测试
HTTP/2 vs HTTP/1.1 性能对比
| 特性 | HTTP/1.1 | HTTP/2 | 性能提升 |
|---|---|---|---|
| 连接复用 | 需要多个TCP连接 | 单个TCP连接多路复用 | 减少TCP握手开销80% |
| 头部压缩 | 无压缩,重复发送 | HPACK压缩 | 减少头部大小60-90% |
| 请求优先级 | 无 | 优先级树 | 关键资源优先加载 |
| 服务器推送 | 无 | 支持 | 减少额外请求延迟 |
| 队头阻塞 | 存在(请求级) | 消除(帧级) | 并行处理提升300%+ |
典型场景性能数据
text
复制
下载
场景:加载包含100个资源的网页
HTTP/1.1(6个并行连接):
- TCP连接:6个
- 总请求数:100个
- 完成时间:4.2秒
- 头部开销:~40KB
HTTP/2(1个连接):
- TCP连接:1个
- 总请求数:100个
- 完成时间:1.8秒(提升57%)
- 头部开销:~8KB(减少80%)
关键指标:
- 首字节时间:减少40%
- 完全加载时间:减少57%
- 带宽使用:减少55%
总结
HTTP/2的多路复用和头部压缩通过以下关键技术实现:
-
多路复用:
-
基于流的帧交换机制
-
优先级树调度算法
-
流量控制窗口管理
-
避免队头阻塞
-
-
头部压缩:
-
HPACK静态/动态表
-
Huffman编码
-
增量索引机制
-
高效的字符串编码
-
-
性能优化:
-
智能帧打包
-
连接池管理
-
自适应参数调整
-
全面的监控体系
-
在实际部署中,HTTP/2通常能带来40-60%的性能提升,特别是在高延迟网络和高并发场景下效果显著。通过合理的配置和监控,可以最大化HTTP/2的性能优势。