Skip to content

Commit

Permalink
Merge pull request #2 from M6Web/feature/cache
Browse files Browse the repository at this point in the history
  • Loading branch information
mojoLyon committed Jun 24, 2015
2 parents 3c03727 + 1720d73 commit c94fc8a
Show file tree
Hide file tree
Showing 10 changed files with 645 additions and 23 deletions.
74 changes: 70 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Require the bundle in your composer.json file :
```json
{
"require": {
"m6web/guzzle-http-bundle"": "dev-master",
"m6web/guzzle-http-bundle"": "~1.0",
}
}
```
Expand Down Expand Up @@ -42,6 +42,7 @@ $ composer update m6web/guzzle-http-bundle
Add the `m6web_guzzlehttp` section in your configuration file. Here is the minimal configuration required.

```yaml
# app/config/config.yml
m6web_guzzlehttp:
clients:
default:
Expand Down Expand Up @@ -76,6 +77,18 @@ The service return a configured guzzle client, for more information on how to us
The only difference with guzzle6 reside in usage of curl for the redirect responses. You can choose to have the guzzle behavior
for redirection by setting the configuration key `redirect_handler` to `guzzle`.

When a cache system is available, you can use `force_cache` and `cache_ttl` in addition of guzzle options than respectively
force clear cache before request and use a specific ttl to a request that override configuration.

```php
$client = $this->get('m6web_guzzlehttp');
$response = $client->get('http://domain.tld', ['force_cache' => true]); // remove cache entry and set a new one
$response = $client->get('http://doamin.tld/path', ['cache_ttl' => 200]); // set ttl to 200 seconds instead the default one
```

## DataCollector

Datacollector is available when the symfony profiler is enabled. The collector allows you to see the following data :
Expand All @@ -86,9 +99,55 @@ Datacollector is available when the symfony profiler is enabled. The collector a
- Execution time
- Redirect count
- Redirect time
- Cache hit
- Cache ttl

**NOTE :** If you choose guzzle for `redirect_handler`, The redirect count and redirect time will always set to zero.

**NOTE :** If you choose guzzle for `redirect_handler`, The redirect count and redirect time will always set to zero.
Cache informations are available when a cache system is set.

## Cache system

You can set a cache for request by adding in the config the `guzzlehttp_cache` with `service` subkey who is a reference
to a service implementing `M6Web\Bundle\GuzzleHttpBundle\Cache\CacheInterface`

```yaml
# app/config/config.yml
m6web_guzzlehttp:
clients:
default:
base_uri: "http://domain.tld/"
guzzlehttp_cache:
service: my_cache_service
```

We provide a cache interface for Redis with [our RedisBundle](https://github.com/M6Web/RedisBundle) >= 2.4, than you can use in this way:

```yaml
# app/config/config.yml
m6web_guzzlehttp:
clients:
default:
base_uri: "http://domain.tld/"
guzzlehttp_cache:
service: m6_redis.guzzlehttp
m6_redis:
servers:
default:
ip: '127.0.0.1'
port: 6379
clients:
guzzlehttp:
servers: ["default"] # list of servers to use
namespace: GuzzleHttp\
timeout: 2 # timeout in second
readwritetimeout: 2 # read-write timeout in second
class: M6Web\Bundle\RedisBundle\CacheAdapters\M6WebGuzzleHttp
```

For more information on how to setup the RedisBundle, refer to the README in the project.

## Configuration reference

```yaml
Expand All @@ -102,9 +161,16 @@ m6web_guzzlehttp:
redirect_handler: curl # guzzle or curl
redirects:
max: 5 # Maximum redirect to follow
strict: false # use "strict" RFC compliant redirects. (guzzle only)
strict: false # use "strict" RFC compliant redirects. (guzzle redirect handler only)
referer: true # add a Referer header
protocols: ["http", "https"] # restrict redirect to a protocol
guzzlehttp_cache: # optional cache
service: my_cache_service # reference to service who implements the cache interface
default_ttl: 3600 # defautl ttl for cache entry in seconds
use_header_ttl: false # use the cache-control header to set the ttl
otherclient:
...
```

## Contributing
Expand Down
51 changes: 51 additions & 0 deletions src/Cache/CacheInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
namespace M6Web\Bundle\GuzzleHttpBundle\Cache;

/**
* Interface for the cache system on guzzlehttp client
*/
interface CacheInterface
{
/**
* Checks if the cache has a value for a key.
*
* @param string $key A unique key
*
* @return Boolean Whether the cache has a value for this key
*/
public function has($key);

/**
* Returns the value for a key.
*
* @param string $key A unique key
*
* @return string|null The value in the cache
*/
public function get($key);

/**
* Sets a value in the cache.
*
* @param string $key A unique key
* @param string $value The value to cache
* @param integer $ttl Time to live in seconds
*/
public function set($key, $value, $ttl = null);

/**
* Removes a value from the cache.
*
* @param string $key A unique key
*/
public function remove($key);

/**
* Return the TTL in second of a cache key or false if key doesn't exist
*
* @param string $key The key
*
* @return integer|false the ttl or false
*/
public function ttl($key);
}
106 changes: 98 additions & 8 deletions src/DataCollector/GuzzleHttpDataCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ class GuzzleHttpDataCollector extends DataCollector
*/
public function __construct()
{
$this->data['guzzleHttp'] = new \SplQueue();
$this->data['guzzleHttp'] = [
'commands' => new \SplQueue(),
'has5x' => false,
'has4x' => false
];
}

/**
Expand Down Expand Up @@ -48,20 +52,32 @@ public function onGuzzleHttpCommand(GuzzleHttpEvent $event)
{
$request = $event->getRequest();
$response = $event->getResponse();
$statusCode = $response->getStatusCode();

if ($statusCode > 499) {
$this->data['guzzleHttp']['has5x'] = true;
}

if ($statusCode > 399 && $statusCode < 500) {
$this->data['guzzleHttp']['has4x'] = true;
}

$data = [
'uri' => $request->getUri(),
'method' => $request->getMethod(),
'responseCode' => $response->getStatusCode(),
'responseCode' => $statusCode,
'responseReason' => $response->getReasonPhrase(),
'executionTime' => $event->getExecutionTime(),
'curl' => [
'redirectCount' => $response->curlInfo['redirect_count'],
'redirectTime' => $response->curlInfo['redirect_time']
]
'redirectCount' => (isset($response->curlInfo['redirect_count'])) ? $response->curlInfo['redirect_count'] : 0,
'redirectTime' => (isset($response->curlInfo['redirect_time'])) ? $response->curlInfo['redirect_time'] : 0
],
'cache' => (isset($response->cached)) ? 1 : 0,
'cacheTtl' => (isset($response->cacheTtl)) ? $response->cacheTtl : 0

];

$this->data['guzzleHttp']->enqueue($data);
$this->data['guzzleHttp']['commands']->enqueue($data);
}

/**
Expand All @@ -71,7 +87,27 @@ public function onGuzzleHttpCommand(GuzzleHttpEvent $event)
*/
public function getCommands()
{
return $this->data['guzzleHttp'];
return $this->data['guzzleHttp']['commands'];
}

/**
* Return true error 400 occured
*
* @return bool
*/
public function has4x()
{
return $this->data['guzzleHttp']['has4x'];
}

/**
* Return true error 500 occured
*
* @return bool
*/
public function has5x()
{
return $this->data['guzzleHttp']['has5x'];
}

/**
Expand All @@ -89,7 +125,7 @@ public function getTotalExecutionTime()
}

/**
* Return average time spent by cassandra command
* Return average time spent by guzzlehttp command
*
* @return float
*/
Expand All @@ -99,4 +135,58 @@ public function getAvgExecutionTime()

return ($totalExecutionTime) ? ($totalExecutionTime / count($this->getCommands()) ) : 0;
}

/**
* Return total cache hits
*
* @return int
*/
public function getCacheHits()
{
return array_reduce(iterator_to_array($this->getCommands()), function ($hits, $value) {
$hits += $value['cache'];

return $hits;
});
}

/**
* Return total cache hits
*
* @return int
*/
public function getRedirects()
{
return array_reduce(iterator_to_array($this->getCommands()), function ($redirect, $value) {
$redirect += $value['curl']['redirectCount'];;

return $redirect;
});
}

/**
* Return the total time spent by redirection
*
* @return float
*/
public function getTotalRedirectionTime()
{
return array_reduce(iterator_to_array($this->getCommands()), function ($time, $value) {
$time += $value['curl']['redirectTime'];

return $time;
});
}

/**
* Return average time spent by redirection
*
* @return float
*/
public function getAvgRedirectionTime()
{
$totalExecutionTime = $this->getTotalRedirectionTime();

return ($totalExecutionTime) ? ($totalExecutionTime / count($this->getRedirects()) ) : 0;
}
}
7 changes: 7 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ public function getConfigTreeBuilder()
->end()
->end()
->end()
->arrayNode('guzzlehttp_cache')
->children()
->integerNode('default_ttl')->defaultValue(3600)->end()
->booleanNode('use_header_ttl')->defaultValue(false)->end()
->scalarNode('service')->cannotBeEmpty()->end()
->end()
->end()
->end()
->end() // end prototype
->end()
Expand Down
15 changes: 12 additions & 3 deletions src/DependencyInjection/M6WebGuzzleHttpExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,24 @@ protected function loadClient(ContainerBuilder $container, $clientId, array $con
$config['curl'] = $this->getCurlConfig($config);
}

if (array_key_exists('guzzlehttp_cache', $config)) {
$defaultTtl = $config['guzzlehttp_cache']['default_ttl'];
$headerTtl = $config['guzzlehttp_cache']['use_header_ttl'];
$cacheService = new Reference($config['guzzlehttp_cache']['service']);

$curlHandler = $container->getDefinition('m6web_guzlehttp.handler.curlhandler');
$curlMultiHandler = $container->getDefinition('m6web_guzlehttp.handler.curlmultihandler');

$curlHandler->addMethodCall('setCache', [$cacheService, $defaultTtl, $headerTtl]);
$curlMultiHandler->addMethodCall('setCache', [$cacheService, $defaultTtl, $headerTtl]);
}


$guzzleClientDefintion = new Definition('%m6web_guzzlehttp.guzzle.client.class%');
$guzzleClientDefintion->addArgument($config);

//$clientDefinition->addMethodCall('setClient', [$guzzleClient]);

$containerKey = ($clientId == 'default') ? 'm6web_guzzlehttp' : 'm6web_guzzlehttp_'.$clientId;

//$container->setDefinition($containerKey, $clientDefinition);
$container->setDefinition($containerKey, $guzzleClientDefintion);

}
Expand Down
Loading

0 comments on commit c94fc8a

Please sign in to comment.