PHP code example of ackintosh / ganesha

1. Go to this page and download the library: Download ackintosh/ganesha library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

ackintosh / ganesha example snippets


$ganesha->isAvailable($service);
$ganesha->success($service);
$ganesha->failure($service);

// For further details about builder options, please see the `Strategy` section.
$ganesha = Ackintosh\Ganesha\Builder::withRateStrategy()
    ->adapter(new Ackintosh\Ganesha\Storage\Adapter\Redis($redis))
    ->failureRateThreshold(50)
    ->intervalToHalfOpen(10)
    ->minimumRequests(10)
    ->timeWindow(30)
    ->build();

$service = 'external_api';

if (!$ganesha->isAvailable($service)) {
    die('external api is not available');
}

try {
    echo \Api::send($request)->getBody();
    $ganesha->success($service);
} catch (\Api\RequestTimedOutException $e) {
    // If an error occurred, it must be recorded as failure.
    $ganesha->failure($service);
    die($e->getMessage());
}

$ganesha->subscribe(function ($event, $service, $message) {
    switch ($event) {
        case Ganesha::EVENT_TRIPPED:
            \YourMonitoringSystem::warn(
                "Ganesha has tripped! It seems that a failure has occurred in {$service}. {$message}."
            );
            break;
        case Ganesha::EVENT_CALMED_DOWN:
            \YourMonitoringSystem::info(
                "The failure in {$service} seems to have calmed down :). {$message}."
            );
            break;
        case Ganesha::EVENT_STORAGE_ERROR:
            \YourMonitoringSystem::error($message);
            break;
        default:
            break;
    }
});

// Ganesha with Count strategy(threshold `3`).
// $ganesha = Ackintosh\Ganesha\Builder::withCountStrategy() ...

// Disable
Ackintosh\Ganesha::disable();

// Although the failure is recorded to storage,
$ganesha->failure($service);
$ganesha->failure($service);
$ganesha->failure($service);

// Ganesha does not trip and Ganesha::isAvailable() returns true.
var_dump($ganesha->isAvailable($service));
// bool(true)

$ganesha = Ackintosh\Ganesha\Builder::withRateStrategy()
    // ...
    ->build();

$ganesha->reset();


$ganesha = Ackintosh\Ganesha\Builder::withRateStrategy()
    // The interval in time (seconds) that evaluate the thresholds.
    ->timeWindow(30)
    // The failure rate threshold in percentage that changes CircuitBreaker's state to `OPEN`.
    ->failureRateThreshold(50)
    // The minimum number of requests to detect failures.
    // Even if `failureRateThreshold` exceeds the threshold,
    // CircuitBreaker remains in `CLOSED` if `minimumRequests` is below this threshold.
    ->minimumRequests(10)
    // The interval (seconds) to change CircuitBreaker's state from `OPEN` to `HALF_OPEN`.
    ->intervalToHalfOpen(5)
    // The storage adapter instance to store various statistics to detect failures.
    ->adapter(new Ackintosh\Ganesha\Storage\Adapter\Memcached($memcached))
    ->build();

$ganesha = Ackintosh\Ganesha\Builder::withCountStrategy()
    // The failure count threshold that changes CircuitBreaker's state to `OPEN`.
    // The count will be increased if `$ganesha->failure()` is called,
    // or will be decreased if `$ganesha->success()` is called.
    ->failureCountThreshold(100)
    // The interval (seconds) to change CircuitBreaker's state from `OPEN` to `HALF_OPEN`.
    ->intervalToHalfOpen(5)
    // The storage adapter instance to store various statistics to detect failures.
    ->adapter(new Ackintosh\Ganesha\Storage\Adapter\Memcached($memcached))
    ->build();

$adapter = new Ackintosh\Ganesha\Storage\Adapter\Apcu();

$ganesha = Ackintosh\Ganesha\Builder::withRateStrategy()
    ->adapter($adapter)
    // ... (omitted) ...
    ->build();

$redis = new \Redis();
$redis->connect('localhost');
$adapter = new Ackintosh\Ganesha\Storage\Adapter\Redis($redis);

$ganesha = Ackintosh\Ganesha\Builder::withRateStrategy()
    ->adapter($adapter)
    // ... (omitted) ...
    ->build();

$memcached = new \Memcached();
$memcached->addServer('localhost', 11211);
$adapter = new Ackintosh\Ganesha\Storage\Adapter\Memcached($memcached);

$ganesha = Ackintosh\Ganesha\Builder::withRateStrategy()
    ->adapter($adapter)
    // ... (omitted) ...
    ->build();

$manager = new \MongoDB\Driver\Manager('mongodb://localhost:27017/');
$adapter = new Ackintosh\Ganesha\Storage\Adapter\MongoDB($manager, 'dbName', 'collectionName');

$ganesha = Ackintosh\Ganesha\Builder::withRateStrategy()
    ->adapter($adapter)
    // ... (omitted) ...
    ->build();

class YourStorageKeys implements StorageKeysInterface
{
    public function prefix()
    {
        return 'your_prefix_';
    }

    // ... (omitted) ...
}

$ganesha = Ackintosh\Ganesha\Builder::withRateStrategy()
    // The keys which will stored by Ganesha to the storage you specified via `adapter`
    // will be prefixed with `your_prefix_`.
    ->storageKeys(new YourStorageKeys())
    // ... (omitted) ...
    ->build();

use Ackintosh\Ganesha\Builder;
use Ackintosh\Ganesha\GuzzleMiddleware;
use Ackintosh\Ganesha\Exception\RejectedException;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;

$ganesha = Builder::withRateStrategy()
    ->timeWindow(30)
    ->failureRateThreshold(50)
    ->minimumRequests(10)
    ->intervalToHalfOpen(5)
    ->adapter($adapter)
    ->build();

$middleware = new GuzzleMiddleware($ganesha);

$handlers = HandlerStack::create();
$handlers->push($middleware);

$client = new Client(['handler' => $handlers]);

try {
    $client->get('http://api.example.com/awesome_resource');
} catch (RejectedException $e) {
    // If the circuit breaker is open, RejectedException will be thrown.
}

// In the example above, `api.example.com` is used as `$service`.
$client->get('http://api.example.com/awesome_resource');

// via constructor argument
$client = new Client([
    'handler' => $handlers,
    // 'ganesha.service_name' is defined as ServiceNameExtractor::OPTION_KEY
    'ganesha.service_name' => 'specified_service_name',
]);

// via request method argument
$client->get(
    'http://api.example.com/awesome_resource',
    [
        'ganesha.service_name' => 'specified_service_name',
    ]
);

// via request header
$request = new Request(
    'GET',
    'http://api.example.com/awesome_resource',
    [
        // 'X-Ganesha-Service-Name' is defined as ServiceNameExtractor::HEADER_NAME
        'X-Ganesha-Service-Name' => 'specified_service_name'
    ]
);
$client->send($request);

use Ackintosh\Ganesha\GuzzleMiddleware\ServiceNameExtractorInterface;
use Psr\Http\Message\RequestInterface;

class SampleExtractor implements ServiceNameExtractorInterface
{
    /**
     * @override
     */
    public function extract(RequestInterface $request, array $requestOptions)
    {
        // We treat the combination of host name and HTTP method name as $service.
        return $request->getUri()->getHost() . '_' . $request->getMethod();
    }
}

// ---

$ganesha = Builder::withRateStrategy()
    // ...
    ->build();
$middleware = new GuzzleMiddleware(
    $ganesha,
    // Pass the extractor as an argument of GuzzleMiddleware constructor.
    new SampleExtractor()
);

use Ackintosh\Ganesha\GuzzleMiddleware\FailureDetectorInterface;
use Psr\Http\Message\ResponseInterface;

class HttpStatusFailureDetector implements FailureDetectorInterface
{
    public function isFailureResponse(ResponseInterface $response) : bool
    {
        return in_array($response->getStatusCode(), [503, 504], true);
    }
}

// ---
$ganesha = Builder::withRateStrategy()
    // ...
    ->build();
$middleware = new GuzzleMiddleware(
    $ganesha,
    // Pass the failure detector to the GuzzleMiddleware constructor.
    failureDetector: new HttpStatusFailureDetector()
);

// For details on how to build middleware please see https://github.com/ackintosh/ganesha#ganesha-heart-guzzle
$middleware = new GuzzleMiddleware($ganesha);

// Set the middleware to HTTP client.
$handlers = HandlerStack::create();
$handlers->push($middleware);
$client = new Client(['handler' => $handlers]);

// Just pass the HTTP client to the constructor of API class.
$api = new PetApi($client);

try {
    // Ganesha is working in the shadows! The result of api call is monitored by Ganesha.
    $api->getPetById(123);
} catch (RejectedException $e) {
    awesomeErrorHandling($e);
}

use Ackintosh\Ganesha\Builder;
use Ackintosh\Ganesha\GaneshaHttpClient;
use Ackintosh\Ganesha\Exception\RejectedException;

$ganesha = Builder::withRateStrategy()
    ->timeWindow(30)
    ->failureRateThreshold(50)
    ->minimumRequests(10)
    ->intervalToHalfOpen(5)
    ->adapter($adapter)
    ->build();

$client = HttpClient::create();
$ganeshaClient = new GaneshaHttpClient($client, $ganesha);

try {
    $ganeshaClient->request('GET', 'http://api.example.com/awesome_resource');
} catch (RejectedException $e) {
    // If the circuit breaker is open, RejectedException will be thrown.
}

// In the example above, `api.example.com` is used as `$service`.
$ganeshaClient->request('GET', 'http://api.example.com/awesome_resource');

// via constructor argument
$ganeshaClient = new GaneshaHttpClient($client, $ganesha, [
    // 'ganesha.service_name' is defined as ServiceNameExtractor::OPTION_KEY
    'ganesha.service_name' => 'specified_service_name',
]);

// via request method argument
$ganeshaClient->request(
    'GET',
    'http://api.example.com/awesome_resource',
    [
        'ganesha.service_name' => 'specified_service_name',
    ]
);

// via request header
$ganeshaClient->request('GET', '', ['headers' => [
     // 'X-Ganesha-Service-Name' is defined as ServiceNameExtractor::HEADER_NAME
     'X-Ganesha-Service-Name' => 'specified_service_name'
]]);

use Ackintosh\Ganesha\HttpClient\HostTrait;
use Ackintosh\Ganesha\HttpClient\ServiceNameExtractorInterface;

final class SampleExtractor implements ServiceNameExtractorInterface
{
    use HostTrait;

    /**
     * @override
     */
    public function extract(string $method, string $url, array $requestOptions): string
    {
        // We treat the combination of host name and HTTP method name as $service.
        return self::extractHostFromUrl($url) . '_' . $method;
    }
}

// ---

$ganesha = Builder::withRateStrategy()
    // ...
    ->build();
$ganeshaClient = new GaneshaHttpClient(
    $client,
    $ganesha,
    // Pass the extractor as an argument of GaneshaHttpClient constructor.
    new SampleExtractor()
);

// via constructor argument
$ganeshaClient = new GaneshaHttpClient(
    $client, $ganesha, null,
    new RestFailureDetector([503])
);

// via request method argument
$ganeshaClient->request(
    'GET',
    'http://api.example.com/awesome_resource',
    [
        // 'ganesha.failure_status_codes' is defined as RestFailureDetector::OPTION_KEY
        'ganesha.failure_status_codes' => [503],
    ]
);

use Ackintosh\Ganesha\HttpClient\FailureDetectorInterface;
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;

final class SampleFailureDetector implements FailureDetectorInterface
{
    /**
     * @override
     */
    public function isFailureResponse(ResponseInterface $response, array $requestOptions): bool
    {
        try {
            $jsonData = $response->toArray();
        } catch (ExceptionInterface $e) {
            return true;
        }

        // Server is not RestFull and always returns HTTP 200 Status Code, but set an error flag in the JSON payload.
        return true === ($jsonData['error'] ?? false);
    }

    /**
     * @override
     */
    public function getOptionKeys(): array
    {
       // No option is defined for this implementation
       return [];
    }
}

// ---

$ganesha = Builder::withRateStrategy()
    // ...
    ->build();
$ganeshaClient = new GaneshaHttpClient(
    $client,
    $ganesha,
    null,
    // Pass the failure detector as an argument of GaneshaHttpClient constructor.
    new SampleFailureDetector()
);