AMPHP는 파이버 및 동시성을 염두에 두고 설계된 PHP용 이벤트 중심 라이브러리 모음입니다. amphp/amp
특히 비동기 프로그래밍을 위한 기본 기본 요소로 미래와 취소를 제공합니다. 이제 우리는 amphp/amp
사용하여 이벤트 루프 구현을 제공하는 대신 Revolt를 사용하고 있습니다.
Amp는 동기식 차단 코드와 마찬가지로 비동기식 코드를 작성하기 위해 PHP 8.1과 함께 제공되는 파이버를 많이 사용합니다. 이전 버전과 달리 생성기 기반 코루틴이나 콜백이 필요하지 않습니다. 스레드와 유사하게 각 파이버에는 자체 호출 스택이 있지만 파이버는 이벤트 루프에 의해 협력적으로 예약됩니다. 동시에 작업을 실행하려면 Ampasync()
사용하세요.
전통적으로 PHP는 순차적 실행 모델을 따릅니다. PHP 엔진은 순차적으로 한 줄씩 실행합니다. 그러나 종종 프로그램은 동시에 실행될 수 있는 여러 개의 독립적인 하위 프로그램으로 구성됩니다.
데이터베이스에 쿼리하는 경우에는 쿼리를 보내고 차단 방식으로 데이터베이스 서버의 응답을 기다립니다. 응답을 받으면 다음 작업을 시작할 수 있습니다. 거기 앉아서 기다리는 동안 아무것도 하지 않고, 이미 다음 데이터베이스 쿼리를 보내거나 API에 대한 HTTP 호출을 수행할 수 있습니다. 평소 I/O를 기다리며 보내는 시간을 활용해보자!
Revolt는 이러한 동시 I/O 작업을 허용합니다. 콜백을 방지하여 인지 부하를 낮게 유지합니다. 우리 API는 내부적으로 비차단 I/O를 사용하기 때문에 동시에 작동한다는 점을 제외하면 다른 라이브러리처럼 사용할 수 있습니다. Ampasync()
사용하여 동시에 실행하고 필요할 때 Future::await()
사용하여 결과를 기다리세요!
수년 동안 PHP에서 동시성을 구현하기 위한 다양한 기술이 있었습니다(예: PHP 5에 포함된 콜백 및 생성기). 이러한 접근 방식은 "함수 색상이 무엇입니까?" 문제로 인해 어려움을 겪었고, 이는 PHP 8.1에 Fibers를 포함하여 해결되었습니다. 여러 개의 독립적인 호출 스택과의 동시성을 허용합니다.
파이버는 이벤트 루프에 의해 협력적으로 예약되므로 코루틴이라고도 합니다. 주어진 시간에 하나의 코루틴만 실행되고 그 동안 다른 모든 코루틴은 일시 중지된다는 점을 이해하는 것이 중요합니다.
단일 CPU 코어를 사용하여 여러 프로그램을 실행하는 컴퓨터와 코루틴을 비교할 수 있습니다. 각 프로그램은 실행할 시간 슬롯을 갖습니다. 그러나 코루틴은 선점형이 아닙니다. 그들은 정해진 시간표를 얻지 못합니다. 이벤트 루프에 대한 제어권을 자발적으로 포기해야 합니다.
차단 I/O 기능은 I/O를 기다리는 동안 전체 프로세스를 차단합니다. 당신은 그들을 피하고 싶을 것입니다. 설치 가이드를 읽지 않았다면 차단 기능의 효과를 보여주는 Hello World 예제를 살펴보세요. AMPHP에서 제공하는 라이브러리는 I/O 차단을 방지합니다.
이 패키지는 Composer 종속성으로 설치할 수 있습니다.
composer require amphp/amp
이 라이브러리를 사용하는 경우 Revolt를 사용하여 이벤트를 예약할 가능성이 높습니다. Revolt는 자동으로 종속 항목으로 설치되더라도 별도로 필요합니다.
composer require revolt/event-loop
이 패키지는 PHP의 비동기/동시 애플리케이션을 위한 기본 빌딩 블록을 제공합니다. 우리는 이들 위에 구축된 많은 패키지를 제공합니다.
amphp/byte-stream
amphp/socket
amphp/parallel
amphp/http-client
amphp/http-server
amphp/mysql
및 amphp/postgres
이 패키지에는 PHP 8.1 이상이 필요합니다. 확장이 필요하지 않습니다!
확장은 앱에 많은 수의 동시 소켓 연결이 필요한 경우에만 필요합니다. 일반적으로 이 제한은 최대 1024개의 파일 설명자로 구성됩니다.
코루틴은 인터럽트 가능한 함수입니다. PHP에서는 파이버를 사용하여 구현할 수 있습니다.
참고 이전 버전의 Amp는 비슷한 목적으로 생성기를 사용했지만 호출 스택의 어느 곳에서나 파이버가 중단될 수 있으므로
Ampcall()
과 같은 이전 상용구가 필요하지 않습니다.
주어진 시간에 단 하나의 광케이블만 실행됩니다. 코루틴이 정지되면 코루틴 실행이 일시적으로 중단되어 다른 작업을 실행할 수 있습니다. 타이머가 만료되거나, 스트림 작업이 가능하거나, 대기 중인 Future
완료되면 실행이 재개됩니다.
코루틴의 낮은 수준 일시 중단 및 재개는 Revolt의 Suspension
API에 의해 처리됩니다.
<?php
require __DIR__ . ' /vendor/autoload.php ' ;
use Revolt EventLoop ;
$ suspension = EventLoop:: getSuspension ();
EventLoop:: delay ( 5 , function () use ( $ suspension ): void {
print ' ++ Executing callback created by EventLoop::delay() ' . PHP_EOL ;
$ suspension -> resume ( null );
});
print ' ++ Suspending to event loop... ' . PHP_EOL ;
$ suspension -> suspend ();
print ' ++ Script end ' . PHP_EOL ;
Revolt 이벤트 루프에 등록된 콜백은 자동으로 코루틴으로 실행되며 일시 중단해도 안전합니다. 이벤트 루프 API 외에도 Ampasync()
사용하여 독립적인 호출 스택을 시작할 수 있습니다.
<?php
use function Amp delay ;
require __DIR__ . ' /vendor/autoload.php ' ;
Amp async ( function () {
print ' ++ Executing callback passed to async() ' . PHP_EOL ;
delay ( 3 );
print ' ++ Finished callback passed to async() ' . PHP_EOL ;
});
print ' ++ Suspending to event loop... ' . PHP_EOL ;
delay ( 5 );
print ' ++ Script end ' . PHP_EOL ;
Future
는 비동기 작업의 최종 결과를 나타내는 개체입니다. 세 가지 상태가 있습니다.
성공적으로 완료된 future는 반환 값과 유사하고, 오류가 발생한 future는 예외를 발생시키는 것과 유사합니다.
비동기 API에 접근하는 한 가지 방법은 작업이 시작될 때 전달되고 완료되면 호출되는 콜백을 사용하는 것입니다.
doSomething ( function ( $ error , $ value ) {
if ( $ error ) {
/* ... */
} else {
/* ... */
}
});
콜백 접근 방식에는 몇 가지 단점이 있습니다.
그것이 미래가 작용하는 곳입니다. 이는 다른 반환 값처럼 반환되는 결과에 대한 자리 표시자입니다. 호출자는 Future::await()
사용하여 결과를 기다리거나 하나 또는 여러 개의 콜백을 등록할 수 있습니다.
try {
$ value = doSomething ()-> await ();
} catch (...) {
/* ... */
}
동시 애플리케이션에는 여러 개의 미래가 있을 것이며, 미래를 모두 기다리거나 첫 번째 미래만 기다리고 싶을 수도 있습니다.
AmpFutureawait($iterable, $cancellation)
iterable
의 모든 Future
객체를 기다립니다. Future
인스턴스 중 하나에 오류가 발생하면 해당 예외와 함께 작업이 중단됩니다. 그렇지 않으면 결과는 입력 반복 가능 항목의 키와 해당 완료 값과 iterable
하는 배열입니다.
await()
연결자는 동시에 많은 비동기 작업을 동시에 실행할 수 있게 해주기 때문에 매우 강력합니다. amphp/http-client
사용하여 여러 HTTP 리소스를 동시에 검색하는 예를 살펴보겠습니다.
<?php
use Amp Future ;
use Amp Http Client HttpClientBuilder ;
use Amp Http Client Request ;
$ httpClient = HttpClientBuilder:: buildDefault ();
$ uris = [
" google " => " https://www.google.com " ,
" news " => " https://news.google.com " ,
" bing " => " https://www.bing.com " ,
" yahoo " => " https://www.yahoo.com " ,
];
try {
$ responses = Future await ( array_map ( function ( $ uri ) use ( $ httpClient ) {
return Amp async ( fn () => $ httpClient -> request ( new Request ( $ uri , ' HEAD ' )));
}, $ uris ));
foreach ( $ responses as $ key => $ response ) {
printf (
" %s | HTTP/%s %d %s n" ,
$ key ,
$ response -> getProtocolVersion (),
$ response -> getStatus (),
$ response -> getReason ()
);
}
} catch ( Exception $ e ) {
// If any one of the requests fails the combo will fail
echo $ e -> getMessage (), "n" ;
}
AmpFutureawaitAnyN($count, $iterable, $cancellation)
개별 오류를 허용한다는 점을 제외하면 await()
와 동일합니다. iterable
인스턴스의 정확히 $count
인스턴스가 성공적으로 완료되면 결과가 반환됩니다. 반환 값은 값의 배열입니다. 구성 요소 배열의 개별 키는 평가를 위해 함수에 전달된 iterable
항목에서 보존됩니다.
AmpFutureawaitAll($iterable, $cancellation)
모든 미래를 기다리고 해당 결과를 [$errors, $values]
배열로 반환합니다.
AmpFutureawaitFirst($iterable, $cancellation)
성공적으로 완료되었는지 또는 오류가 발생했는지 여부에 관계없이 처음으로 완료된 Future
래핑 해제합니다.
AmpFutureawaitAny($iterable, $cancellation)
성공적으로 완료된 첫 번째 Future
펼칩니다.
Future는 여러 가지 방법으로 생성될 수 있습니다. 대부분의 코드는 함수를 가져와 다른 Fiber에서 코루틴으로 실행하는 Ampasync()
사용합니다.
때때로 인터페이스는 Future
를 반환하도록 요구하지만 결과는 캐시되어 있기 때문에 즉시 사용할 수 있습니다. 이러한 경우 Future::complete(mixed)
및 Future::error(Throwable)
사용하여 즉시 완료된 Future
생성할 수 있습니다.
참고 아래에 설명된
DeferredFuture
API는 많은 애플리케이션에 필요하지 않은 고급 API입니다. 가능하면 대신Ampasync()
또는 결합자를 사용하십시오.
AmpDeferredFuture
보류 중인 Future
완료하는 역할을 담당합니다. AmpDeferredFuture
생성하고 해당 getFuture
메서드를 사용하여 AmpFuture
호출자에게 반환합니다. 결과가 준비되면 연결된 DeferredFuture
에서 complete
또는 error
사용하여 호출자가 보유한 Future
완료합니다.
final class DeferredFuture
{
public function getFuture (): Future ;
public function complete ( mixed $ value = null );
public function error ( Throwable $ throwable );
}
경고
DeferredFuture
객체를 전달하는 경우 아마도 뭔가 잘못하고 있는 것일 수 있습니다. 작업의 내부 상태여야 합니다.
경고 다른 미래로 미래를 완료할 수 없습니다. 이러한 경우
DeferredFuture::complete()
호출하기 전에Future::await()
사용하십시오.
다음은 DeferredFuture
를 생성하고 관련 Future
호출자에게 반환하는 비동기 값 생산자 asyncMultiply()
의 간단한 예입니다.
<?php // Example async producer using DeferredFuture
use Revolt EventLoop ;
function asyncMultiply ( int $ x , int $ y ): Future
{
$ deferred = new Amp DeferredFuture ;
// Complete the async result one second from now
EventLoop:: delay ( 1 , function () use ( $ deferred , $ x , $ y ) {
$ deferred -> complete ( $ x * $ y );
});
return $ deferred -> getFuture ();
}
$ future = asyncMultiply ( 6 , 7 );
$ result = $ future -> await ();
var_dump ( $ result ); // int(42)
취소를 지원하는 모든 작업은 Cancellation
인스턴스를 인수로 받아들입니다. 취소는 핸들러를 등록하여 취소 요청을 구독할 수 있도록 하는 개체입니다. 이러한 개체는 하위 작업으로 전달되거나 작업 자체에서 처리되어야 합니다.
취소가 요청되면 $cancellation->throwIfRequested()
사용하여 CancelledException
으로 현재 작업을 실패할 수 있습니다. throwIfRequested()
잘 작동하지만 일부 작업에서는 대신 콜백을 구독해야 할 수도 있습니다. Cancellation::subscribe()
사용하여 발생할 수 있는 취소 요청을 구독할 수 있습니다.
호출자는 아래 구현 중 하나를 사용하여 Cancellation
생성합니다.
참고 취소는 권고 사항일 뿐입니다. 응답은 어쨌든 처리되어야 하고 여전히 캐시될 수 있으므로 쿼리가 전송된 후 취소 요청을 무시할 수 있습니다. HTTP 클라이언트는 연결을 재사용하기 위해 거의 완료된 HTTP 요청을 계속할 수 있지만, 계속하는 것이 실제로 중단하는 것보다 더 저렴한지 여부를 알 수 없으므로 청크 분할된 인코딩 응답을 중단할 수 있습니다.
TimeoutCancellations
는 지정된 시간(초)이 지나면 자동으로 취소됩니다.
request ( " ... " , new Amp TimeoutCancellation ( 30 ));
SignalCancellation
현재 프로세스에서 지정된 신호를 받은 후 자동으로 취소됩니다.
request ( " ... " , new Amp SignalCancellation ( SIGINT ));
DeferredCancellation
을 사용하면 메서드 호출을 통해 수동으로 취소할 수 있습니다. 이는 자체 구현을 제공하는 대신 어딘가에 사용자 정의 콜백을 등록해야 하는 경우 선호되는 방법입니다. 호출자만 DeferredCancellation
에 액세스할 수 있으며 DeferredCancellation::cancel()
사용하여 작업을 취소할 수 있습니다.
$ deferredCancellation = new Amp DeferredCancellation ();
// Register some custom callback somewhere
onSomeEvent ( fn () => $ deferredCancellation -> cancel ());
request ( " ... " , $ deferredCancellation -> getCancellation ());
NullCancellation
은 취소되지 않습니다. 취소는 선택 사항인 경우가 많으며 일반적으로 매개 변수를 nullable로 만들어 구현됩니다. if ($cancellation)
과 같은 가드를 피하기 위해 대신 NullCancellation
을 사용할 수 있습니다.
$ cancellation ??= new NullCancellationToken ();
CompositeCancellation
여러 개의 독립적인 취소 객체를 결합합니다. 이러한 취소 중 하나라도 취소되면 CompositeCancellation
자체가 취소됩니다.
amphp/amp
다른 모든 amphp
패키지와 마찬가지로 semver 의미 체계 버전 관리 사양을 따릅니다.
호환 패키지는 GitHub의 amphp
주제를 사용해야 합니다.
보안 관련 문제를 발견한 경우 문제 추적기를 사용하는 대신 [email protected]
으로 이메일을 보내주세요.
MIT 라이센스(MIT). 자세한 내용은 LICENSE
참조하세요.