/** * @param InputInterface $input * @param OutputInterface $output * * @return int|null|void */ protected function execute(InputInterface $input, OutputInterface $output) { $redis = new Client(sprintf('tcp://%s:%d', ConfigurationLoader::get('client.async.redis.host'), ConfigurationLoader::get('client.async.redis.port'))); $logger = LoggerFactory::create('Client #' . $input->getArgument('client_id'), true); $client = ClientFactory::create($logger, $redis, $input->getArgument('account_key'), $input->getArgument('client_id'), true); $connector = new ClientWorker($logger, $client, $redis); $connector->listen(); }
/** * Show the asynchronous client logs in the main process logger (console) * * @return string|null * * @throws \RuntimeException */ public static function subscribe() { if (!isset(self::$redisClient)) { throw new \RuntimeException('Redis client has not been initialised'); } while (null != ($log = self::$redisClient->lpop(ConfigurationLoader::get('client.async.redis.key') . '.client.logs'))) { list($level, $message) = explode('|', $log); self::$logger->addRecord($level, $message); } }
/** * @param string $regionUniqueName * * @return RegionInterface * * @throws RegionNotFoundException */ private static function createRegion($regionUniqueName) { try { $region = ConfigurationLoader::get('region.servers.' . $regionUniqueName); } catch (ConfigurationKeyNotFoundException $e) { throw new RegionNotFoundException('The region with unique name "' . $regionUniqueName . '" is not found'); } $class = ConfigurationLoader::get('region.class'); return new $class($regionUniqueName, $region['name'], $region['server'], $region['loginQueue']); }
/** * @param string $protocol * @param string $server * @param int $port */ public function __construct($protocol, $server, $port) { $this->timeout = (int) ConfigurationLoader::get('client.request.timeout'); if (1 > $this->timeout) { $this->timeout = 5; } $this->socket = stream_socket_client(sprintf('%s://%s:%d', $protocol, $server, $port), $errorCode, $errorMessage, $this->timeout); if (0 != $errorCode) { $this->errorMessage = $errorMessage; } else { stream_set_timeout($this->socket, $this->timeout); stream_set_blocking($this->socket, false); } }
/** * @param LoggerInterface $logger * @param LOLClientInterface $client * @param Client $redis * * @throws \Exception */ public function __construct(LoggerInterface $logger, LOLClientInterface $client, $redis) { $this->logger = $logger; $this->client = $client; $this->redis = $redis; // Init configuration to handle exception and log them try { $this->expire = (int) ConfigurationLoader::get('client.response.expire'); if ($this->expire < (int) ConfigurationLoader::get('client.request.timeout')) { $this->expire = (int) ConfigurationLoader::get('client.request.timeout'); } $this->key = ConfigurationLoader::get('client.async.redis.key'); $this->defaultPort = ConfigurationLoader::get('client.async.startPort'); } catch (\Exception $e) { $this->logger->critical($e->getMessage()); throw $e; } }
/** * Fetch the result from client, transform it into an array and store it into the $this->results array * * @param int $invokeId * @param null|callable $resultsCallback This callback will format the result if needed * @param int $timeout * @param bool $bypassOverload Some API return nothing on error, we need to bypass overload system to<br /> * avoid timeout issue. */ protected function fetchResult($invokeId, \Closure $resultsCallback = null, $timeout = null, $bypassOverload = false) { $this->invokeCount++; if (null == $timeout) { $timeout = ConfigurationLoader::get('client.request.timeout'); } $timedOut = time() + $timeout; $this->onClientReady(function (LOLClientInterface $client) use($invokeId, $timedOut, $bypassOverload, $resultsCallback, $timeout) { $this->apiManager->getLoop()->addPeriodicTimer(0.0001, function (TimerInterface $timer) use($invokeId, $timedOut, $bypassOverload, $client, $resultsCallback, $timeout) { if ($this->hasError) { $timer->cancel(); return; } // Timeout process if (time() > $timedOut) { $this->hasError = true; $this->conn->emit('api-error', [new RequestTimeoutException('Request timeout, the client will reconnect', $client)]); $timer->cancel(); return null; } $resultParams = $client->getResult($invokeId); if (null == $resultParams) { return; } list($data, $callback) = $resultParams; $formatter = new ResultFormatter(); try { // RTMP API return error if ('_error' == $data['result']) { $this->hasError = true; $errorParams = $formatter->format($data['data']->getData()->rootCause); $this->conn->emit('api-error', [new ApiException($errorParams['rootCauseClassname'], $errorParams['message'])]); $timer->cancel(); return; } $result = $formatter->format($data['data']->getData()->body); if (null != $callback) { if ($callback instanceof Callback) { $result = $callback->getResult($result); } else { $result = $callback($result); } } if (null != $resultsCallback) { $this->results = $resultsCallback($result, $this->results); } else { $this->results[] = $result; } $this->responseCount++; $timer->cancel(); } catch (ClientOverloadException $e) { if ($bypassOverload) { $this->results[] = []; // empty response $this->responseCount++; $timer->cancel(); } else { // Flag client as overloaded & retry $client->setIsOverloaded(); $timer->cancel(); $this->fetchResult($invokeId, $resultsCallback, $timeout, $bypassOverload); } } }); }); }
/** * The RTMP LoL API will temporary ban you if you call too many times a service<br /> * To avoid this limitation, you must wait before making a new request * * @param string $regionUniqueName * @param callable $callback * * @return LOLClientInterface * * @throws RegionNotFoundException When there is not client with the selected region unique name */ public function getClient($regionUniqueName, \Closure $callback) { if (!isset($this->clients[$regionUniqueName])) { throw new RegionNotFoundException('There is no registered client with the region "' . $regionUniqueName . '"'); } $nextAvailableTime = (double) ConfigurationLoader::get('client.request.overload.available'); $nextAvailableTime /= 2; foreach ($this->clients[$regionUniqueName] as $client) { if ($client->isAvailable()) { return $callback($client); } } $this->loop->addPeriodicTimer($nextAvailableTime, function (TimerInterface $timer) use($regionUniqueName, $callback) { foreach ($this->clients[$regionUniqueName] as $client) { if ($client->isAvailable()) { $timer->cancel(); return $callback($client); } } }); }
/** * {@inheritdoc} */ public function setIsOverloaded() { $this->lastCall += (int) ConfigurationLoader::get('client.request.overload.wait'); }
/** * @param string $commandName * @param array $parameters * @param int|string|null $invokeId * * @return int|string */ protected function send($commandName, array $parameters = array(), $invokeId = null) { if (null == $invokeId) { $invokeId = $this->redis->incr($this->getKey('invokeId')); } $nextAvailableTime = (double) ConfigurationLoader::get('client.request.overload.available'); $this->lastCall = microtime(true) + $nextAvailableTime; $this->con->send(json_encode(['invokeId' => $invokeId, 'command' => $commandName, 'parameters' => $parameters]), \ZMQ::MODE_DONTWAIT); return $invokeId; }
/** * @param array $results * @param string $format * * @return mixed * * @throws UnknownFormatException */ protected function format(array $results, $format = null) { if (null == $format) { $format = ConfigurationLoader::get('server.format'); } else { if (!isset($this->formatters[$format])) { throw new UnknownFormatException('Unknown format for "' . $format . '". Did you mean : "' . join(', ', array_keys($this->formatters)) . '" ?'); } } return $this->formatters[$format]->format($results); }