【ETCD】[源码阅读]深度解析 EtcdServer 的 processInternalRaftRequestOnce 方法

在分布式系统中,etcd 的一致性与高效性得益于其强大的 Raft 协议模块。而 processInternalRaftRequestOnce 是 etcd 服务器处理内部 Raft 请求的核心方法之一。本文将从源码角度解析这个方法的逻辑流程,帮助读者更好地理解 etcd 的内部实现。

方法源码

go 复制代码
func (s *EtcdServer) processInternalRaftRequestOnce(ctx context.Context, r pb.InternalRaftRequest) (*applyResult, error) {
	ai := s.getAppliedIndex()
	ci := s.getCommittedIndex()
	if ci > ai+maxGapBetweenApplyAndCommitIndex {
		return nil, ErrTooManyRequests
	}

	r.Header = &pb.RequestHeader{
		ID: s.reqIDGen.Next(),
	}

	// check authinfo if it is not InternalAuthenticateRequest
	if r.Authenticate == nil {
		authInfo, err := s.AuthInfoFromCtx(ctx)
		if err != nil {
			return nil, err
		}
		if authInfo != nil {
			r.Header.Username = authInfo.Username
			r.Header.AuthRevision = authInfo.Revision
		}
	}

	data, err := r.Marshal()
	if err != nil {
		return nil, err
	}

	if len(data) > int(s.Cfg.MaxRequestBytes) {
		return nil, ErrRequestTooLarge
	}

	id := r.ID
	if id == 0 {
		id = r.Header.ID
	}
	ch := s.w.Register(id)

	cctx, cancel := context.WithTimeout(ctx, s.Cfg.ReqTimeout())
	defer cancel()

	start := time.Now()
	err = s.r.Propose(cctx, data)
	if err != nil {
		proposalsFailed.Inc()
		s.w.Trigger(id, nil) // GC wait
		return nil, err
	}
	proposalsPending.Inc()
	defer proposalsPending.Dec()

	select {
	case x := <-ch:
		return x.(*applyResult), nil
	case <-cctx.Done():
		proposalsFailed.Inc()
		s.w.Trigger(id, nil) // GC wait
		return nil, s.parseProposeCtxErr(cctx.Err(), start)
	case <-s.done:
		return nil, ErrStopped
	}
}

方法解析

1. 校验状态与索引

go 复制代码
ai := s.getAppliedIndex()
ci := s.getCommittedIndex()
if ci > ai+maxGapBetweenApplyAndCommitIndex {
	return nil, ErrTooManyRequests
}

getAppliedIndexgetCommittedIndex 分别获取当前节点的已应用索引和已提交索引。如果两者的差值过大,说明节点存在过多未应用的日志条目,可能导致性能问题,因此直接返回错误。

  • maxGapBetweenApplyAndCommitIndex:定义了允许的最大索引差距。
  • 防止机制:避免提交速度过快导致内存积压。

2. 生成请求头

go 复制代码
r.Header = &pb.RequestHeader{
	ID: s.reqIDGen.Next(),
}

每个请求分配一个唯一的 ID,以便后续跟踪和处理。

3. 身份验证检查

go 复制代码
if r.Authenticate == nil {
	authInfo, err := s.AuthInfoFromCtx(ctx)
	if err != nil {
		return nil, err
	}
	if authInfo != nil {
		r.Header.Username = authInfo.Username
		r.Header.AuthRevision = authInfo.Revision
	}
}
  • 目的:除认证请求外,其他请求需要验证用户身份。
  • 逻辑
    1. 调用 AuthInfoFromCtx 从上下文中提取用户身份。
    2. 将身份信息写入请求头,供后续处理。

4. 请求大小检查

go 复制代码
if len(data) > int(s.Cfg.MaxRequestBytes) {
	return nil, ErrRequestTooLarge
}
  • 目的:防止超大请求导致内存或网络问题。
  • 机制:检查请求序列化后的大小是否超过配置的最大限制。

5. 注册请求等待通道

go 复制代码
id := r.ID
if id == 0 {
	id = r.Header.ID
}
ch := s.w.Register(id)
  • 注册通道 :使用请求 IDs.w(wait 组件)中注册一个等待通道,用于异步获取结果。

6. 发起 Raft 提案

go 复制代码
cctx, cancel := context.WithTimeout(ctx, s.Cfg.ReqTimeout())
defer cancel()

start := time.Now()
err = s.r.Propose(cctx, data)
  • 发起提案 :调用 s.r.Propose 将请求数据交给 Raft 模块进行分布式一致性处理。
  • 超时控制 :通过 Context.WithTimeout 设置提案的最大执行时间,避免长期阻塞。
  • 错误处理:如果提案失败,增加失败计数,并触发通道清理。

7. 等待提案结果

go 复制代码
select {
case x := <-ch:
	return x.(*applyResult), nil
case <-cctx.Done():
	proposalsFailed.Inc()
	s.w.Trigger(id, nil) // GC wait
	return nil, s.parseProposeCtxErr(cctx.Err(), start)
case <-s.done:
	return nil, ErrStopped
}
  • 等待逻辑
    1. 通道 ch:正常返回应用结果。
    2. 上下文超时:处理超时错误,并清理等待通道。
    3. 服务关闭:直接返回停止错误。
  • 触发机制 :使用 Trigger 清理通道,避免资源泄露。

8. 性能指标统计

  • proposalsPending.Inc():增加当前挂起的提案计数。
  • proposalsFailed.Inc():统计失败提案次数。

关键逻辑总结

processInternalRaftRequestOnce 方法的核心逻辑可分为以下几个阶段:

  1. 预检查:检查索引状态、请求大小和用户认证。
  2. 请求处理:序列化请求并将其提交到 Raft 模块。
  3. 结果等待:通过通道或超时控制获取提案的处理结果。

流程图

超出限制 正常 超出限制 正常 正常 超时 服务关闭 收到内部请求 检查已应用索引与已提交索引 返回 ErrTooManyRequests 生成请求头并检查认证信息 检查请求大小 返回 ErrRequestTooLarge 注册等待通道 调用 Raft 提案 等待结果 返回提案结果 返回超时错误 返回 ErrStopped

zz总结

processInternalRaftRequestOnce 是 etcd 服务端处理内部 Raft 请求的重要方法,它结合了请求校验、身份认证、Raft 提案以及结果返回的完整逻辑链条。理解其实现,可以帮助我们深入掌握 etcd 的核心一致性协议和服务端处理流程。

相关推荐
行者游学9 小时前
ETCD 集群备份与恢复
数据库·etcd
行者游学9 小时前
ETCD 学习使用
数据库·学习·etcd
代码程序猿RIP9 小时前
【Etcd 】Etcd 详解以及安装教程
linux·数据库·etcd
丈剑走天涯6 天前
k8s etcd服务安装维护
云原生·etcd·devops·1024程序员节
孤独得猿11 天前
聊天室项目开发——etcd的安装和使用
linux·服务器·c++·etcd
thginWalker15 天前
etcd实战课-基础篇(上)
etcd
thginWalker15 天前
etcd实战课-实战篇(上)
etcd
敲上瘾15 天前
Linux系统C++开发环境搭建工具(二)—— etcd 使用指南
linux·c++·etcd
IT 小阿姨(数据库)17 天前
PostgreSQL etcd 集群介绍
运维·数据库·sql·postgresql·centos·etcd
thginWalker17 天前
etcd实战课-实战篇(下)
etcd