目录
- [一、etcd里存的是什么(核心:Namespace JSON配置)](#一、etcd里存的是什么(核心:Namespace JSON配置))
- 二、配置是怎么更新的(两阶段热加载,面试高频)
- 三、Mermaid时序图
- 四、一句话总结
一、etcd里存的是什么(核心:Namespace JSON配置)
Gaea 分两类配置:
- Proxy本地启动配置(ini):etcd地址、日志、监听端口,不放在etcd,很少改
- 动态租户配置(存在etcd,JSON):每个业务租户对应一个 namespace ,这是etcd里主体内容
etcd key 前缀一般类似:
/gaea/{cluster_name}/namespace/{ns_name}
JSON里面包含这些信息:
- 账号权限:该namespace下MySQL账号、密码、白名单、连接限流
- Slice 后端实例信息:
- 一组MySQL(主库、从库列表、地址端口)
- 读写分离策略:读走从库、主从权重、延迟阈值
- 分片路由规则(重点)
- 路由模式: kingshard / mycat 两种
- 分库分表字段、分片数量、分片算法(hash/range)
- 表分布:哪张表落在哪个slice
- SQL黑白名单、超时、事务限制、连接池参数
- 状态标记:版本号、prepare/commit标记(用于两阶段热更)
补充:etcd还存少量agent心跳、实例健康状态,主体是namespace配置。
二、配置是怎么更新的(两阶段热加载,面试高频)
Gaea不是简单watch到key变更就直接切,是CC控制的2PC式热更新,保证集群所有proxy配置一致性,避免一部分proxy新配置、一部分旧配置(脑路由)
完整流程
- DBA 在 gaea-web 页面修改配置(改分片、加从库、改路由)
- web 调用 gaea-cc ,cc 先写入新版本namespace配置到etcd(新version)
- Prepare阶段(预加载)
cc 向集群所有gaea-proxy发prepare指令;
proxy 收到后,从etcd拉取新版本配置,新建一套独立路由、连接池资源,不替换正在跑的旧配置;
全部proxy prepare成功,才往下走;任意一个失败,直接回滚,业务无影响。 - Commit阶段(切换生效)
cc 下发commit信号;
proxy 原子切换路由指针:新请求走新namespace配置;老会话、老连接继续跑完旧版本,平滑过渡,不踢连接。 - 旧资源慢慢GC释放,更新完成。
两种监听机制区分开
- ✅ 控制面:cc主动推送prepare/commit(主流程,保证一致性)
- ✅ proxy同时持续watch etcd key:兜底。proxy重启、新proxy上线时,通过watch主动拉取当前最新namespace配置。
❗ 关键点:不是etcd watch事件直接触发切换,watch只负责感知版本、拉数据;真正切换时机由cc统一调度,两阶段提交,保证全集群proxy配置一致。
三、Mermaid时序图
gaea-proxy-2 gaea-proxy-1 Etcd gaea-cc gaea-web DBA gaea-proxy-2 gaea-proxy-1 Etcd gaea-cc gaea-web DBA #mermaid-svg-6O2QvObqjsApAIke{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6O2QvObqjsApAIke .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6O2QvObqjsApAIke .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6O2QvObqjsApAIke .error-icon{fill:#552222;}#mermaid-svg-6O2QvObqjsApAIke .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6O2QvObqjsApAIke .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6O2QvObqjsApAIke .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6O2QvObqjsApAIke .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6O2QvObqjsApAIke .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6O2QvObqjsApAIke .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6O2QvObqjsApAIke .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6O2QvObqjsApAIke .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6O2QvObqjsApAIke .marker.cross{stroke:#333333;}#mermaid-svg-6O2QvObqjsApAIke svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6O2QvObqjsApAIke p{margin:0;}#mermaid-svg-6O2QvObqjsApAIke .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6O2QvObqjsApAIke text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-6O2QvObqjsApAIke .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-6O2QvObqjsApAIke .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-6O2QvObqjsApAIke .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-6O2QvObqjsApAIke .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-6O2QvObqjsApAIke #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-6O2QvObqjsApAIke .sequenceNumber{fill:white;}#mermaid-svg-6O2QvObqjsApAIke #sequencenumber{fill:#333;}#mermaid-svg-6O2QvObqjsApAIke #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-6O2QvObqjsApAIke .messageText{fill:#333;stroke:none;}#mermaid-svg-6O2QvObqjsApAIke .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6O2QvObqjsApAIke .labelText,#mermaid-svg-6O2QvObqjsApAIke .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-6O2QvObqjsApAIke .loopText,#mermaid-svg-6O2QvObqjsApAIke .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-6O2QvObqjsApAIke .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-6O2QvObqjsApAIke .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-6O2QvObqjsApAIke .noteText,#mermaid-svg-6O2QvObqjsApAIke .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-6O2QvObqjsApAIke .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6O2QvObqjsApAIke .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6O2QvObqjsApAIke .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6O2QvObqjsApAIke .actorPopupMenu{position:absolute;}#mermaid-svg-6O2QvObqjsApAIke .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-6O2QvObqjsApAIke .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6O2QvObqjsApAIke .actor-man circle,#mermaid-svg-6O2QvObqjsApAIke line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-6O2QvObqjsApAIke :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 新请求走新配置;已有会话继续旧配置 修改namespace配置 提交变更 写入新版本namespace JSON prepare(加载新版配置) prepare(加载新版配置) prepare ok prepare ok commit(原子切换路由) commit(原子切换路由) watch监听,兜底同步 watch监听,兜底同步
四、一句话总结
etcd 存储每个业务namespace的JSON配置:后端MySQL实例、读写分离、分库分表路由、账号限流;
更新由gaea-cc做两阶段热更,先全部proxy预加载、全部成功再统一commit切换;proxy同时watch etcd,用于新节点启动和故障后自动同步配置,全程不需要重启proxy。