I am trying to set up SQS and after receiving the message, I need to delete it from the queue.
Creating Client -
$client = Aws\Sqs\SqsClient::factory(array(
'key' => '******',
'secret' => '******',
'region' => 'ap-southeast-1'
));
Sending Message
public static function SendMessage()
{
if(!isset(self::$queueUrl))
self::getQueueUrl();
$command = "This is a command";
$commandstring = json_encode($command);
self::$client->sendMessage(array(
'QueueUrl' => self::$queueUrl,
'MessageBody' => $commandstring,
));
}
Receiving Message
public static function RecieveMessage()
{
if(!isset(self::$queueUrl))
self::getQueueUrl();
$result = self::$client->receiveMessage(array(
'QueueUrl' => self::$queueUrl,
));
// echo "Message Recieved >> ";
print_r($result);
foreach ($result->getPath('Messages/*/Body') as $messageBody) {
// Do something with the message
echo $messageBody;
//print_r(json_decode($messageBody));
}
foreach ($result->getPath('Messages/*/ReceiptHandle') as $ReceiptHandle) {
self::$client->deleteMessage(self::$queueUrl, $ReceiptHandle);
}
}
When I try to delete the message using the Receipt Handle in the receive message code, I get error from Guzzle -
Catchable fatal error: Argument 2 passed to Guzzle\Service\Client::getCommand() must be an array, string given,
Now after searching a lot for it, I was able to find similar questions which state that they were using wrong SDK version. I am still not able to narrow it down though. I am using the zip version of the latest sdk 2.6.15
Why don't you give this a try this:
self::$client->deleteMessage(array(
'QueueUrl' => self::$queueUrl,
'ReceiptHandle' => $ReceiptHandle,
));
The Basic formatting example in the API docs for SqsClient::deleteMessage() (and other operations) should help. All of the methods that execute operations take exactly one parameter, which is an associative array of the operation's parameters. You should read through the SDK's Getting Started Guide (if you haven't already), which talks about how to perform operations in general.
Related
I have a list of Job IDs to check their status. So, I'm simply looping through all the Job IDs to get their status on Media Convert.
function get_aws_job_id_status($job_id)
{
$result = [];
$client = \App::make('aws')->createClient('MediaConvert', [
// 'profile' => 'default',
// 'version' => '2017-08-29',
'region' => 'region',
'endpoint' => "endpoint"
]);
try {
$result = $client->getJob([
'Id' => $job_id,
]);
return $result;
} catch (AwsException $e) {
return $result;
}
}
I'm using the above function inside the loop to get the status.
Referred to AWS Docs and Stackoverflow, but still, when I don't find a record for the given Job ID, it returns "NotFoundException" error that is not going in catch block and breaking the loop. Is there any way to handle that exception so I can continue the loop?
I believe you need to call Aws\MediaConvert\Exception\MediaConvertException and catch for MediaConvert specific errors. I don't see any of your use statements but I assume the code would look something like the following.
Note I am catching for all MediaConvert client errors, but I believe you could specifically call out the NotFoundException by doing Aws\MediaConvert\Exception\MediaConvertException\NotFoundException
use Aws\MediaConvert\MediaConvertClient;
use Aws\Exception\AwsException;
use Aws\MediaConvert\Exception\MediaConvertException as MediaConvertError;
function get_aws_job_id_status($job_id)
{
$result = [];
$client = \App::make('aws')->createClient('MediaConvert', [
// 'profile' => 'default',
// 'version' => '2017-08-29',
'region' => 'region',
'endpoint' => "endpoint"
]);
try {
$result = $client->getJob([
'Id' => $job_id,
]);
return $result;
} catch (MediaConvertError $e) {
/*Oh no, the job id provided ca not be found.
Let us log the job id and the message and return it back up to the main application
Note this assumes the main application is iterating through a list of JobIDs and
can handle this and log that job id was not found and will not have the normal Job
JSON structure.
*/
$error = array("Id"=>$job_id, "Message"=>"Job Id Not found");
$result = json_encode($error);
return $result;
}
}
Also keep in mind that if you are polling for job status's you may be throttled if your list grows too big. You would need to catch for a TooManyRequestsException [1] and try the poll with a back off threshold [2].
The most scalable solution is to use CloudWatch Events and track jobs based the STATUS_UPDATE, COMPLETE and ERROR status. [3]
[1] https://docs.aws.amazon.com/aws-sdk-php/v3/api/class-Aws.MediaConvert.Exception.MediaConvertException.html
[2] https://docs.aws.amazon.com/general/latest/gr/api-retries.html
[3] https://docs.aws.amazon.com/mediaconvert/latest/ug/monitoring-overview.html
I have an AWS Elastic Beanstalk Worker Environment setup using SQS. The SQS is posting to a URL, which is an endpoint to a codebase that uses Laravel. From this endpoint, it takes up the message and process the payload. Some of my processes are time-consuming and is taking more than 20 minutes to complete. I am sending back a success response from the endpoint, but as it takes a lot of time to complete the process, most of the time, the messages are going to the inflight mode in SQS. I am trying to make a deleteMessage() call using PHP SDK, but I need to pass ReceiptHandle for deleting a message. As per the documentation here, SQS is not posting ReceiptHandle to my application and so I can't make a delete call. The problem with messages in inFlight mode is that it will be called again in the next time the and so the process is duplicated.
How can I delete a message once the process is completed ?
My current code is as follows :
$worker->process(
$request->header('X-Aws-Sqsd-Queue'), $job, [
'maxTries' => 0,
'delay' => 0
]
);
return $this->response([
'Processed ' . $job->getJobId()
]);
Where worker is an instance of
Illuminate\Queue\Worker;
and the response function is json encoding the data and respond with 200
You must have ReceiptHandle to delete a message in queue. In core PHP below is the function to read and delete a message in queue.
function ReadMessages($client,$queueUrl){
try {
$result = $client->receiveMessage(array(
'AttributeNames' => ['SentTimestamp'],
'MaxNumberOfMessages' => 1,
'MessageAttributeNames' => ['All'],
'QueueUrl' => $queueUrl, // REQUIRED
'WaitTimeSeconds' => 0,
));
if (count($result->get('Messages')) > 0) {
var_dump($result->get('Messages')[0]);
//to delete a message pass the receipt Handle
$result = $client->deleteMessage([
'QueueUrl' => $queueUrl, // REQUIRED
'ReceiptHandle' => $result->get('Messages')[0]['ReceiptHandle'] // REQUIRED
]);
} else {
echo "No messages in queue. \n";
}
}
catch (AwsException $e) {
// output error message if fails
return 'error';
error_log($e->getMessage());
}
}
Do some workaround so you get a ReceiptHandle to delete a message in queue.
I modified it all now I have this file that makes my api work.
auth.php:
<?php
include 'Unirest.php';
function login()
{
$headers = array('Accept' => 'application/json');
$data = array(
"grant_type" => "password",
"client_id" => "myclientid",
"client_secret" => "myclientsecret",
"username" => "username",
"password" => "password"
);
$response = Unirest\Request::post('http://i-scent.fr/api/oauth_token', $headers, $data);
// $response->code;
// $response->headers;
return $response->body->access_token;
}
function device_info($device_id,$token){
$header = array('Accept' => 'application/json',
'Authorization' => 'Bearer '.$token );
$response = Unirest\Request::get('http://i-scent.fr/api/devices/'.$device_id,$header);
echo $response->body->name;
echo "</br>";
}
function diffuse($device_id,$token,$duration,$intensity){
$header = array('Accept' => 'application/json', 'Authorization' => 'Bearer '.$token );
$data = array('time' => 1, 'percent' => 50);
$body = Unirest\Request\Body::form($data);
$response = Unirest\Request::put('http://i-scent.fr/app_dev.php/api/device/'.$device_id.'/actions/diffusion',$header,$body);
echo $response->code;
echo "</br>";
}
When I use all the functions in a simple script it works perfectly on my website. But when I put it like this in my webhook, I have error 500 internal server error. I have all the unirest libraries.
<?php
include "auth.php";
function processMessage($update) {
if($update["result"]["action"] == "sayHello"){
$token = login();
$name = device_info("1966",$token);
diffuse("1966",$token,"0.5","50");
sendMessage(array(
"source" => $update["result"]["source"],
"speech" => "bonjour webhook",
"displayText" => "bonjour webhook",
"contextOut" => array()
));
}
}
function sendMessage($parameters) {
echo json_encode($parameters);
}
$update_response = file_get_contents("php://input");
$update = json_decode($update_response, true);
if (isset($update["result"]["action"])) {
processMessage($update);
}
Error 500 is supposed to mean that the webhokk's script crashed somewhere but I don't know where and why.
Update 2
Based on your most recent code, you're including "auth.php", which works in the original environment (which is being called as part of a web page, it sounds like).
Your code has two functions, device_info() and diffuse(), which output their results instead of returning them. This output isn't JSON, and includes HTML markup. This is being sent as part of the result of your webhook and will cause what is returned to be invalid.
Update
Based on your latest code, there are still many logical, and a few syntactical, problems.
A "500 Internal Server Error" indicates that your program didn't run correctly and crashed for some reason. As posted, it is missing a closing }, which could be the problem if that isn't in your actual code.
Even if you fix that, there are many issues with the code:
It isn't clear what you intend to do with the results of calling your "test1" script. You store them in $data and don't do anything with it.
You're calling the other website (test1) before you look at what the user has asked you to do. Which is fine, but then why do you care what the user is asking you?
Original Answer
There are a few errors here, but the underlying problem is that you're mixing up where things run and the capabilities of the caller to your webhook.
For a Dialogflow webhook, Google/Dialogflow is sending JSON (which you seem to be handling ok), and expecting back JSON. Although it looks like you send this back as part of send_message(), you're also sending something back when you call connexion(). What you're sending back in this case is not JSON, but HTML with JavaScript.
Which leads to the second problem - If this was php that was generating an HTML page that included a script, you'd be in fine shape. But it isn't. You have to send back only JSON.
You can do something like this to call the other API and get back the contents:
$body = file_get_contents("http://google-home.exhalia.fr/test1");
Which will set $body to the body of the page you've called. What you do with that, at that point, is up to you. But you need to make this call before your call to send_message() because you want to represent the contents as part of what you're saying.
(See How to send a GET request from PHP? for a discussion of other methods available to you in case you need to do a POST, use header information, etc.)
I'm using AWS SDK V3 for PHP. Sometimes when I make calls to AWS S3 I get errors like 400 errors Bad Request RequestTimeout (client): Your socket connection to the server was not read from or written to within the timeout period due to network problem even when the object I'm trying to interact with is there. What I need to do is to implement a retry mechanism. I wonder if we can do it simply with an option in the AWS SDK to specify the number of times we want a retry after an error.
I know that I can do that with simple try catch and retry, but I'm thinking may be the SDK already provides a much cleaner way to do that.
I already found static function Middleware::retry() but I have no idea how to use it.
You can specify the number of retries when constructing a new instance of any AWS service client class:
$client = new Aws\EC2\Ec2Client([
'region' => 'eu-central-1',
'retries' => 3
]);
https://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/configuration.html#retries
You can use something like this,
public function get_s3_connection(){
$sdk = new \Aws\Sdk([
'region' => 'us-east-1',
'version' => 'latest',
'credentials' => $credentials
]);
$s3_connection = $sdk->createS3([
'retries' => 3,
'http' => [
'connect_timeout' => 15,
'timeout' => 20
],
'endpoint' => 'https://bucket.0123xyz.s3.us-east-1.amazonaws.com'
]);
return $s3_connection;
}
In the above code the retries => 3 means it will try 3 times and then stop trying.
You can also set the timeout and connect_timeout as show above.
You can also retry manually by surrounding the method with do-while loop, as shown below,
public function s3_connect_with_retry($total_retry = 3){
$retry_count = 0;
$do_retry = false;
do{
try{
$s3_connection = $this->get_s3_connection();
}catch(Exception $exception){
$retry_count++;
$do_retry = true;
}
}while($total_retry < $retry && $do_retry == true;);
}
When the connection breaks and throws exception the $do_retry value will become true and the $retry_count will increase. based on the $do_retry flag the retry happens until the $total_retry count is reached.
I have an online software that sends emails to Amazon SES. Currently I have a cron job that sends the emails via the SMTP with phpmailer to send the messages. Currently I have to max the send limit to around 300 every minute to make sure my server doesn't time out. We see growth and eventually I'd like to send out to 10,000 or more.
Is there a better way to send to Amazon SES, or is this what everyone else does, but with just more servers running the workload?
Thanks in advance!
You can try using the AWS SDK for PHP. You can send emails through the SES API, and the SDK allows you to send multiple emails in parallel. Here is a code sample (untested and only partially complete) to get you started.
<?php
require 'vendor/autoload.php';
use Aws\Ses\SesClient;
use Guzzle\Service\Exception\CommandTransferException;
$ses = SesClient::factory(/* ...credentials... */);
$emails = array();
// #TODO SOME SORT OF LOGIC THAT POPULATES THE ABOVE ARRAY
$emailBatch = new SplQueue();
$emailBatch->setIteratorMode(SplQueue::IT_MODE_DELETE);
while ($emails) {
// Generate SendEmail commands to batch
foreach ($emails as $email) {
$emailCommand = $ses->getCommand('SendEmail', array(
// GENERATE COMMAND PARAMS FROM THE $email DATA
));
$emailBatch->enqueue($emailCommand);
}
try {
// Send the batch
$successfulCommands = $ses->execute(iterator_to_array($emailBatch));
} catch (CommandTransferException $e) {
$successfulCommands = $e->getSuccessfulCommands();
// Requeue failed commands
foreach ($e->getFailedCommands() as $failedCommand) {
$emailBatch->enqueue($failedCommand);
}
}
foreach ($successfulCommands as $command) {
echo 'Sent message: ' . $command->getResult()->get('MessageId') . "\n";
}
}
// Also Licensed under version 2.0 of the Apache License.
You could also look into using the Guzzle BatchBuilder and friends to make it more robust.
There are a lot of things you will need to fine tune with this code, but you may be able to achieve higher throughput of emails.
If anyone is looking for this answer, its outdated and you can find the new documentation here: https://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/commands.html
use Aws\S3\S3Client;
use Aws\CommandPool;
// Create the client.
$client = new S3Client([
'region' => 'us-standard',
'version' => '2006-03-01'
]);
$bucket = 'example';
$commands = [
$client->getCommand('HeadObject', ['Bucket' => $bucket, 'Key' => 'a']),
$client->getCommand('HeadObject', ['Bucket' => $bucket, 'Key' => 'b']),
$client->getCommand('HeadObject', ['Bucket' => $bucket, 'Key' => 'c'])
];
$pool = new CommandPool($client, $commands);
// Initiate the pool transfers
$promise = $pool->promise();
// Force the pool to complete synchronously
$promise->wait();
Same thing can be done for SES commands
Thank you for your answer. It was a good starting point. #Jeremy Lindblom
My problem is now that i can't get the Error-Handling to work.
The catch()-Block works fine and inside of it
$successfulCommands
returns all the succeed Responses with Status-Codes, but only if an error occurs. For example "unverified address" in Sandbox-Mode. Like a catch() should work. :)
The $successfulCommands inside the try-Block only returns:
SplQueue Object
(
[flags:SplDoublyLinkedList:private] => 1
[dllist:SplDoublyLinkedList:private] => Array
(
)
)
I can't figure it out how to get the real Response from Amazon with Status-Codes etc.