文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

Nacos基于HTTP协议的Raft日志复制实现深度解析

一、整体架构设计

1. Nacos Raft通信架构

Nacos集群
HTTP请求
HTTP请求
HTTP请求
HTTP请求
Leader节点
Follower节点
Follower节点
Follower节点
客户端

2. HTTP通信协议栈

Raft协议
序列化层
HTTP传输层
Netty网络层

二、日志复制流程实现

1. Leader端日志追加流程

ClientLeaderFollowersHTTP请求(PUT /nacos/v1/ns/service)封装为LogEntry本地日志追加并发HTTP请求HTTP响应提交日志应用状态机200 OK504 Timeoutalt[多数节点成功][未达多数]ClientLeaderFollowers

2. AppendEntries请求封装(源码分析)

// com.alibaba.nacos.core.distributed.raft.JRaftUtils
public class LogEntryProcessor {
    public void onApply(final Iterator iter) {
        // 处理日志提交
        while (iter.hasNext()) {
            Status status = null;
            final LogEntry log = (LogEntry) iter.next();
            final HttpRequest req = deserialize(log.getData()); // 反序列化请求
            
            // 应用状态机
            if (isWriteRequest(req)) {
                status = handleWriteRequest(req);
            } else {
                status = handleReadRequest(req);
            }
            
            // 返回客户端响应
            if (iter.done() != null) {
                iter.done().run(status);
            }
        }
    }
}

3. HTTP请求构造(源码分析)

// com.alibaba.nacos.core.distributed.raft.http.RaftHttpClient
public class RaftHttpClient {
    public AppendEntriesResponse sendAppendEntries(String serverId, AppendEntriesRequest request) {
        // 序列化请求
        String body = JSON.toJSONString(request);
        
        // 构造HTTP请求
        HttpRequest httpRequest = new DefaultHttpRequest(
            HttpVersion.HTTP_1_1, 
            HttpMethod.POST,
            "http://" + serverId + "/nacos/raft/appendEntries");
        httpRequest.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json");
        httpRequest.content().writeBytes(body.getBytes());
        
        // 发送请求
        HttpResponse response = httpClient.execute(httpRequest).get();
        
        // 解析响应
        AppendEntriesResponse raftResponse = 
            JSON.parseObject(response.content().toString(), AppendEntriesResponse.class);
        return raftResponse;
    }
}

三、Follower一致性检查

1. 日志匹配检查流程

接收AppendEntries请求
PrevLogIndex存在?
PrevLogTerm匹配?
返回false
追加新日志
返回false
更新CommitIndex
返回true

2. 日志应用实现(源码分析)

// com.alibaba.nacos.core.distributed.raft.NacosStateMachine
public class NacosStateMachine extends StateMachineAdapter {
    @Override
    public void onApply(final Iterator iter) {
        while (iter.hasNext()) {
            LogEntry log = iter.next();
            Operation op = Operation.parseFrom(log.getData().toByteArray());
            
            // 应用操作到状态机
            switch (op.getOpType()) {
                case PUT:
                    keyValueStore.put(op.getKey(), op.getValue());
                    break;
                case DELETE:
                    keyValueStore.delete(op.getKey());
                    break;
            }
            
            // 响应Leader
            if (iter.done() != null) {
                iter.done().run(Status.OK());
            }
        }
    }
}

四、HTTP协议优化策略

1. 批量日志复制

多个客户端请求
日志缓冲队列
批量封装
单次HTTP请求
减少网络开销

2. 流水线处理

// com.alibaba.nacos.core.distributed.raft.PipelineAppender
public class PipelineAppender {
    private final Map<String, CompletableFuture<Response>> pendingRequests = new ConcurrentHashMap<>();
    
    public void append(LogEntry log) {
        // 批量处理
        batch.add(log);
        if (batch.size() >= BATCH_SIZE) {
            sendBatch(batch);
            batch.clear();
        }
    }
    
    private void sendBatch(List<LogEntry> batch) {
        AppendEntriesRequest request = buildRequest(batch);
        CompletableFuture<Response> future = httpClient.sendAsync(request);
        future.whenComplete((resp, ex) -> {
            // 处理批量响应
            handleBatchResponse(batch, resp);
        });
    }
}

五、故障处理机制

1. 重试策略实现

// com.alibaba.nacos.core.distributed.raft.RaftRetryPolicy
public class RaftRetryPolicy {
    private static final int MAX_RETRIES = 3;
    private static final long[] BACKOFF = {100, 500, 1000}; // 毫秒
    
    public <T> T executeWithRetry(Callable<T> task) {
        int retry = 0;
        while (true) {
            try {
                return task.call();
            } catch (Exception e) {
                if (retry >= MAX_RETRIES) throw e;
                
                // 指数退避
                Thread.sleep(BACKOFF[retry]);
                retry++;
            }
        }
    }
}

2. 网络分区处理

LeaderFollower1Follower2AppendEntriesTimeout (网络中断)AppendEntriesSuccess检测到多数节点可达继续服务LeaderFollower1Follower2

六、性能优化技术

1. 多路复用连接

// com.alibaba.nacos.core.distributed.raft.http.HttpConnectionPool
public class HttpConnectionPool {
    private static final Map<String, Channel> connections = new ConcurrentHashMap<>();
    
    public Channel getChannel(String serverId) {
        return connections.computeIfAbsent(serverId, id -> {
            // 创建新连接
            Bootstrap b = new Bootstrap();
            b.group(eventLoopGroup);
            b.channel(NioSocketChannel.class);
            b.handler(new RaftClientHandler());
            return b.connect(serverId, 8848).sync().channel();
        });
    }
}

2. 零拷贝序列化

public class LogEntryEncoder implements MessageToMessageEncoder<LogEntry> {
    @Override
    protected void encode(ChannelHandlerContext ctx, LogEntry msg, List<Object> out) {
        // 直接使用ByteBuf避免内存拷贝
        ByteBuf buf = Unpooled.wrappedBuffer(msg.getData().toByteArray());
        out.add(buf);
    }
}

七、与标准RPC实现对比

特性HTTP实现gRPC实现
协议开销较高较低
开发难度
跨语言支持优秀优秀
性能中等
调试便利性非常好中等
适用场景内部通信高性能场景

八、生产实践建议

1. 关键配置项

# application.properties
raft.http.max_connections=100
raft.http.io_threads=4
raft.batch_size=64
raft.log.retention.hours=72

2. 监控指标

# 日志复制延迟
curl http://nacos-node:8848/nacos/raft/metrics | grep replicate_latency

# 关键指标
• raft_commit_latency
• raft_apply_latency
• http_request_duration
• batch_queue_size

3. 性能优化配置

// 启动参数优化
java -jar nacos-server.jar \
  -Draft.http.ioRatio=70 \
  -Draft.log.sync=true \
  -Draft.max_append_buffer_size=65536

总结:HTTP实现Raft的设计哲学

  1. 简单性原则

    降低
    降低
    开发复杂度
    HTTP协议
    调试难度
  2. 可观测性优势

    // 直接查看HTTP日志即可调试
    [2023-07-20 10:00:00] POST /raft/appendEntries
    Request: {"term":5,"prevLogIndex":100,"entries":[...]}
    Response: {"success":true,"term":5}
    
  3. 弹性扩展能力

    HTTP
    HTTP
    HTTP
    HTTP
    HTTP
    Leader
    Follower
    Follower
    Follower
    Observer
    客户端
  4. 实现关键点

    • 使用Netty实现高效HTTP服务器
    • 批量日志合并减少请求数
    • 流水线处理提高并发度
    • 异步响应避免阻塞

Nacos通过HTTP实现Raft协议,虽然在性能上略逊于gRPC等二进制协议,但获得了极佳的可维护性和可观测性,是架构权衡的典范实践。

posted @ 2025-09-14 11:51  NeoLshu  阅读(4)  评论(0)    收藏  举报  来源