Symfony上的批量请求

时间:2016-11-23 14:34:45

标签: symfony

我正在尝试在其图表api上重现facebook batch requests函数的行为。

所以我认为最简单的解决方案是在控制器上向我的应用程序发出几个请求,如:

public function batchAction (Request $request)
{
    $requests = $request->all();
    $responses = [];

    foreach ($requests as $req) {
        $response = $this->get('some_http_client')
            ->request($req['method'],$req['relative_url'],$req['options']);

        $responses[] = [
            'method' => $req['method'],
            'url' => $req['url'],
            'code' => $response->getCode(),
            'headers' => $response->getHeaders(),
            'body' => $response->getContent()
        ]
    }

    return new JsonResponse($responses)
}

因此,使用此解决方案,我认为我的功能测试将是绿色的。

但是,我填写像初始化服务容器X次可能会使应用程序更慢。因为对于每个请求,每个bundle都是构建的,所以每次都会重建服务容器......

您是否看到了我的问题的其他解决方案?

换句话说,我是否需要向服务器发出完整的新HTTP请求以获取应用程序中其他控制器的响应?

提前感谢您的建议!

2 个答案:

答案 0 :(得分:5)

内部Symfony使用http_kernel组件处理请求。因此,您可以为要执行的每个批处理操作模拟请求,然后将其传递给http_kernel组件,然后详细说明结果。

考虑这个示例控制器:

/**
 * @Route("/batchAction", name="batchAction")
 */
public function batchAction()
{
    // Simulate a batch request of existing route
    $requests = [
        [
            'method' => 'GET',
            'relative_url' => '/b',
            'options' => 'a=b&cd',
        ],
        [
            'method' => 'GET',
            'relative_url' => '/c',
            'options' => 'a=b&cd',
        ],
    ];

    $kernel = $this->get('http_kernel');

    $responses = [];
    foreach($requests as $aRequest){

        // Construct a query params. Is only an example i don't know your input
        $options=[];
        parse_str($aRequest['options'], $options);

        // Construct a new request object for each batch request
        $req = Request::create(
            $aRequest['relative_url'],
            $aRequest['method'],
            $options
        );
        // process the request
        // TODO handle exception
        $response = $kernel->handle($req);

        $responses[] = [
            'method' => $aRequest['method'],
            'url' => $aRequest['relative_url'],
            'code' => $response->getStatusCode(),
            'headers' => $response->headers,
            'body' => $response->getContent()
        ];
    }
    return new JsonResponse($responses);
}

使用以下控制器方法:

/**
 * @Route("/a", name="route_a_")
 */
public function aAction(Request $request)
{
    return new Response('A');
}

/**
 * @Route("/b", name="route_b_")
 */
public function bAction(Request $request)
{
    return new Response('B');
}

/**
 * @Route("/c", name="route_c_")
 */
public function cAction(Request $request)
{
    return new Response('C');
}

请求的输出将是:

[
{"method":"GET","url":"\/b","code":200,"headers":{},"body":"B"},
{"method":"GET","url":"\/c","code":200,"headers":{},"body":"C"}
]

PS:我希望我能正确理解你的需要。

答案 1 :(得分:0)

有一些方法可以优化测试速度,包括PHPunit配置(例如,xdebug配置,或使用phpdbg SAPI运行测试,而不是将Xdebug模块包含到通常的PHP实例中)。

因为代码将始终运行AppKernel类,所以您还可以针对特定环境对其进行一些优化 - 包括在测试期间不经常初始化容器。

我正在使用Kris Wallsmith的one such example。这是他的示例代码。

class AppKernel extends Kernel
{
// ... registerBundles() etc
// In dev & test, you can also set the cache/log directories 
// with getCacheDir() & getLogDir() to a ramdrive (/tmpfs).
// particularly useful when running in VirtualBox

protected function initializeContainer()
{
    static $first = true;

    if ('test' !== $this->getEnvironment()) {
        parent::initializeContainer();
        return;
    }

    $debug = $this->debug;

    if (!$first) {
        // disable debug mode on all but the first initialization
        $this->debug = false;
    }

    // will not work with --process-isolation
    $first = false;

    try {
        parent::initializeContainer();
    } catch (\Exception $e) {
        $this->debug = $debug;
        throw $e;
    }

    $this->debug = $debug;
}