PHP中Promise链式调用解析:Redis Sentinel地址获取代码疑问
解析Redis Sentinel客户端的Promise链式逻辑
我在GitHub PR里看到一段给Redis客户端添加Redis Sentinel支持的代码(Redis Sentinel是用于监控多个Redis实例状态并返回主节点IP的服务)。我对PHP不太熟悉,虽然代码能正常运行,但无法理解它的逻辑:
public function masterAddress(): PromiseInterface { $chain = reject(new \RuntimeException('Initial reject promise')); foreach ($this->urls as $url) { $chain = $chain->then(function ($masterUrl) { return $masterUrl; }, function () use ($url) { return $this->onError($url); }); } return $chain; }
代码说明
$this->urls是不同Redis Sentinel的地址列表- 该方法返回的Promise最终会得到主节点URL
- 调用
$this->masterAddress()即可获取主节点URL
我的疑惑:这段代码为什么能返回URL?看起来好像没用到$url变量,链式调用难道不会一直处于拒绝状态吗?
逻辑拆解
这段代码是通过Promise链式调用实现的Sentinel地址轮询逻辑,逐个尝试Sentinel直到拿到主节点URL,具体步骤如下:
- 初始状态:
$chain被初始化为一个已拒绝的Promise,抛出RuntimeException。这是故意为之,目的是触发第一个错误回调,启动轮询流程。 - 遍历轮询:对每个Sentinel地址
$url,给当前$chain追加then回调:- 第一个回调(成功分支):如果前面的Promise已经成功拿到主节点URL,直接返回这个URL,后续的Sentinel尝试会被“短路”,不再执行。
- 第二个回调(错误分支):如果前面的Promise失败(比如上一个Sentinel请求超时/无响应),就通过
use ($url)绑定当前的Sentinel地址,调用$this->onError($url)——这个方法应该是向该Sentinel发送请求,获取主节点URL的逻辑。如果onError成功拿到URL,返回的Promise就会变成成功状态,后续的then会走成功分支;如果还是失败,就继续触发下一个Sentinel的错误回调。
- 最终结果:只要有一个Sentinel成功返回主节点URL,整个链式Promise就会以成功状态返回该URL;如果所有Sentinel都失败,最终Promise才会处于拒绝状态。
解答你的疑惑
- 关于
$url的使用:代码里用了PHP的闭包use ($url)语法,把当前循环的Sentinel地址绑定到错误回调函数里了,每个循环的错误回调都对应一个不同的$url,并不是没用到。 - 链式调用不会一直拒绝:初始是拒绝状态,但第一个错误回调会尝试第一个Sentinel,如果成功,Promise就转为成功状态,后续的
then会走成功分支直接返回结果;只有当前Sentinel尝试失败时,才会继续触发下一个错误回调,直到找到可用的Sentinel。
内容的提问来源于stack exchange,提问作者Quentin Lerebours
相关产品推荐
相关产品推荐

