I just want to queue an email when an user registers. So I do this when the user posts the registration form:
Mail::queue('emails.activate', $data, function($message) use ($user)
{
$message->from('no-reply#mysite.com', 'Mysite.com');
$message->to($user->email, $user->username)->subject('Welcome');
});
The queue listener is running (php artisan queue:listen) and a supervisor process make sure it will restart if stopped.
It works, the user get the email but the HTTP response when registering is very slow, exactly as I would expect it to be if I was trying to directly send the email. If I comment all the queuing code above, the HTTP response time is just fine.
I use the sync driver in queue.app:
'default' => 'sync',
'connections' => array(
'sync' => array(
'driver' => 'sync',
),
etc...
At last, I run my own private server (Ubuntu) with postfix. Can someone help me figure out why the response is so slow while I'm queuing the email?
The sync driver runs its queued jobs just before Laravel ends its execution. That is why it is called the sync driver, you will need to change it to achieve desired functionality.
Related
I am creating an API that uses websockets for real-time communication. I use Laravel 8 with Pusher as the broadcast driver for the backend, Soketi as the web socket server on windows (development environment) and I use postman to test the requests.
The problem is, my connection to the Soketi web socket server Always crashes after about 2 minutes of connectivity with a 4201 Error: Connection was closed due to an unknown error. I would like the connection to last a bit longer or ideally indefinitely, at least I think that's how it should work.
Console Connection message:
Console Disconnection message:
Both Postman and the console show the error, Note the time from connection to disconnection.
I did a work around to force postman to reconnect if an unexpected error caused it to close but that doesn't reconnect to the channels that were subscribed to before disconnection.
In config\broadcasting.php I have:
'pusher' => [
'driver' => 'pusher',
'key' => env('PUSHER_APP_KEY'),
'secret' => env('PUSHER_APP_SECRET'),
'app_id' => env('PUSHER_APP_ID'),
'options' => [
'host' => env('PUSHER_HOST'),
'port' => env('PUSHER_PORT'),
'scheme' => env('PUSHER_SCHEME', 'http'),
'cluster' => env('PUSHER_APP_CLUSTER'),
'useTLS' => env('PUSHER_SCHEME') === 'https',
],
],
My Soketi config:
{
"debug": true,
"port": 6001,
"appManager.array.apps": [
{
"id": "SLMT",
"key": "1229272",
"secret": "sLq2dAswE&9q",
"webhooks": [
{
"url": "",
"event_types": ["channel_occupied"]
}
]
}
]
}
The key and secret values are only used for development that's why don't hide them. Any help is much appreciated beacuse this situation makes testing websocket events very tedious if the connection keeps breaking.
EDIT
I forgot to mention a crucial piece of information. My project is an API which uses Sanctum to authenticate users. It does not serve any Html. In case someone wants to reproduce.
Alright so I think I figured it out. I believe the socket connection closes due to an activity timeout.
How did I arrive at this conclusion?
I have another app which uses web sockets too but instead of an API it serves Html. Anyway I fired up the Soketi server but with different credentials specific to the Html project and I compared the console output with the API project, where the problem occurs. I noticed that on the Html app, which uses Laravel echo, the client constantly sends a pusher:ping event to the server every 30 seconds, and the server replies with a pusher:pong event.
The interesting part is that the pusher:pong event increments the _iddleStart: value under the Timeout property of the webSocket connection output, which essentially keeps the connection from closing due to inactivity. I think the reason for the 2 minutes delay comes from the _iddleTimeout:120000 property. (Please refer to the images on the question for clarity)
After this discovery I went back to my API application and made a pusher:ping event message on my Postman, which I then kept sending to the server Manually every minute, and the connection held intact!
The ping message that I mention looks like this on Postman:
{
"event":"pusher:ping",
"data":{}
}
So, why was it so hard to figure out in the first place?
I have to admit I had my suspicions but I didn't want to conclude anything without checking. The error shown by the server is not detailed or descriptive, it literally says Connection was closed due to an unknown error and there is no lookup table for the error code afaik.
What does this mean?
Well, this is a Postman problem in my opinion. A client is responsible for keeping the connection with the server open in a WebSockets implementation right? Postman should at least give an option to auto-send some messages for this sort of thing but I will cut them some slack because apparently their WebSocket Request is still in beta development.
I also feel there should be more detailed error info provided by the Soketi server output, but they too I will cut some slack because its a fairly new server app, which is actually great and very stable, if I might add.
Problem description:
I am unable to send SMS from AWS Lambda.
Controller Code
try {
$sms = AwsFacade::createClient('sns');
$result = $sms->publish([
'MessageAttributes' => [
'AWS.SNS.SMS.SenderID' => [
'DataType' => 'String',
'StringValue' => 'CyQuer',
],
'AWS.SNS.SMS.SMSType' => [
'DataType' => 'String',
'StringValue' => 'Transactional',
],
],
'Message' => "Hello John Doe".PHP_EOL."Use following Code to Login:".PHP_EOL."123456",
'PhoneNumber' => $phone,
]);
Log::info($result);
} catch (Exception $e) {
Log::error($e->getMessage());
}
Error message:
via web
{"message": "Internal server error"}Task timed out after 28.00 seconds
via Artisan
Task timed out after 120.00 seconds
Setup
Laravel application running on AWS Lambda using bref/laravel-bridge
IAM user for this application has been created. Locally everything works. Online everything works too except sending SMS.
Tried solutions:
The following packages were tried.
https://github.com/aws/aws-sdk-php
https://github.com/aws/aws-sdk-php-laravel
All the following described approaches worked locally on AWS Lambda but not.
Write AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY directly into config/aws.php
Write AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY directly in the code
$sms = AwsFacade::createClient('sns',[
'credentials' => [
'key' => '********************',
'secret' => '****************************************',
],
'region' => env('AWS_REGION', 'eu-central-1'),
'version' => 'latest',
'ua_append' => [
'L5MOD/' . AwsServiceProvider::VERSION,
],
]);
Giving all IAM users full admin access didn't work either.
Locally, all alternative solutions have also always worked!
Does anyone know the problem and have a solution for it? I've been trying to find a way for more than 24 hours. My last approach would be to rebuild the complete call via CURL and try it out, but I hope someone has/finds a solution.
For anyone facing similar issue, this could be/ most likely is due to lambda being configured in a VPC (most likely to give it access to RDS) and therefore losing internet connection, and by extension of that, access to other AWS services. One of the ways to work around this is to enable internet access to your VPC-bound lambda via setting up a NAT gateway (guide here). Alternatively, you can also use VPC Endpoints if the desired AWS service is supported. More info here.
In my experience, I had issues being unable to use specific credentials for certain clients in the AWS PHP SDK. I've seen that the best solution is to include the IAM permissions needed in the policy statements for the lambda function itself.
In this case for example, you may need to include the relevant statement for SNS like below:
{
"Action": [
"ses:*"
],
"Resource": "*",
"Effect": "Allow"
}
If you're using serverless to deploy, you may define it under provider.iamRoleStatements in the serverless.yml file like below:
provider:
name: aws
iamRoleStatements:
- Effect: Allow
Action:
- sns:*
Resource: '*'
IMPORTANT: Do provide only the minimum permissions that your lambda needs. The above statements are just examples.
After the relevant permissions are applied in this way, you may drop the specific credentials from the constructor of your AWS client.
I have a cron on ubuntu server, it fetched data from another server (using yii) and make changes in DB. That cron is much time taking.
But today suddenly I faced an issue while running cron
'PDOException' with message 'SQLSTATE[08S01]: Communication link failure: 1053 Server shutdown in progress'
But I was unaware about this failure. later while debugging got to know this from error logs.
Is there any possibility where I can
either shoot and email when such communication link failure is there (through Yii or any other option)
or any other good suggestion for such scenario?
Anyhow at least I want to know my cron has not ended successfully.
Can we drop an email in Yii for such "'PDOException' with message 'SQLSTATE[08S01]: Communication link failure: 1053 Server shutdown in progress'" errors?
How to email error logs to developer on Yii2 apps? Setup a new target for the log component besides the yii\log\FileTarget that is configured by default:
'components' => [
// ...
'log' => [
'targets' => [
// other targets...
[
'class' => 'yii\log\EmailTarget',
'mailer' => 'mailer',
'levels' => ['error', 'warning'],
'message' => [
'from' => ['log#example.com'],
'to' => ['developer1#example.com', 'developer2#example.com'],
'subject' => 'Log message',
],
],
],
],
// ...
],
The EmailTarget component is another way to log messages, in this case emailing them via the mailer component of the app as specified on the mailer attribute of EmailTarget configuration. Note that you can also specify messages properties and which levels of messages should be the sent trough this target.
There are other components that represents log targets:
Telegram: https://github.com/sergeymakinen/yii2-telegram-log
Slack: https://github.com/sergeymakinen/yii2-slack-log
In my client server, I send queue to AWS SQS using Artisan::queue. In .env file I configured QUEUE_DRIVER=sqs and in config/queue.php file I configured like below.
'default' => env('QUEUE_DRIVER', 'sqs'),
'sqs' => [
'driver' => 'sqs',
'key' => 'MY_AWS_KEY',
'secret' => 'MY_AWS_SECRET',
'prefix' => 'https://sqs.us-west-2.amazonaws.com/1234567890',
'queue' => 'queue-name',
'region' => 'us-west-2',
],
Now when I call Artisan::queue from Controller I see message is created in SQS. I can see them in AWS console and they are like below.
{"job":"Illuminate\\Foundation\\Console\\QueuedJob",
"data":[{"some_data_key":"some_data_value"}]}
Everything is fine so far I believe. But my worker tier never receives data. I have configured in Worker tier>Configuration>Worker Details like below:
Worker queue: queue-name
Worker queue URL: https://sqs.us-west-2.amazonaws.com/1234567890/queue-name
HTTP path: /worker
Here, my problem is that I always get 404 error on /worker address. As soon as message was sent, I see one count up in "Messages in Flight" in AWS SQS console and when I check Worker tier's log file, I see bunch of
`127.0.0.1 (-) - - [28/Jun/2017:08:37:10 +0000] "POST /worker HTTP/1.1" 404 204 "-" "aws-sqsd/2.3"`
I checked if post request to /worker returned error but it works okay in different server (I couldn't check in worker tier as I don't have URL address for it). At this point, Worker tier server has only
Route::match(['GET', 'POST'], 'worker', function () {
return 200;
});
in routes/web.php to see if POST request could reach there.
What did I do wrong? Did I miss something? Or implement it in wrong way?
If your are using your worker environment in elastic beanstalk, make sure your
Configuration->Software Configuration->Container Option->Document Root
is set to value
/public
Also check your route provider somewhere like
app/Providers/RouteServiceProvider.php
There check map() function to see if you are creating your http routes to appropriate file. There can be reference to multiple route files like web.php and api.php etc
I'm trying to send mail in Laravel 5.4 project with Mailgun. I think I set the configuration correctly. But, I got this error message such as
ClientException in RequestException.php line 111:
Client error: POST https://api.mailgun.net/v3/sandboxfeb88d58f18841738b2fc81d7cbc7631.mailgun.org/messages.mime >resulted in a 401 UNAUTHORIZED response:
Forbidden
Here is my configuration:
in .env file
MAIL_DRIVER=mailgun
MAILGUN_DOMAIN=sandboxfeb88d58f18841738b2fc81d7cbc7631.mailgun.org
MAILGUN_SECRET=pubkey-1767e**********
in mail.php file
'driver' => env('MAIL_DRIVER', 'smtp'),
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
'port' => env('MAIL_PORT', 587),
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'richi.htoo#gmail.com'),
'name' => env('MAIL_FROM_NAME', 'Richi Htoo'),
],
in services.php file
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
],
and I wrote mail sending code in default route such as
Route::get('/', function () {
//return view('welcome');
$data = [
'title' => 'Hi student I hope you like the course',
'content' => 'This laravel course was created with a lot of love and dedication for you'
];
Mail::send('emails.test', $data, function($message){
$message->to('white.nuzzle#gmail.com', 'White Nuzzle')->subject('Hello student how are you?');
});
});
And I also installed Laravel Package "guzzlehttp/guzzle" version 6.2 to send mail.
But when I call that default home route, I got an error message as I mention above.
I can't find any solution for my error in any where including this forum "stackoverflow.com".
Can anyone help me please?
Ok found it,
In the file "vendor\laravel\framework\src\Illuminate\Mail\Transport\MailgunTransport.php", the endpoint used is US one.
As said in documentation, https://documentation.mailgun.com/en/latest/api-intro.html#mailgun-regions, you have an endpoint for US and EU.
If you are european, you must use "api.eu.mailgun.net" or you get a 401.
Just change the endpoint: Laravel is powerful and they think about that. You can add an 'endpoint' key to the config/services.php/mailgun entry.
Frankly it was quite an ordeal, I made the sandbox work as following
Added authorized recipient - Apparently sandbox can't send email to
anyone, you need to add them as authorized recipient.
Use proper credentials in .env File added MAILGUN_DOMAIN and MAILGUN_SECRET too as services.php uses them. Remember MAILGUN_SECRET is the private key and starts with key-, don't use public key here
Put
MAIL_DRIVER=mailgun MAIL_HOST=smtp.mailgun.org MAIL_PORT=587 MAIL_USERNAME=postmaster#sandboxcc*****************.mailgun.org MAIL_PASSWORD=****************** MAIL_ENCRYPTION=tls
!!!MOST IMPORTANT!!!! RESTART YOUR SERVER to load the new .env file.
If you are not using the United States Mailgun region, you can define your region's endpoint in the services configuration file:
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'),
],
Alternatively, one can define the MAILGUN_ENDPOINT in the .env file:
MAILGUN_ENDPOINT="api.eu.mailgun.net"
Make sure to check your credentials for the mailgun, make sure that those are correct.
Dont copy the Public Validation Key.
please copy the Private API Key
Default Mail Driver: Mailgun
MAILGUN_DOMAIN=*your-domain*.mailgun.org
MAILGUN_SECRET=pubkey-*your-public-key*
Default Mail Driver: SMTP
MAIL_USERNAME=postmaster#*your-domain*.mailgun.org
MAIL_PASSWORD=*your-password*
Don't forget to php artisan config:clear
I had the same issue. Check your DNS settings in the Mailgun Sending domain checklist. I found I could not get an EU MG setting to work so I suggest you use a US MG server
Ok, I just want to summarize what I had to do to make Mailgun work in Laravel 8.
1. Install guzzle
composer require guzzlehttp/guzzle
2. EU or US?
I'm from EU. The Laravel Doc for Mailgun says to configure mailgun for usage in EU. This is WRONG if you're using the sandbox because it is located in the US apparently. I have a small US flag next to my sandbox url and it took me ages to figure this out.
If you want to use the sandbox put this into \config\services.php:
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
],
If you want to use a domain that is registered for EU you will need to make this change:
'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'),
If you put the wrong region, your log should throw 401 UNAUTHORIZED response
3. Authorize Recipients
If you're using the sandbox you need to authorize recipients. Go to your mailgun account and to your sandbox domain and authorize recipients in the email field. If you try to send an email to non-authorized recipients your log should throw an 403 FORBIDDEN response
4. Default Mailer
If you want mailgun to be your default mailer you can change this in your \config\mail.php:
'default' => env('MAIL_MAILER', 'mailgun'),
5. Credentials
These credentials made it work for me, hopefully they will make it work for you too. Put them in .env:
MAIL_DRIVER=mailgun
MAILGUN_DOMAIN=sandboxXXXXXXXXXXXXXXXXXXXXXXXXXXXX.mailgun.org
MAILGUN_SECRET=2dfXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_ENCRYPTION=tls
MAILGUN_SECRET is your private api key which can be found when go to your mailgun account and click on your profile logo -> api keys.
MAILGUN_DOMAIN is your (I believe currently used) domain and can be accessed in your mailgun account either through the dashboard or through sending->domains in the navpanel. The only not messy reference field for your domain seems to be in sending->overview-> select SMTP and copy the end part of the Username field (without postmaster#)
I hope this saves time for someone because it took forever to get it to work.
Personally I never got the sandbox account to work. I just did this a little over a month ago. Sandbox never worked, but the live account I created did. Try switching to the live account and let me know if that works for you.
Just go to the config folder. and made some changes..
'mailgun' => [
'domain' => env('your_domainxxxxxxx.mailgun.org'),
'secret' => env('key-xxxxxxxx_private_API_keyxxxx'),
],
error will be resolved.
In case if you use API base mailgun, check if the domain exists.
I removed my testing domain and after it tried to send a new email message from that domain. I got 401 error.
I had the same problem in Symfony. Yes, it's not Laravel but they're close.
In .env file I had
MAILER_DSN=mailgun://KEY:DOMAIN#default
The error disappeared after I changed it to
MAILER_DSN=mailgun://KEY:DOMAIN#default?region=eu