Symfony 4、Doctrine 和 AWS RDS 只读副本的使用

Symfony 4, Doctrine and AWS RDS read replica usage

我即将开始将 RDS 与主 read/write 和从属只读设置一起使用。

我读过 Doctrine MasterSlaveConnection 类型。

但是,我会创建一些端点,我想使用只读副本(我的大部分 GET 端点),有些端点需要写入数据(PUT、PATCH、DELETE 端点)。

我也在使用 API 平台。

在 Symfony 4 和 Doctrine 2 中实现这一目标的最佳方法是什么?

我过去所做的只是使用不同的连接。

类似于:

doctrine:
  dbal:
    default_connection: default
    connections:
      default:
        # This is your Master
        url: '%env(DATABASE_URL)%'
        driver: 'pdo_mysql'
        server_version: '5.7'
        charset: utf8mb4
      slave:
        # This would be the slave
        url: '%env(DATABASE_SLAVE_URL)%'
        driver: 'pdo_mysql'
        server_version: '5.7'
        charset: utf8mb4

  orm:
    default_entity_manager: default
    entity_managers:
      default:
        connection: default
        mappings:
          Main:
            is_bundle: false
            type: annotation
            dir: '%kernel.project_dir%/src/Entity'
            prefix: 'App\Entity'
            alias: Main
      slave:
        connection: slave
        mappings:
          Main:
            is_bundle: false
            type: annotation
            dir: '%kernel.project_dir%/src/Entity'
            prefix: 'App\Entity'
            alias: Main

https://symfony.com/doc/current/doctrine/multiple_entity_managers.html

然后在您的控制器或业务逻辑中,您可以选择使用默认实体管理器:

// Controller
$this->getDoctrine()->getEntityManager();

或者你可以获取slave连接:

// Controller
$this->getDoctrine()->getEntityManager('slave');

如果您需要它只对所有请求起作用,而不必为所有内容创建特殊操作,那么您最好的选择是装饰 Collection 和 Item DataProviders 以实现学说。

https://symfony.com/doc/current/service_container/service_decoration.html

https://github.com/api-platform/core/blob/master/src/Bridge/Doctrine/Orm/CollectionDataProvider.php

https://github.com/api-platform/core/blob/master/src/Bridge/Doctrine/Orm/ItemDataProvider.php

所以基本上你需要根据 $opperationName 更改选择的经理,例如:

if($opperationName === 'GET'){
    $manager = $this->managerRegistry->getManager('slave');
} else {
    $manager = $this->managerRegistry->getManager();
}

感谢@Chase 提供的解决方案。你成就了我的一天。尽管它在 'dev' 环境中对我有用,但在切换到 'prod' 时我遇到了问题。我收到无法找到实体的错误。解决方案来自 this post - 感谢@xabbuh。基本上我必须将 default_entity_manager: name_of_default_em 添加到 doctrine.yml。这是代码的副本:

# config/packages/prod/doctrine.yaml
doctrine:
    orm:
        default_entity_manager: BOE <- add this line to let know prod about default em
        auto_generate_proxy_classes: false
        metadata_cache_driver:
            type: service
            id: doctrine.system_cache_provider
        query_cache_driver:
            type: service
            id: doctrine.system_cache_provider
        result_cache_driver:
            type: service
            id: doctrine.result_cache_provider

# ...

您实际上不需要设置多个实体管理器,也不是更可取的,因为处理一个具有多个实体管理器的实体很困难。

使用 Doctrine 2.2,您可以直接从配置中设置 slaves/replicas,无需额外的实体管理器:

在此处查看配置参考: https://www.doctrine-project.org/projects/doctrine-bundle/en/2.2/configuration.html#configuration-overview

示例:

doctrine:
    dbal:
        default_connection: default
        connections:
            default:
                dbname: '%env(DATABASE_DBNAME)%'
                user: '%env(DATABASE_USER)%'
                password: '%env(DATABASE_PASSWORD)%'
                host: '%env(DATABASE_HOST)%'
                driver: 'pdo_mysql'
                server_version: '5.7'
                charset: utf8mb4
                default_table_options:
                    charset: utf8mb4
                    collate: utf8mb4_unicode_ci
                slaves:
                    ro_replica:
                        dbname: '%env(REPLICA_DBNAME)%'
                        user: '%env(REPLICA_USER)%'
                        password: '%env(REPLICA_PASSWORD)%'
                        host: '%env(REPLICA_HOST)%'
                        charset: utf8mb4