Amp PHP - совместная многозадачность / асинхронный сон - PullRequest
0 голосов
/ 10 февраля 2019

В однопоточном PHP мне нужно написать приложение, которое:

  • а) работает как HTTP-сервер
  • б) может выполнять некоторые HTTP-запросы

a) и b) должны работать, даже когда сервер ожидает / обслуживает запрос или HTTP-клиент ожидает ответа

У меня возникла идея использовать сервер PHP Amp.Это прекрасно работает.

Однако для HTTP-клиента мне нужно использовать PHP curl.

Мой код выглядит так:

...
$mh = curl_multi_init();
curl_multi_add_handle($mh, $ch);
do {
    AmpLoopHelper::asyncSleep(0.001);
    $mrc = curl_multi_exec($mh, $isRunning);
} while ($isRunning && ($mrc == CURLM_CALL_MULTI_PERFORM || $mrc == CURLM_OK));
...

и пользовательский AmpLoopHelperкласс:

<?php

namespace Mvorisek\Dsv;

use Amp\Loop;
use Amp\Loop\Driver;


class AmpLoopHelper {
    /** @var int|null */
    private static $dummyWatcherId;

    /**
     * Async sleep and keep processing of Loop tasks.<br>
     * Loop\Driver::tick() is always called at least once even if
     * the sleep delay is zero or negative.
     * 
     * @param float $sleepSecs
     */
    public static function asyncSleep(float $sleepSecs): void {
        $t = microtime(true);

        // add dummy function to repeat to prevent Loop\Driver::tick() to block
        $maxCheckDelayMillis = min(max(1, $sleepSecs * 1000 / 50), 50);
        $isNested = static::$dummyWatcherId !== null;
        if (!$isNested) {
            static::$dummyWatcherId = Loop::repeat($maxCheckDelayMillis, static function() {});
        }

        try {
            do {
                if (static::loopDriverIsRunning()) {
                    static::loopDriverTick();
                } else {
                    usleep(($sleepSecs - (microtime(true) - $t)) * 1e6);
                    break;
                }
                usleep(40);
            } while(microtime(true) - $t < $sleepSecs);
        } finally {
            if (!$isNested) {
                Loop::cancel(static::$dummyWatcherId);
            }
        }
    }

    private static function loopDriverGet(): Driver {
        return \Closure::bind(static function() {
            return Loop::$driver;
        }, null, Loop::class)();
    }

    private static function loopDriverIsRunning(): bool {
        $driver = static::loopDriverGet();
        return \Closure::bind(static function() use($driver) {
            return $driver->running;
        }, null, Driver::class)();
    }

    private static function loopDriverTick(): void {
        $driver = static::loopDriverGet();
        \Closure::bind(static function() use($driver) {
            $driver->tick();
        }, null, Driver::class)();
    }
}

Но HTTP-сервер иногда недоступен.Вспомогательный класс использует некоторые закрытые методы классов Amp.

Является ли идея asyncSleep правильной?

1 Ответ

0 голосов
/ 10 февраля 2019

Проблема заключалась в том, что Driver->dispatch() может вызываться с флагом блокировки, а потоки блокировки не были обновлены из моего вспомогательного класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...