/** * @param Envelope $envelope * @param Queue $queue * @return DeliveryResult */ public function __invoke(Envelope $envelope, Queue $queue) : DeliveryResult { $data = json_decode($envelope->getBody(), true); if (!isset($data['created_at'])) { return DeliveryResult::MSG_REJECT(); } $data['created_at'] = DateTimeImmutable::createFromFormat('Y-m-d\\TH:i:s.u', $data['created_at'], new DateTimeZone('UTC')); if (false === $data['created_at']) { return DeliveryResult::MSG_REJECT(); } try { $command = $this->messageFactory->createMessageFromArray($envelope->getType(), $data); $this->commandBus->dispatch($command); } catch (\Throwable $e) { while ($e = $e->getPrevious()) { if ($e instanceof ConcurrencyException) { return DeliveryResult::MSG_REJECT_REQUEUE(); } } return DeliveryResult::MSG_REJECT(); } return DeliveryResult::MSG_ACK(); }
/** * @test */ public function it_rejects_and_requeues_message_when_concurrency_exception_occured() { $time = (string) microtime(true); if (false === strpos($time, '.')) { $time .= '.0000'; } $now = \DateTimeImmutable::createFromFormat('U.u', $time); $envelope = $this->prophesize(Envelope::class); $envelope->getBody()->willReturn('{"message_name":"test-command","uuid":"ccefedef-85e1-4fd0-b247-ed13d378b050","version":1,"payload":[],"metadata":[],"created_at":"' . $now->format('Y-m-d\\TH:i:s.u') . '"}')->shouldBeCalled(); $envelope->getType()->willReturn('test-command')->shouldBeCalled(); $queue = $this->prophesize(Queue::class); $command = $this->prophesize(Command::class); $messageFactory = $this->prophesize(MessageFactory::class); $messageFactory->createMessageFromArray('test-command', ['message_name' => 'test-command', 'uuid' => 'ccefedef-85e1-4fd0-b247-ed13d378b050', 'version' => 1, 'payload' => [], 'metadata' => [], 'created_at' => $now])->willReturn($command->reveal())->shouldBeCalled(); $commandBus = $this->prophesize(CommandBus::class); $commandBus->dispatch($command)->willThrow(new MessageDispatchException('', 0, new ConcurrencyException()))->shouldBeCalled(); $amqpCommandConsumerCallback = new AmqpCommandConsumerCallback($commandBus->reveal(), $messageFactory->reveal()); $deliveryResult = $amqpCommandConsumerCallback($envelope->reveal(), $queue->reveal()); $this->assertEquals(DeliveryResult::MSG_REJECT_REQUEUE(), $deliveryResult); }
/** * @param Envelope $envelope * @return DeliveryResult */ protected function handleInternalMessage(Envelope $envelope) : DeliveryResult { if ('shutdown' === $envelope->getType()) { $this->logger->info('Shutdown message received'); $this->shutdown(); $result = DeliveryResult::MSG_ACK(); } elseif ('reconfigure' === $envelope->getType()) { $this->logger->info('Reconfigure message received'); try { list($idleTimeout, $target, $prefetchSize, $prefetchCount) = json_decode($envelope->getBody()); if (is_numeric($idleTimeout)) { $idleTimeout = (double) $idleTimeout; } Assertion::float($idleTimeout); Assertion::min($target, 0); Assertion::min($prefetchSize, 0); Assertion::min($prefetchCount, 0); } catch (\Throwable $e) { $this->logger->error('Exception during reconfiguration: ' . $e->getMessage()); return DeliveryResult::MSG_REJECT(); } $this->idleTimeout = $idleTimeout; $this->target = $target; $this->queue->getChannel()->qos($prefetchSize, $prefetchCount); $this->blockSize = $prefetchCount; $result = DeliveryResult::MSG_ACK(); } elseif ('ping' === $envelope->getType()) { $this->logger->info('Ping message received'); $result = DeliveryResult::MSG_ACK(); } else { $this->logger->error('Invalid internal message: ' . $envelope->getType()); $result = DeliveryResult::MSG_REJECT(); } return $result; }
/** * @param Envelope $envelope * @param Queue $queue * @return DeliveryResult */ protected function handleDelivery(Envelope $envelope, Queue $queue) : DeliveryResult { $this->countMessagesConsumed++; $this->countMessagesUnacked++; $this->lastDeliveryTag = $envelope->getDeliveryTag(); $this->timestampLastMessage = microtime(true); $this->ack(); $this->logger->debug('Handling delivery of message', $this->extractMessageInformation($envelope)); if ($envelope->getAppId() === 'Humus\\Amqp') { $this->handleInternalMessage($envelope); return DeliveryResult::MSG_ACK(); } try { $request = $this->requestFromEnvelope($envelope); $callback = $this->deliveryCallback; $response = $callback($request); if ('' === $request->id()) { // notifications have no reply return DeliveryResult::MSG_ACK(); } if (!$response instanceof JsonRpcResponse) { $response = JsonRpcResponse::withResult($envelope->getCorrelationId(), $response); } } catch (Exception\InvalidJsonRpcVersion $e) { $this->logger->error('Invalid json rpc version', $this->extractMessageInformation($envelope)); $response = JsonRpcResponse::withError($envelope->getCorrelationId(), new JsonRpcError(JsonRpcError::ERROR_CODE_32600)); } catch (Exception\InvalidJsonRpcRequest $e) { $this->logger->error('Invalid json rpc request', $this->extractMessageInformation($envelope)); $response = JsonRpcResponse::withError($envelope->getCorrelationId(), new JsonRpcError(JsonRpcError::ERROR_CODE_32600)); } catch (Exception\JsonParseError $e) { $this->logger->error('Json parse error', $this->extractMessageInformation($envelope)); $response = JsonRpcResponse::withError($envelope->getCorrelationId(), new JsonRpcError(JsonRpcError::ERROR_CODE_32700)); } catch (\Throwable $e) { $extra = $this->extractMessageInformation($envelope); $extra['exception_class'] = get_class($e); $extra['exception_message'] = $e->getMessage(); $extra['exception_trace'] = $e->getTraceAsString(); $this->logger->error('Exception occurred', $extra); $response = JsonRpcResponse::withError($envelope->getCorrelationId(), new JsonRpcError(JsonRpcError::ERROR_CODE_32603)); } $this->sendReply($response, $envelope); return DeliveryResult::MSG_ACK(); }