Serverless AWS

For starter with AWS, Serverless Framework CLI is a must. Build your entire infrastructure in seconds !

What does AWS mean ? Amazon Web Service.

What does AWS provide ?  provides on-demand cloud computing platforms and APIs to individuals, companies, and governments, on a metered pay-as-you-go basis such as hosting web server

What is Serverless framework CLI ? . A single configuration file allows you to list your functions and define the endpoints that they’re subscribed to. It provides structure, automation and best practices out-of-the-box, allowing you to focus on building sophisticated, event-driven, serverless architectures, comprised of functions and events. The Serverless Framework CLI is the simplest way to develop infinitely scalable, pay-per-execution serverless applications.

  • Big plus is the serverless.yaml creating the above structure in seconds

I built an architecture SQS, DLQ, SNS and Lambda with serverless and it is fantastic for deployment and quick development. This framework made the whole development experience smooth

What is SQS ? Simple Queue Service (SQS) is a fully managed message queuing service that enables you to decouple and scale microservices, distributed systems, and serverless applications. 

What is DLQ ? You can now set a dead-letter queue (DLQ) to an Amazon Simple Notification Service (SNS) subscription to capture undeliverable messages. Amazon SNS DLQs make your application more resilient and durable by storing messages in case your subscription endpoint becomes unreachable.

What is SNS ? Amazon SNS is a fully managed pub/sub messaging service. You can use Amazon SNS topics to decouple message publishers and subscribers, and simultaneously distribute messages to multiple endpoints, such as Amazon SQS queues, AWS Lambda functions, HTTP endpoints, email addresses, and mobile devices (SMS text messages and mobile push notifications).

What is Lambda ? AWS Lambda is a serverless compute service that runs your code in response to events and automatically manages the underlying compute resources for you. 

See below some useful Links

— Tutorial to quick start with serverless —

https://www.serverless.com/framework/docs/providers/aws/guide/quick-start/

David Raleche

AWS – SQS – LAMBDA – CloudWatch – Slack – DLQ – SNS

I am currently working on a transactional platform where orders are pushed to a Queue SQS then pulled and processed by lambda.

In a problematic situation , lambda triggers AWS SNS which will notify sysOp via email and a Slack Channel. Awesome ! And more importantly a DLQ – Dead-Letter Queue will contain workers waiting to be reprocessed and sent to our Technical Customer Service Department to analyze what went wrong

SOLUTION – POST API CALL in AWS SQS-LAMBDA – NODEJS – Javascript

If you have been struggling in amazon aws with the combo Lambda-SQS NodeJS fixing following type of errors :

read ECONNRESET at TLSWrap.onStreamRead

Error: read ECONNRESET at TLSWrap.onStreamRead

 

ERROR Uncaught Exception {“errorType”:”Error”,”errorMessage”:”getaddrinfo ENOTFOUND https://xxxx.xxxxxr.com https://xxxxx.xxxxx.com:443″,”code”:”ENOTFOUND”,”stack”:[“Error: getaddrinfo ENOTFOUND

 

ERROR Invoke Error {“errorType”:”TypeError [ERR_INVALID_ARG_TYPE]”,”errorMessage”:”The first argument must be one

Here is the solution

exports.handler = async (event) => {
     try {
         const res = await axios.post('https://davidraleche.com/v1/test',JSON.stringify({}), {
         headers: {
             'Content-Type': 'application/json'
         }})
         console.log(res)
         return {
             statusCode: 200,
             body: res.data
         }
     } catch (e) {
         console.log(e)
         return {
             statusCode: 400,
             body: JSON.stringify(e)
         }
     }
};

 

AWS SQS Library

Image result for aws

Interface Contract

<?php

namespace David\App\Models;

interface Queue
{
    public function push(array $message);
    public function pop() : array;
    public function flush(string $queueUrl);
    public function process();
}

 

QueueService Class

<?php

namespace App\Core\Services;

use App\Core\Prelude;
use Aws\Sqs\SqsClient;
use Mapi\App\Models\Queue;

/**
 * Class QueueService
 *
 * @package App\Core\Services
 */
class QueueService extends \App\Core\Classes\Service implements Queue
{
    /**
     * QueueConfigs
     *
     * @var bool
     */
    private $queueConfigs = false;
    /**
     * DbConnections
     *
     * @var bool
     */
    private $dbConnections = false;
    /**
     * SqsClient
     *
     * @var SqsClient|bool
     */
    private $sqsClient  = false;

    /**
     * GetSqsClient
     *
     * @return SqsClient|bool|static
     */
    public function getSqsClient()
    {
        return $this->sqsClient;
    }

    /**
     * SetSqsClient
     *
     * @param SqsClient|bool|static $sqsClient
     */
    public function setSqsClient($sqsClient)
    {
        $this->sqsClient = $sqsClient;
    }

    /**
     * StorageService constructor.
     */
    public function __construct($queue_name, Prelude $prelude)
    {
        if ($queue_name === null) {
            die(
                'QueueService::constructor queue_name does not exist');
        }
        $this->prelude = $prelude;
        $this->queues = $this->queueConfigs()['queues'];
        $this->selectedQueue =  $this->queues[$queue_name];
        $this->queueUrl = $this->selectedQueue['queue_url'] ;
        $this->sqsCredentials = array(
            'region' => $this->selectedQueue['region'],
            'version' => $this->selectedQueue['version'],
            'credentials' => array(
                'key' => $this->selectedQueue['key'],
                'secret' =>$this->selectedQueue['secret'],
            ),
        );

        $this->sqsClient = $this->queueConnect($queue_name);
    }

    /**
     * Storage Configs
     *
     * @return bool|mixed
     */
    private function queueConfigs()
    {
        if ($this->queueConfigs === false) {
            $this->queueConfigs = yaml_parse_file(__DIR__.'/../../config/storage.yaml');
        }

        return $this->queueConfigs;
    }

    /**
     * Queue connection
     *
     * @param string $schema
     * @param bool $allowShared
     */
    public function queueConnect()
    {
        try {
            // Instantiate the client
            $sqsClient = SqsClient::factory($this->sqsCredentials);
        } catch (Exception $e) {
            die('Error Connecting to the Queue '.$e->getMessage());
        }

        return $sqsClient;
    }

    /**
     * Push
     *
     * @param string $jsonMessage
     *
     * @return bool
     */
    public function push(array $params) : bool
    {
        try {
            // Send the message
            $this->sqsClient->sendMessage(
                $params
            );
        } catch (Exception $e) {
            die('Error sending message to queue '.$e->getMessage());
            return false;
        }
        return true;
    }

    public function deleteMessage($result)
    {
        $this->sqsClient->deleteMessage(
            [
                'QueueUrl' => $this->queueUrl, // REQUIRED
                'ReceiptHandle' => $result->get('Messages')[0]['ReceiptHandle'] // REQUIRED
            ]
        );
    }

    /**
     * Pop Message
     *
     * @return array
     */
    public function pop() : array
    {
        $resultMessage = array();
        try {
            $result = $this->sqsClient->receiveMessage(
                array(
                    'AttributeNames' => ['SentTimestamp'],
                    'MaxNumberOfMessages' => 1,
                    'MessageAttributeNames' => ['All'],
                    'QueueUrl' => $this->queueUrl,
                    'WaitTimeSeconds' => 0,
                )
            );
            //Logging Pending Queue Messages
            $this->prelude->log->debug('QueueService:pop COUNT MESSAGES '
                    .count($result->getPath('Messages')));

            if (is_array($result->get('Messages'))) {
                if (count($result->get('Messages')) > 0) {
//                    $this->prelude->log->debug('QueueService:pop'
//                        .json_encode($result->get('Messages')));

                    $status = true;
                    $resultMessage = $result->get('Messages')[0]['Body'];
                    //Delete Message
                    $this->deleteMessage($result);
                }
            } else {
                $status = false;
                $resultMessage = "No messages in queue. \n";
            }
        } catch (Exception $e) {
            die(' message to queue '.$e->getMessage());
        }
        // Add Debugging SQS QUEUE
        $this->prelude->log->debug('WORKER BODY MESSAGE '.$resultMessage);
        return array('status' => $status, 'result' => $resultMessage);
    }