├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml.dist
├── src
├── Controllers
│ ├── LaravelController.php
│ └── WorkerController.php
├── Exceptions
│ ├── ExpiredJobException.php
│ └── MalformedRequestException.php
├── Integrations
│ ├── BindsWorker.php
│ ├── LaravelServiceProvider.php
│ └── LumenServiceProvider.php
├── Jobs
│ ├── AwsJob.php
│ └── CallQueuedHandler.php
└── Wrappers
│ ├── DefaultWorker.php
│ ├── Laravel53Worker.php
│ ├── Laravel6Worker.php
│ ├── Laravel8Worker.php
│ └── WorkerInterface.php
└── tests
└── bootstrap.php
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - '7.0'
5 |
6 | before_script:
7 | - composer install --dev
8 |
9 | addons:
10 | code_climate:
11 | repo_token: 989ab71e9103d594cabbd93fe473ff9c92ad76bb80c55c57d2a9c436e28a3f14
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Denis Mysenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # laravel-aws-worker
2 | [](https://codeclimate.com/github/dusterio/laravel-aws-worker/badges)
3 | [](https://packagist.org/packages/dusterio/laravel-aws-worker)
4 | [](https://packagist.org/packages/dusterio/laravel-aws-worker)
5 | [](https://packagist.org/packages/dusterio/laravel-aws-worker)
6 | [](https://packagist.org/packages/dusterio/laravel-plain-sqs)
7 |
8 | Run Laravel tasks and queue listeners inside of AWS Elastic Beanstalk workers
9 |
10 | > We've dropped future support for Lumen, however you can still use [v0.1.40](https://github.com/dusterio/laravel-aws-worker/releases/tag/v0.1.40) for Lumen.
11 |
12 | ## Overview
13 |
14 | Laravel documentation recommends to use supervisor for queue workers and *IX cron for scheduled tasks. However, when deploying your application to AWS Elastic Beanstalk, neither option is available.
15 |
16 | This package helps you run your Laravel jobs in AWS worker environments.
17 |
18 | 
19 | 
20 |
21 | ## Dependencies
22 |
23 | * PHP >= 5.5
24 | * Laravel >= 5.1
25 |
26 | ## Scheduled tasks - option 1
27 |
28 | Option one is to use Kernel.php as the schedule and run Laravel schedule runner every minute.
29 | You remember how Laravel documentation advised you to invoke the task scheduler? Right, by running ```php artisan schedule:run``` on regular basis, and to do that we had to add an entry to our cron file:
30 |
31 | ```bash
32 | * * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
33 | ```
34 |
35 | AWS doesn't allow you to run *IX commands or to add cron tasks directly. Instead, you have to make regular HTTP (POST, to be precise) requests to your worker endpoint.
36 |
37 | Add cron.yaml to the root folder of your application (this can be a part of your repo or you could add this file right before deploying to EB - the important thing is that this file is present at the time of deployment):
38 |
39 | ```yaml
40 | version: 1
41 | cron:
42 | - name: "schedule"
43 | url: "/worker/schedule"
44 | schedule: "* * * * *"
45 | ```
46 |
47 | From now on, AWS will do POST /worker/schedule to your endpoint every minute - kind of the same effect we achieved when editing a UNIX cron file. The important difference here is that the worker environment still has to run a web process in order to execute scheduled tasks.
48 | Behind the scenes it will do something very similar to a built-in `schedule:run` command.
49 |
50 | Your scheduled tasks should be defined in ```App\Console\Kernel::class``` - just where they normally live in Laravel, eg.:
51 |
52 | ```php
53 | protected function schedule(Schedule $schedule)
54 | {
55 | $schedule->command('inspire')
56 | ->everyMinute();
57 | }
58 | ```
59 |
60 | ## Scheduled tasks - option 2
61 |
62 | Option two is to use AWS schedule defined in the cron.yml:
63 |
64 | ```yaml
65 | version: 1
66 | cron:
67 | - name: "run:command"
68 | url: "/worker/schedule"
69 | schedule: "0 * * * *"
70 |
71 | - name: "do:something --param=1 -v"
72 | url: "/worker/schedule"
73 | schedule: "*/5 * * * *"
74 | ```
75 |
76 | Note that AWS will use UTC timezone for cron expressions. With the above example,
77 | AWS will hit /worker/schedule endpoint every hour with `run:command` artisan command and every
78 | 5 minutes with `do:something` command. Command parameters aren't supported at this stage.
79 |
80 | Pick whichever option is better for you!
81 |
82 | ## Queued jobs: SQS
83 |
84 | Normally Laravel has to poll SQS for new messages, but in case of AWS Elastic Beanstalk messages will come to us – inside of POST requests from the AWS daemon.
85 |
86 | Therefore, we will create jobs manually based on SQS payload that arrived, and pass that job to the framework's default worker. From this point, the job will be processed the way it's normally processed in Laravel. If it's processed successfully,
87 | our controller will return a 200 HTTP status and AWS daemon will delete the job from the queue. Again, we don't need to poll for jobs and we don't need to delete jobs - that's done by AWS in this case.
88 |
89 | If you dispatch jobs from another instance of Laravel or if you are following Laravel's payload format ```{"job":"","data":""}``` you should be okay to go. If you want to receive custom format JSON messages, you may want to install
90 | [Laravel plain SQS](https://github.com/dusterio/laravel-plain-sqs) package as well.
91 |
92 | ## Configuring the queue
93 |
94 | Every time you create a worker environment in AWS, you are forced to choose two SQS queues – either automatically generated ones or some of your existing queues. One of the queues will be for the jobs themselves, another one is for failed jobs – AWS calls this queue a dead letter queue.
95 |
96 | You can set your worker queues either during the environment launch or anytime later in the settings:
97 |
98 | 
99 |
100 | Don't forget to set the HTTP path to ```/worker/queue``` – this is where AWS will hit our application. If you chose to generate queues automatically, you can see their details later in SQS section of the AWS console:
101 |
102 | 
103 |
104 | You have to tell Laravel about this queue. First set your queue driver to SQS in ```.env``` file:
105 |
106 | ```
107 | QUEUE_DRIVER=sqs
108 | ```
109 |
110 | Then go to ```config/queue.php``` and copy/paste details from AWS console:
111 |
112 | ```php
113 | ...
114 | 'sqs' => [
115 | 'driver' => 'sqs',
116 | 'key' => 'your-public-key',
117 | 'secret' => 'your-secret-key',
118 | 'prefix' => 'https://sqs.us-east-1.amazonaws.com/your-account-id',
119 | 'queue' => 'your-queue-name',
120 | 'region' => 'us-east-1',
121 | ],
122 | ...
123 | ```
124 |
125 | To generate key and secret go to Identity and Access Management in the AWS console. It's better to create a separate user that ONLY has access to SQS.
126 |
127 | ## Installation via Composer
128 |
129 | To install simply run:
130 |
131 | ```
132 | composer require dusterio/laravel-aws-worker
133 | ```
134 |
135 | Or add it to `composer.json` manually:
136 |
137 | ```json
138 | {
139 | "require": {
140 | "dusterio/laravel-aws-worker": "~0.1"
141 | }
142 | }
143 | ```
144 |
145 | ### Usage in Laravel 5
146 |
147 | ```php
148 | // Add in your config/app.php
149 |
150 | 'providers' => [
151 | '...',
152 | 'Dusterio\AwsWorker\Integrations\LaravelServiceProvider',
153 | ];
154 | ```
155 |
156 | After adding service provider, you should be able to see two special routes that we added:
157 |
158 | ```bash
159 | $ php artisan route:list
160 | +--------+----------+-----------------+------+----------------------------------------------------------+------------+
161 | | Domain | Method | URI | Name | Action | Middleware |
162 | +--------+----------+-----------------+------+----------------------------------------------------------+------------+
163 | | | POST | worker/queue | | Dusterio\AwsWorker\Controllers\WorkerController@queue | |
164 | | | POST | worker/schedule | | Dusterio\AwsWorker\Controllers\WorkerController@schedule | |
165 | +--------+----------+-----------------+------+----------------------------------------------------------+------------+
166 | ```
167 |
168 | Environment variable ```REGISTER_WORKER_ROUTES``` is used to trigger binding of the two routes above. If you run the same application in both web and worker environments,
169 | don't forget to set ```REGISTER_WORKER_ROUTES``` to ```false``` in your web environment. You don't want your regular users to be able to invoke scheduler or queue worker.
170 |
171 | This variable is set to ```true``` by default at this moment.
172 |
173 | So that's it - if you (or AWS) hits ```/worker/queue```, Laravel will process one queue item (supplied in the POST). And if you hit ```/worker/schedule```, we will run the scheduler (it's the same as to run ```php artisan schedule:run``` in shell).
174 |
175 | ### Usage in Lumen 5
176 |
177 | ```php
178 | // Add in your bootstrap/app.php
179 | $app->register(Dusterio\AwsWorker\Integrations\LumenServiceProvider::class);
180 | ```
181 |
182 | ## Errors and exceptions
183 |
184 | Please make sure that two special routes are not mounted behind a CSRF middleware. Our POSTs are not real web forms and CSRF is not necessary here. If you have a global CSRF middleware, add these routes to exceptions, or otherwise apply CSRF to specific routes or route groups.
185 |
186 | If your job fails, we will throw a ```FailedJobException```. If you want to customize error output – just customise your exception handler.
187 | Note that your HTTP status code must be different from 200 in order for AWS to realize the job has failed.
188 |
189 | ## Job expiration (retention)
190 |
191 | A new nice feature is being able to set a job expiration (retention in AWS terms) in seconds so
192 | that low value jobs get skipped completely if there is temporary queue latency due to load.
193 |
194 | Let's say we have a spike in queued jobs and some of them don't even make sense anymore
195 | now that a few minutes passed – we don't want to spend computing resources processing them
196 | later.
197 |
198 | By setting a special property on a job or a listener class:
199 | ```php
200 | class PurgeCache implements ShouldQueue
201 | {
202 | public static int $retention = 300; // If this job is delayed more than 300 seconds, skip it
203 | }
204 | ```
205 |
206 | We can make sure that we won't run this job later than 300 seconds since it's been queued.
207 | This is similar to AWS SQS "message retention" setting which can only be set globally for
208 | the whole queue.
209 |
210 | To use this new feature, you have to use provided ```Jobs\CallQueuedHandler``` class that
211 | extends Laravel's default ```CallQueuedHandler```. A special ```ExpiredJobException``` exception
212 | will be thrown when expired jobs arrive and then it's up to you what to do with them.
213 |
214 | If you just catch these exceptions and therefore stop Laravel from returning code 500
215 | to AWS daemon, the job will be deleted by AWS automatically.
216 |
217 | ## ToDo
218 |
219 | 1. Add support for AWS dead letter queue (retry jobs from that queue?)
220 |
221 | ## Video tutorials
222 |
223 | I've just started a educational YouTube channel that will cover top IT trends in software development and DevOps: [config.sys](https://www.youtube.com/channel/UCIvUJ1iVRjJP_xL0CD7cMpg)
224 |
225 | Also I'm glad to announce a new cool tool of mine – [GrammarCI](https://www.grammarci.com/), an automated typo/grammar checker for developers, as a part of the CI/CD pipeline.
226 |
227 | ## Implications
228 |
229 | Note that AWS cron doesn't promise 100% time accuracy. Since cron tasks share the same queue with other jobs, your scheduled tasks may be processed later than expected.
230 |
231 | ## Post scriptum
232 |
233 | I wrote a [blog post](https://web.archive.org/web/20210616063916/https://blog.menara.com.au/2016/06/running-laravel-in-amazon-elastic-beanstalk/) explaining how this actually works.
234 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dusterio/laravel-aws-worker",
3 | "type": "library",
4 | "description": "Run Laravel (or Lumen) tasks and queue listeners inside of AWS Elastic Beanstalk workers",
5 | "keywords": [
6 | "php",
7 | "laravel",
8 | "lumen",
9 | "sqs",
10 | "aws",
11 | "elasticbeanstalk",
12 | "worker"
13 | ],
14 | "homepage": "https://github.com/dusterio/laravel-aws-worker",
15 | "license": "MIT",
16 | "authors": [
17 | {
18 | "name": "Denis Mysenko",
19 | "email": "denis@mysenko.com",
20 | "homepage": "https://www.mysenko.com"
21 | }
22 | ],
23 | "require": {
24 | "php": ">=5.5.0",
25 | "illuminate/support": "5.*|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
26 | "illuminate/queue": "5.*|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
27 | "illuminate/bus": "5.*|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
28 | "aws/aws-sdk-php": "~3.0",
29 | "illuminate/http": "5.*|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0"
30 | },
31 | "require-dev": {
32 | "phpunit/phpunit": "3.7.*|^9.5.10|^10.5",
33 | "codeclimate/php-test-reporter": "dev-master"
34 | },
35 | "autoload": {
36 | "psr-4": {
37 | "Dusterio\\AwsWorker\\": "src/"
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | tests/PlainSqs/
16 |
17 |
18 |
19 |
20 |
21 | src/
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/Controllers/LaravelController.php:
--------------------------------------------------------------------------------
1 | middleware[$middleware] = $options;
24 | }
25 |
26 | /**
27 | * Get the middleware assigned to the controller.
28 | *
29 | * @return array
30 | */
31 | public function getMiddleware()
32 | {
33 | return $this->middleware;
34 | }
35 |
36 | /**
37 | * Execute an action on the controller.
38 | *
39 | * @param string $method
40 | * @param array $parameters
41 | * @return \Symfony\Component\HttpFoundation\Response
42 | */
43 | public function callAction($method, $parameters)
44 | {
45 | return call_user_func_array([$this, $method], $parameters);
46 | }
47 |
48 | /**
49 | * Get the registered "after" filters.
50 | *
51 | * @return array
52 | *
53 | * @deprecated since version 5.1.
54 | */
55 | public function getAfterFilters()
56 | {
57 | return [];
58 | }
59 |
60 | /**
61 | * Get the registered "before" filters.
62 | *
63 | * @return array
64 | *
65 | * @deprecated since version 5.1.
66 | */
67 | public function getBeforeFilters()
68 | {
69 | return [];
70 | }
71 |
72 | /**
73 | * Get the middleware for a given method.
74 | *
75 | * @param string $method
76 | * @return array
77 | */
78 | public function getMiddlewareForMethod($method)
79 | {
80 | $middleware = [];
81 |
82 | foreach ($this->middleware as $name => $options) {
83 | if (isset($options['only']) && !in_array($method, (array)$options['only'])) {
84 | continue;
85 | }
86 |
87 | if (isset($options['except']) && in_array($method, (array)$options['except'])) {
88 | continue;
89 | }
90 |
91 | $middleware[] = $name;
92 | }
93 |
94 | return $middleware;
95 | }
96 | }
97 |
98 |
--------------------------------------------------------------------------------
/src/Controllers/WorkerController.php:
--------------------------------------------------------------------------------
1 | headers->get('X-Aws-Sqsd-Taskname', $this::LARAVEL_SCHEDULE_COMMAND);
43 | if ($command != $this::LARAVEL_SCHEDULE_COMMAND) return $this->runSpecificCommand($kernel, $request->headers->get('X-Aws-Sqsd-Taskname'));
44 |
45 | $kernel->bootstrap();
46 |
47 | $events = $schedule->dueEvents($laravel);
48 | $eventsRan = 0;
49 | $messages = [];
50 |
51 | foreach ($events as $event) {
52 | if (method_exists($event, 'filtersPass') && (new \ReflectionMethod($event, 'filtersPass'))->isPublic() && ! $event->filtersPass($laravel)) {
53 | continue;
54 | }
55 |
56 | $messages[] = 'Running: '.$event->getSummaryForDisplay();
57 |
58 | $event->run($laravel);
59 |
60 | ++$eventsRan;
61 | }
62 |
63 | if (count($events) === 0 || $eventsRan === 0) {
64 | $messages[] = 'No scheduled commands are ready to run.';
65 | }
66 |
67 | return $this->response($messages);
68 | }
69 |
70 | /**
71 | * @param string $command
72 | * @return array
73 | */
74 | protected function parseCommand($command)
75 | {
76 | $elements = explode(' ', $command);
77 | $name = $elements[0];
78 | if (count($elements) == 1) return [$name, []];
79 |
80 | array_shift($elements);
81 | $arguments = [];
82 |
83 | array_map(function($parameter) use (&$arguments) {
84 | if (strstr($parameter, '=')) {
85 | $parts = explode('=', $parameter);
86 | $arguments[$parts[0]] = $parts[1];
87 | return;
88 | }
89 |
90 | $arguments[$parameter] = true;
91 | }, $elements);
92 |
93 | return [
94 | $name,
95 | $arguments
96 | ];
97 | }
98 |
99 | /**
100 | * @param Kernel $kernel
101 | * @param $command
102 | * @return Response
103 | */
104 | protected function runSpecificCommand(Kernel $kernel, $command)
105 | {
106 | list ($name, $arguments) = $this->parseCommand($command);
107 |
108 | $exitCode = $kernel->call($name, $arguments);
109 |
110 | return $this->response($exitCode);
111 | }
112 |
113 | /**
114 | * @param Request $request
115 | * @param WorkerInterface $worker
116 | * @param Container $laravel
117 | * @param ExceptionHandler $exceptions
118 | * @return Response
119 | */
120 | public function queue(Request $request, WorkerInterface $worker, Container $laravel, ExceptionHandler $exceptions)
121 | {
122 | //$this->validateHeaders($request);
123 | $body = $this->validateBody($request, $laravel);
124 |
125 | $job = new AwsJob($laravel, $request->header('X-Aws-Sqsd-Queue'), [
126 | 'Body' => $body,
127 | 'MessageId' => $request->header('X-Aws-Sqsd-Msgid'),
128 | 'ReceiptHandle' => false,
129 | 'Attributes' => [
130 | 'ApproximateReceiveCount' => $request->header('X-Aws-Sqsd-Receive-Count'),
131 | 'SentTimestamp' => $request->headers->has('X-Aws-Sqsd-First-Received-At') ? strtotime($request->header('X-Aws-Sqsd-First-Received-At')) * 1000 : null
132 | ]
133 | ]);
134 |
135 | $worker->process(
136 | $request->header('X-Aws-Sqsd-Queue'), $job, array_merge([
137 | 'delay' => 0
138 | ], $this->tryToExtractOptions($body))
139 | );
140 |
141 | return $this->response([
142 | 'Processed ' . $job->getJobId()
143 | ]);
144 | }
145 |
146 | /**
147 | * @param $input
148 | * @return int
149 | */
150 | private function tryToExtractOptions($input)
151 | {
152 | $parameters = [
153 | 'maxTries' => 0,
154 | 'timeout' => 60
155 | ];
156 |
157 | // Try to extract listener class options from the serialized job body
158 | if (preg_match('/tries\\\\\\\\\\\\";i:(?\d+);/', $input, $matches)) {
159 | $parameters['maxTries'] = intval($matches['attempts']);
160 | }
161 |
162 | if (preg_match('/timeout\\\\\\\\\\\\";i:(?\d+);/', $input, $matches)) {
163 | $parameters['timeout'] = intval($matches['timeout']);
164 | }
165 |
166 | return $parameters;
167 | }
168 |
169 | /**
170 | * @param Request $request
171 | * @throws MalformedRequestException
172 | */
173 | private function validateHeaders(Request $request)
174 | {
175 | foreach ($this->awsHeaders as $header) {
176 | if (! $this->hasHeader($request, $header)) {
177 | throw new MalformedRequestException('Missing AWS header: ' . $header);
178 | }
179 | }
180 | }
181 |
182 | /**
183 | * @param Request $request
184 | * @param $header
185 | * @return bool
186 | */
187 | private function hasHeader(Request $request, $header)
188 | {
189 | if (method_exists($request, 'hasHeader')) {
190 | return $request->hasHeader($header);
191 | }
192 |
193 | return $request->header($header, false);
194 | }
195 |
196 | /**
197 | * @param Request $request
198 | * @param Container $laravel
199 | * @return string
200 | * @throws MalformedRequestException
201 | */
202 | private function validateBody(Request $request, Container $laravel)
203 | {
204 | if (empty($request->getContent())) {
205 | throw new MalformedRequestException('Empty request body');
206 | }
207 |
208 | $job = json_decode($request->getContent(), true);
209 | if ($job === null) throw new MalformedRequestException('Unable to decode request JSON');
210 |
211 | if (isset($job['job']) && isset($job['data'])) return $request->getContent();
212 |
213 | // If the format is not the standard Laravel format, try to mimic it
214 | $queueId = explode('/', $request->header('X-Aws-Sqsd-Queue'));
215 | $queueId = array_pop($queueId);
216 |
217 | $class = (array_key_exists($queueId, $laravel['config']->get('sqs-plain.handlers')))
218 | ? $laravel['config']->get('sqs-plain.handlers')[$queueId]
219 | : $laravel['config']->get('sqs-plain.default-handler');
220 |
221 | return json_encode([
222 | 'job' => $class . '@handle',
223 | 'data' => $request->getContent()
224 | ]);
225 | }
226 |
227 | /**
228 | * @param array $messages
229 | * @param int $code
230 | * @return Response
231 | */
232 | private function response($messages = [], $code = 200)
233 | {
234 | return new Response(json_encode([
235 | 'message' => $messages,
236 | 'code' => $code
237 | ]), $code);
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/src/Exceptions/ExpiredJobException.php:
--------------------------------------------------------------------------------
1 | Laravel53Worker::class,
24 | '[67]\.\d+\.\d+' => Laravel6Worker::class,
25 | '([89]|10|11|12)\.\d+\.\d+' => Laravel8Worker::class
26 | ];
27 |
28 | /**
29 | * @param $version
30 | * @return mixed
31 | */
32 | protected function findWorkerClass($version)
33 | {
34 | foreach ($this->workerImplementations as $regexp => $class) {
35 | if (preg_match('/' . $regexp . '/', $version)) return $class;
36 | }
37 |
38 | return DefaultWorker::class;
39 | }
40 |
41 | /**
42 | * @return void
43 | */
44 | protected function bindWorker()
45 | {
46 | // If Laravel version is 6 or above then the worker bindings change. So we initiate it here
47 | if ((int) $this->app->version() >= 6) {
48 | $this->app->singleton(Worker::class, function () {
49 | $isDownForMaintenance = function () {
50 | return $this->app->isDownForMaintenance();
51 | };
52 |
53 | return new Worker(
54 | $this->app['queue'],
55 | $this->app['events'],
56 | $this->app[ExceptionHandler::class],
57 | $isDownForMaintenance
58 | );
59 | });
60 | }
61 |
62 | $this->app->bind(WorkerInterface::class, $this->findWorkerClass($this->app->version()));
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Integrations/LaravelServiceProvider.php:
--------------------------------------------------------------------------------
1 | bindWorker();
30 | $this->addRoutes();
31 | }
32 |
33 | /**
34 | * @return void
35 | */
36 | protected function addRoutes()
37 | {
38 | $this->app['router']->post('/worker/schedule', 'Dusterio\AwsWorker\Controllers\WorkerController@schedule');
39 | $this->app['router']->post('/worker/queue', 'Dusterio\AwsWorker\Controllers\WorkerController@queue');
40 | }
41 |
42 | /**
43 | * @return void
44 | */
45 | public function boot()
46 | {
47 | $this->app->singleton(QueueManager::class, function() {
48 | return new QueueManager($this->app);
49 | });
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Integrations/LumenServiceProvider.php:
--------------------------------------------------------------------------------
1 | bindWorker();
29 | $this->addRoutes(isset( $this->app->router ) ? $this->app->router : $this->app );
30 | }
31 |
32 | /**
33 | * @param mixed $router
34 | * @return void
35 | */
36 | protected function addRoutes($router)
37 | {
38 | $router->post('/worker/schedule', 'Dusterio\AwsWorker\Controllers\WorkerController@schedule');
39 | $router->post('/worker/queue', 'Dusterio\AwsWorker\Controllers\WorkerController@queue');
40 | }
41 |
42 | /**
43 | * @return void
44 | */
45 | public function boot()
46 | {
47 | $this->app->singleton(QueueManager::class, function() {
48 | return new QueueManager($this->app);
49 | });
50 |
51 | // If lumen version is 6 or above then the worker bindings change. So we initiate it here
52 | if (preg_match('/Lumen \(6/', $this->app->version())) {
53 | $this->app->singleton(Worker::class, function () {
54 | $isDownForMaintenance = function () {
55 | return $this->app->isDownForMaintenance();
56 | };
57 |
58 | return new Worker(
59 | $this->app['queue'],
60 | $this->app['events'],
61 | $this->app[ExceptionHandler::class],
62 | $isDownForMaintenance
63 | );
64 | });
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Jobs/AwsJob.php:
--------------------------------------------------------------------------------
1 | job = $job;
30 | $this->queue = $queue;
31 | $this->container = $container;
32 | }
33 |
34 | /**
35 | * Fire the job.
36 | *
37 | * @return void
38 | */
39 | public function fire()
40 | {
41 | if (method_exists($this, 'resolveAndFire')) {
42 | $this->resolveAndFire(json_decode($this->getRawBody(), true));
43 | return;
44 | }
45 |
46 | parent::fire();
47 | }
48 |
49 | /**
50 | * Get the raw body string for the job.
51 | *
52 | * @return string
53 | */
54 | public function getRawBody()
55 | {
56 | return $this->job['Body'];
57 | }
58 |
59 | /**
60 | * Actually, AWS will do this for us, we just need to mark the job as deleted
61 | *
62 | * @return void
63 | */
64 | public function delete()
65 | {
66 | parent::delete();
67 | }
68 |
69 | /**
70 | * AWS daemon will do this for us
71 | *
72 | * @param int $delay
73 | * @return void
74 | */
75 | public function release($delay = 0)
76 | {
77 | parent::release($delay);
78 | }
79 |
80 | /**
81 | * Get the number of times the job has been attempted.
82 | *
83 | * @return int
84 | */
85 | public function attempts()
86 | {
87 | return (int) $this->job['Attributes']['ApproximateReceiveCount'];
88 | }
89 |
90 | /**
91 | * Get the UNIX timestamp of the message creation.
92 | *
93 | * @return int
94 | */
95 | public function timestamp()
96 | {
97 | return (int) round($this->job['Attributes']['SentTimestamp'] / 1000);
98 | }
99 |
100 | /**
101 | * Get the job identifier.
102 | *
103 | * @return string
104 | */
105 | public function getJobId()
106 | {
107 | return $this->job['MessageId'];
108 | }
109 |
110 | /**
111 | * Get the IoC container instance.
112 | *
113 | * @return \Illuminate\Container\Container
114 | */
115 | public function getContainer()
116 | {
117 | return $this->container;
118 | }
119 |
120 | /**
121 | * We don't need an underlying SQS client instance.
122 | *
123 | * @return \Aws\Sqs\SqsClient
124 | */
125 | public function getSqs()
126 | {
127 | return null;
128 | }
129 |
130 | /**
131 | * Get the underlying raw SQS job.
132 | *
133 | * @return array
134 | */
135 | public function getSqsJob()
136 | {
137 | return $this->job;
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/Jobs/CallQueuedHandler.php:
--------------------------------------------------------------------------------
1 | hasExpired($command->class, $job->timestamp())) {
20 | throw new ExpiredJobException("Job {$command->class} has already expired");
21 | }
22 |
23 | return parent::dispatchThroughMiddleware($job, $command);
24 | }
25 |
26 | /**
27 | * @param $className
28 | * @param $queuedAt
29 | * @return bool
30 | */
31 | protected function hasExpired($className, $queuedAt) {
32 | if (! property_exists($className, 'retention')) {
33 | return false;
34 | }
35 |
36 | return time() > $queuedAt + $className::$retention;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Wrappers/DefaultWorker.php:
--------------------------------------------------------------------------------
1 | cache = $cache;
33 | $this->worker = $worker;
34 | }
35 |
36 | /**
37 | * @param $queue
38 | * @param $job
39 | * @param array $options
40 | * @return void
41 | */
42 | public function process($queue, $job, array $options)
43 | {
44 | $workerOptions = new WorkerOptions('default', $options['delay'], 128, $options['timeout'], 3, $options['maxTries']);
45 |
46 | $this->worker->process(
47 | $queue, $job, $workerOptions
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Wrappers/Laravel53Worker.php:
--------------------------------------------------------------------------------
1 | worker = $worker;
21 | }
22 |
23 | /**
24 | * @param $queue
25 | * @param $job
26 | * @param array $options
27 | * @return void
28 | */
29 | public function process($queue, $job, array $options)
30 | {
31 | $workerOptions = new WorkerOptions($options['delay'], 128, 60, 3, $options['maxTries']);
32 |
33 | $this->worker->process(
34 | $queue, $job, $workerOptions
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Wrappers/Laravel6Worker.php:
--------------------------------------------------------------------------------
1 | cache = $cache;
23 | $this->worker = $worker;
24 | }
25 |
26 | /**
27 | * @param $queue
28 | * @param $job
29 | * @param array $options
30 | * @return void
31 | */
32 | public function process($queue, $job, array $options)
33 | {
34 | $workerOptions = new WorkerOptions($options['delay'], 128, 60, 3, $options['maxTries']);
35 |
36 | $this->worker->process(
37 | $queue, $job, $workerOptions
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Wrappers/Laravel8Worker.php:
--------------------------------------------------------------------------------
1 | cache = $cache;
33 | $this->worker = $worker;
34 | }
35 |
36 | /**
37 | * @param $queue
38 | * @param $job
39 | * @param array $options
40 | * @return void
41 | */
42 | public function process($queue, $job, array $options)
43 | {
44 | $workerOptions = new WorkerOptions('default', $options['delay'], 128, $options['timeout'], 3, $options['maxTries']);
45 |
46 | $this->worker->process(
47 | $queue, $job, $workerOptions
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Wrappers/WorkerInterface.php:
--------------------------------------------------------------------------------
1 |