【问题标题】:Laravel + predis + Redis cluster - MOVED / no connection to 127.0.0.1:6379Laravel + predis + Redis 集群 - 已移动 / 未连接到 127.0.0.1:6379
【发布时间】:2017-04-26 17:50:33
【问题描述】:

我有一个带有 redis 的 laravel (5.3) 应用程序,用于会话(使用 predis)。只要我使用单个 redis 节点(使用 config/database.php 中的默认方法),一切正常。一旦我切换到 Redis 集群,尽管我开始在 50% 的时间里遇到 MOVED 错误(根据谷歌搜索,我知道这应该由 predis 管理,但不知何故不是)。

我尝试将集群参数更改为 true,但随后出现奇怪的错误

No connection could be made because the target machine actively refused it. [tcp://127.0.0.1:6379] 

虽然我使用的redis集群是部署在Azure中(通过.env文件配置的),并且在使用单节点时参数接受没有任何问题。

配置

这是我的 laravel 配置(如前所述,这是标准默认配置)

'redis' => [

        'client' => 'predis',
        'cluster' => false,

        'default' => [
            'host' => env('REDIS_HOST', 'localhost'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 0,
        ],

    ],

对于 Redis,我使用 Azure Redis Cache Cluster Premium P1,2 个分片(如 here 所述)。

更新 2

到目前为止,我还尝试了以下配置变体:

  1. 将集群设置为 true
  2. 将集群设置为 redis
  3. 添加默认值 -> 集群设置为 redis
  4. 添加默认值 -> 选项设置为 array('cluster', 'redis')

我一直收到 MOVED 错误...

我的Redis版本是3.2,predis/predis包1.1.1

predis 1.1+ 的工作配置

'redis' => [
        'cluster' => true,

        'default' => [
            'host' => env('REDIS_HOST', 'localhost'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 0,
        ] ,
        'options' => [
            'cluster' => 'redis',
             'parameters' => ['password' => env('REDIS_PASSWORD', null)],
        ],
    ],

非常感谢您的所有帮助:)

【问题讨论】:

  • 我们可以看看你的配置吗?这是由 Laravel 通过 Predis 自动处理的。调用 Predis\Client 时,会传递一个参数,例如 ['client' => 'redis']。这会自动处理 -ASK-MOVED 响应。
  • 我已经添加了配置。
  • 看起来选项应该在 default 键之外,并且数组应该是 key=>value (你有两个值)。所以,我会说试试:'redis' => [ 'cluster' => true, 'default' => [ /*your config*/ ], 'options' => [ 'cluster' => 'redis' ], ]。让我们知道。

标签: laravel redis azure-caching


【解决方案1】:

TL;DR:

  • 'cluster' => true 应该为 true,以创建一个处理多个节点的聚合客户端。
  • 'options' => ['cluster' => 'redis'] 需要作为default 的兄弟(不是子级)添加到配置中,以便告诉 Predis 处理 Azure 提供的服务器端集群。
  • 如果在服务器端集群中使用身份验证,则需要'options' => [ 'cluster' => 'redis', 'parameters' => ['password' => env('REDIS_PASSWORD', null)], ] 来对新发现的集群节点进行身份验证。

全文

在redis配置中,可以设置多个redis实例的多个连接。 cluster 选项告诉 Laravel 如何处理这些定义的多个连接。

如果 cluster 设置为 false,Laravel 将为每个连接创建单独的 \Predis\Client 实例。每个连接都可以单独访问,并且与另一个连接没有任何关系。

如果 cluster 设置为 true,Laravel 将使用所有定义的连接创建一个聚合的 \Predis\Client 实例。没有其他配置,这是一种“假”集群。它使用客户端分片来分配密钥空间,并且可能需要外部监控和维护以确保适当的密钥负载平衡。

但是,您遇到的问题是 Azure 实现(大概)一个真正的服务器端 Redis 集群,该集群处理密钥空间的自动分片。在这种情况下,节点相互了解并相互交谈,并且可以上下移动。这就是MOVEDASK 响应的来源。

Predis 库可以自动处理这些响应,但前提是您告诉它它需要这样做。在这种情况下,您需要告诉 Predis 客户端它需要处理集群,这由 Laravel 通过 redis 配置上的 options 数组完成。

redis 配置中,options 键应该是您的连接的兄弟(即default),而不是子连接。此外,选项应指定为key => value 对。

因此,您的配置应如下所示:

'redis' => [
    'cluster' => true,

    'default' => [
        'host' => env('REDIS_HOST', 'localhost'),
        'password' => env('REDIS_PASSWORD', null),
        'port' => env('REDIS_PORT', 6379),
        'database' => 0,
    ],

    'options' => [
        'cluster' => 'redis',
    ],
],

redis 配置下的 cluster 键将告诉 Laravel 创建一个可以处理多个节点的聚合 Predis\Client 实例,cluster 数组下的 cluster 键将告诉该实例它需要处理服务器端集群,而不是客户端集群。

授权

原始连接参数(包括身份验证)不与通过-MOVED-ASK 响应发现的新节点的连接共享。因此,您之前从 -MOVED 响应中得到的任何错误现在都将转换为 NOAUTH 错误。但是,服务器端'cluster' 配置允许'parameters' 同级,它定义了用于新发现的节点的参数列表。在这里您可以将您的身份验证参数用于新节点。

我相信这看起来像:

'redis' => [
    'cluster' => true,

    'default' => [
        'host' => env('REDIS_HOST', 'localhost'),
        'password' => env('REDIS_PASSWORD', null),
        'port' => env('REDIS_PORT', 6379),
        'database' => 0,
    ],

    'options' => [
        'cluster' => 'redis',
        'parameters' => ['password' => env('REDIS_PASSWORD', null)],
    ],
],

公平警告,这是我刚刚从研究和代码挖掘中获得的所有信息。虽然我在 Laravel 中使用过 Redis,但我还没有使用过服务器端集群,所以这仍然可能行不通。

我在调查时发现了一些有用的信息:

讨论连接到 redis 集群的 Predis 问题:
https://github.com/nrk/predis/issues/259#issuecomment-117339028

您似乎没有将 Predis 配置为使用 redis-cluster,而是将它与普通的旧客户端分片逻辑一起使用(这也是默认行为)。您应该使用值 redis 配置客户端设置选项集群,以让客户端知道它必须与 redis-cluster 一起玩。快速示例:

$client = new Predis\Client([$node1, $node2, ...], ['cluster' => 'redis']);

这样做将使客户端能够自动处理来自 Redis 节点的 -MOVED 或 -ASK 响应。

MS 文章讨论 redis 缓存上的集群:
https://docs.microsoft.com/en-us/azure/redis-cache/cache-how-to-premium-clustering#how-do-i-connect-to-my-cache-when-clustering-is-enabled

您可以使用在连接到未启用集群的缓存时使用的相同端点、端口和键来连接到您的缓存。 Redis 在后端管理集群,因此您不必从客户端进行管理。

用于创建Predis\Client 实例的 Laravel 代码:
https://github.com/laravel/framework/blob/v5.3.28/src/Illuminate/Redis/Database.php#L25-L66

【讨论】:

  • 谢谢,至少现在我看到了不同的错误消息:) 它以“池中没有可用的连接”开头,然后变成“Client.php 第 370 行中的 ServerException:需要 NOAUTH 身份验证。 "大约有 50% 的时间发生。看起来虽然 predis 处理了 MOVED 消息,但它没有在新节点上进行身份验证。任何建议如何使这项工作? :)
  • @LechMigdal 我已经更新了我的答案以包含处理身份验证的建议。
  • 谢谢 :) 对 predis 1.1.1 有什么建议吗?我看到 setDefaltParameters 不再存在(据我所见,在 1.0.3 中存在)。
  • 好的,我找到了丢失的小块,而不是设置自定义函数,从1.1.0开始你可以执行以下操作:'options' => ['cluster' => 'redis', 'parameters ' => ['password' => env('REDIS_PASSWORD', null)], ],非常感谢您的帮助!
  • @LechMigdal 太棒了。感谢更新。我已更新我的答案以反映新信息。很高兴你成功了!
【解决方案2】:

相关:Laravel + Redis Cache via SSL?

我在这里回答过:https://stackoverflow.com/a/48876398/663058

相关详情如下:

如果您有集群 TLS,那么您将需要以下配置(使用 AWS Elasticache 测试):

'redis' => [
        'client' => 'predis',
        'cluster' => env('REDIS_CLUSTER', false),

        // Note! for single redis nodes, the default is defined here.
        // keeping it here for clusters will actually prevent the cluster config
        // from being used, it'll assume single node only.
        //'default' => [
        //    ...
        //],

        // #pro-tip, you can use the Cluster config even for single instances!
        'clusters' => [
            'default' => [
                [
                    'scheme'   => env('REDIS_SCHEME', 'tcp'),
                    'host'     => env('REDIS_HOST', 'localhost'),
                    'password' => env('REDIS_PASSWORD', null),
                    'port'     => env('REDIS_PORT', 6379),
                    'database' => env('REDIS_DATABASE', 0),
                ],
            ],
            'options' => [ // Clustering specific options
                'cluster' => 'redis', // This tells Redis Client lib to follow redirects (from cluster)
            ]
        ],
        'options' => [
            'parameters' => [ // Parameters provide defaults for the Connection Factory
                'password' => env('REDIS_PASSWORD', null), // Redirects need PW for the other nodes
                'scheme'   => env('REDIS_SCHEME', 'tcp'),  // Redirects also must match scheme
            ],
            'ssl'    => ['verify_peer' => false], // Since we dont have TLS cert to verify
        ]
    ]

上面的解释:

  • 'client' => 'predis':这指定了要使用的 PHP 库 Redis 驱动程序 (predis)。
  • 'cluster' => 'redis':这告诉 Predis 假设服务器端集群。这只是意味着“跟随重定向”(例如-MOVED 响应)。当与集群一起运行时,节点将以-MOVED 响应您必须请求特定密钥的节点。
    • 如果您没有在 Redis 集群中启用此功能,Laravel 将抛出 -MOVED 异常 1/n 次,n 是 Redis 中的节点数集群(它会很幸运并每隔一段时间询问正确的节点)
  • 'clusters' => [...]:指定节点列表,但仅设置“默认”并将其指向 AWS 'Configuration endpoint' 将使其动态找到任何/所有其他节点(推荐用于 Elasticache,因为您不知道节点何时出现'或去')。
  • 'options':对于 Laravel,可以在顶层、集群级别和节点选项中指定。 (它们在被传递给 Predis 之前在 Illuminate 中组合)
  • 'parameters':这些“覆盖”了 Predis 用于新连接的默认连接设置/假设。由于我们为“默认”连接明确设置了它们,因此不使用它们。但对于集群设置,它们至关重要。 “主”节点可能会发回重定向 (-MOVED),除非为 passwordscheme 设置参数,否则它将采用默认值,并且与新节点的新连接将失败。

【讨论】:

    【解决方案3】:

    对于 AWS elasticcache redis 集群,上面的配置不起作用,但是下面的配置对我有用。文档中也提到:https://laravel.com/docs/5.4/redis#configuration

    'redis' => [
        'client' => 'predis',
        'options' => [
            'cluster' => 'redis',
        ],
        'clusters' => [
            'default' => [
                [
                    'host' => env('REDIS_HOST', 'localhost'),
                    'password' => env('REDIS_PASSWORD', null),
                    'port' => env('REDIS_PORT', 6379),
                    'database' => 0,
                ],
            ],
        ],
    ],
    

    【讨论】:

      【解决方案4】:

      在应用了很多建议后,我终于找到了使用 phpredis 而不是 Redis 的解决方案。使用以下代码:

          'redis' => [
      
          'client' => 'phpredis',
          'clusters' => [
              'default' => [
                  [
                      'host' => env('REDIS_HOST', 'localhost'),
                      'password' => env('REDIS_PASSWORD', null),
                      'port' => env('REDIS_PORT', 6379),
                      'database' => 0,
                  ],
              ],
          ]
      
      ],
      

      使用官方文档更好地理解Laravel Redis with clusters

      【讨论】:

        【解决方案5】:

        请参考[https://laravel.com/docs/5.5/redis]。

        确保您拥有正确的库

        composer require predis/predis
        

        在 config/database.php 以及 config/queue.php [如果你的队列也使用集群 reddis]

        'redis' => [
        
            'client' => 'predis',
            'options' => [
                'cluster' => 'redis',
            ],
            'clusters' => [
                'default' => [
                    [
                        'host' => env('REDIS_HOST', 'localhost'),
                        'password' => env('REDIS_PASSWORD', null),
                        'port' => env('REDIS_PORT', 6379),
                        'database' => 0,
                    ],
                ],
            ],
        
        ]
        

        【讨论】:

          【解决方案6】:

          这对我有用:

          'redis' => [
              'client' => 'predis',
              'cluster' => true,
              'options' => [
                  'cluster' => 'redis',
                  'parameters' => [
                      'host' => env('REDIS_DEFAULT_HOST', '127.0.01'),
                      'password' => env('REDIS_PASSWORD', null),
                      'port' => env('REDIS_DEFAULT_PORT', 6379),
                      'database' => 0,
                      ],
                  ],
              'clusters' => [
                   'default' => [
                      'host' => env('REDIS_DEFAULT_HOST', '127.0.01'),
                      'password' => env('REDIS_PASSWORD', null),
                      'port' => env('REDIS_DEFAULT_PORT', 6379),
                      'database' => 0,
                  ],
                  'jobs' => [
                      'host' => env('REDIS_JOBS_HOST', '127.0.01'),
                      'password' => env('REDIS_PASSWORD', null),
                      'port' => env('REDIS_JOBS_PORT', 6379),
                      'database' => 0,
                  ],
                  'content' => [
                      'host' => env('REDIS_CONTENT_HOST', '127.0.01'),
                      'password' => env('REDIS_PASSWORD', null),
                      'port' => env('REDIS_CONTENT_PORT', 6379),
                      'database' => 0,
                  ],
                  'options' => [
                      'cluster' => 'redis'
                  ],
              ]
          ]
          

          要了解我是如何到达那里的,请查看我的回答 here

          【讨论】:

            【解决方案7】:

            这个结果出现在 Google for predis+redis-cluster (no laravel) 中。我和我的团队在这个问题上苦苦挣扎了几个小时,所以我把这个答案放在这里,供那些在尝试直接连接到 Redis 集群时发现此页面出现错误的人:

            $redis = new Predis\Client(
               ['tcp://127.0.0.1:6379'],
               ['cluster' => 'redis']
            );
            

            【讨论】:

              猜你喜欢
              • 2020-02-25
              • 2023-03-18
              • 2017-05-16
              • 2017-07-06
              • 2020-08-27
              • 1970-01-01
              • 1970-01-01
              • 2021-06-25
              • 2020-12-25
              相关资源
              最近更新 更多