如何在多个 Symfony 实例之间共享应用程序缓存(共享缓存池)?

Posted

技术标签:

【中文标题】如何在多个 Symfony 实例之间共享应用程序缓存(共享缓存池)?【英文标题】:How to share the application cache between multiple Symfony instances (shared cache pool)? 【发布时间】:2021-02-25 07:06:39 【问题描述】:

我的 symfony 应用程序有多个实例在单独的 docker 容器中运行

我已经将我的app.cache 配置为使用redis:

framework:
    cache:
        app: cache.adapter.redis

我有同样的prefix_seed:

framework:
    cache:
        prefix_seed: 'dev'

结果我在 redis 中得到了这样的结果:

1605259288.470950 [0 172.18.0.28:55044] "MGET" "HnMEIyUlZ+:workers.restart_requested_timestamp"
1605259288.471680 [0 172.18.0.28:55044] "SET" "HnMEIyUlZ+:workers.restart_requested_timestamp" "d:1605259288.471522;"
1605259314.483389 [0 172.18.0.29:42884] "MGET" "8TMgMtnOAG:workers.restart_requested_timestamp"

从上面可以看出,2 个不同的实例试图通过相同的键 workers.restart_requested_timestamp 从 redis 中获取值,但即使使用相同的 prefix_seed,前缀也不同。

在这个例子中,我使用了 messenger 组件,我想通过stop-workers 命令(通过共享的 redis)阻止工作人员到处运行。但一般来说这和缓存配置有关。

如何克服这个问题并告诉两个应用程序使用同一个池?这是什么配置?

【问题讨论】:

@yivi 我发现它不会按预期工作 检查this。它可能会帮助你。 这能回答你的问题吗? Set Redis cache prefix key on Symfony 【参考方案1】:

在 symfony 框架包的配置定义中你可以找到:

    private function addCacheSection(ArrayNodeDefinition $rootNode)
    
        $rootNode
            ->children()
                ->arrayNode('cache')
                    ->info('Cache configuration')
                    ->addDefaultsIfNotSet()
                    ->fixXmlConfig('pool')
                    ->children()
                        ->scalarNode('prefix_seed')
                            ->info('Used to namespace cache keys when using several apps with the same shared backend')
                            ->example('my-application-name')
                        ->end()
                        ...

prefix_seed 正是您正在寻找的。 在此处查看文档:https://symfony.com/doc/current/reference/configuration/framework.html#prefix-seed

【讨论】:

我到处都有相同的prefix_seed,就我而言,它取决于环境和构建ID:prefix_seed: '%env(APP_ENV)%_%env(BUILD_ID)%' 但是如果你尝试在2个独立的容器中运行同一个symfony应用你会发现前缀会不一样。【参考方案2】:

最后,我找到了解决方案。有一个选项可以创建您自己的带有映射适配器的缓存池。这里的主要技巧是将带有namespace 的标签传递给您的适配器(名称也应该是cache.pool):

framework:
    cache:
        pools:
            cache.redis_shared_pool:
                adapter: app.cache_shared_redis_adapter

services:
    app.cache_shared_redis_adapter:
        parent: 'cache.adapter.redis'
        tags:
            -  name: 'cache.pool', namespace: 'shared' 

就是这样!你在 redis 中的所有键都将以shared: 为前缀。现在您应该将您的@cache.redis_shared_pool 传递给您想要的任何服务。

至于信使组件,我们应该覆盖服务(但我不确定这是最好的方式):

console.command.messenger_stop_workers:
    class: Symfony\Component\Messenger\Command\StopWorkersCommand
    arguments:
       $restartSignalCachePool: "@cache.redis_shared_pool"

messenger.listener.stop_worker_on_restart_signal_listener:
    class: Symfony\Component\Messenger\EventListener\StopWorkerOnRestartSignalListener
    arguments:
       $cachePool: "@cache.redis_shared_pool"

【讨论】:

如果您使用自动连接,您可以使用CacheInterface $cacheSharedPool,其中变量是 CamelCase 中池的名称。 Symfony 会根据您的池名称自动创建这些自动连接服务 ID。但是,我不确定您是否可以使用名称中带有句点的池。

以上是关于如何在多个 Symfony 实例之间共享应用程序缓存(共享缓存池)?的主要内容,如果未能解决你的问题,请参考以下文章

如何在没有完整项目的情况下测试共享的 Symfony 2 Bundle

如何在 symfony 中实例化多个 mqtt 客户端

如何在多个 OCUnit 测试用例之间共享代码?

如何在 Python 中使用 Managers() 在多个进程之间共享字符串?

是否可以共享 Symfony2 安装(一台服务器上的多个网站 [域])

一次使用多个共享内存实例