キューの話の続きです。
僕がいま抱えている問題としては、メールのテストに
MailTrapっていうサービスを利用することにしたんですが、これ無料のプランだと
1秒間に2回のリクエストしか処理されないんですね。
それを超えるとエラーでリジェクトされるので、テストの意味を為しません。困った。
そこでキューの方で
1つ処理したら1秒待ってもらうみたいなのが必要になってくるわけですね。べつにメールの配信なんかいくらでも遅れて構わないので。
そのためにRedisとかいろいろ導入したんですが、もうちょっと簡単な方法がありそうだったので頑張ってマニュアル探しました。
ググっても見つからなかったので
で、いろいろ探したのですが、キューをシングルスレッドにして、キューを一旦処理したらn秒待つ...という操作がなぜかできなかったので、観念して別の方法で攻めることになります。
public function send(Org $org,MailRequest $request){
QueueDispachFactory::dispatchSendMailList($request,Auth::user());//メーリス送信をキューに入れる
return view("org.authed.mail.send",["org"=>$org,"request"=>$request]);
}
本来、コントローラーでキューにいきなりdispachして処理を丸投げするところを、中間のクラスを定義してそこを経由することにしました。
static public function dispatchSendMailList(MailRequest $request,?User $sendUser){
if(!app()->isLocal()){
SendMailList::dispatch($request,$sendUser);//本番環境の場合、そのまま入れても問題ない
return;
}
$belongs=BelongDAO::getBelongsWithUserFromIdAndHierarchy($request["hierarchy"]??null,$request["belong"]??null);
if($belongs===null){throw new \Exception("メッセージ送信先なし");}
$delay=0;
foreach ($belongs as $b){
//dump($b);
$tempRequest=$request->all();
$tempRequest["hierarchy"]=null;
$tempRequest["belong"]=[$b->b_id];//強制的にbelong単体のIDに書き換えてごまかす
SendMailList::dispatch($tempRequest,$sendUser)->delay(now()->addSeconds($delay));
//dump($tempRequest);
$delay=$delay+6;//とりあえず6秒に1回送信させる
}
}
苦し紛れクラス。本番環境の場合はそのまま本来の処理し、テスト環境、つまりMailtrapを使う場合は、まず一旦ユーザーデータを取ってきて、
リクエストを分けて作ります。
最終的にユーザーデータの配列をforeachで回すんですが、その配列の中身を1つだけにして1つのキューとします。
で、キモなのがdelayメソッドですね。
ここでキューの開始時間を遅らせます。一応。0.5秒に1回のリクエストが許容されるようですが、種々の処理時間が考慮されておらず、安定して送信成功させるには6秒ぐらい間あけないとダメでした。
ただ、とりあえずこれで望む操作にはなりました。
ところで、これを書いているときに思いついたんですが、キューの開始時間が指定できるんだったら
メールを送信する時間も指定できるんじゃ???って思いました。
これがあるんだよなぁ・・・・・・
当たり前だよなぁ・・・・・・。
public function handle()
{
$belongs=BelongDAO::getBelongsWithUserFromIdAndHierarchy($this->request["hierarchy"]??null,$this->request["belong"]??null);
foreach ($belongs as $b){
$usr=$b->user;
Mail::to($usr->email)->send(new MailingListModule($this->request,$this->sendUser));//それぞれにトークンとか発行できるようにこういう形に一応した
sleep(1);
}
}
そもそもこういうforeachで回している以上、ここでローカル環境だけいい感じに遅延した時間を設定すれば済む話なんじゃなかろうか・・・・・・?
やってみた。
public function handle()
{
$belongs=BelongDAO::getBelongsWithUserFromIdAndHierarchy($this->request["hierarchy"]??null,$this->request["belong"]??null);
$delay=0;
foreach ($belongs as $b){
$usr=$b->user;
Mail::to($usr->email)->later(now()->addSeconds(6*$delay),new MailingListModule($this->request,$this->sendUser));//それぞれにトークンとか発行できるようにこういう形に一応した
if(app()->isLocal()){$delay++;}//ローカルのみ追加
}
}
一応これでも送信できるんですけど、なんだかうまくいかない感・・・・・・
理屈ではうまくいくはずなんですけどね〜うまく行ったりいかなかったり。
で、あきらかな問題点がひとつありまして、
なんか同じメールが何回も送信されてるんですよね。
これを見てピンとひらめきました。
メールごとにキューは分けるべきだと。
考えてみれば当たり前で、1つのキューで複数のメールを送信しちゃったら、どれか1つがアウトになったら全部最初からやり直しちゃうわけじゃないですか。
それで同じメールが何回も届いちゃうんですよね。ということで、キューは分けます。
ってことで、最終的には・・・・・・
public function send(Org $org,MailRequest $request){
QueueDispatchFactory::dispatchSendMailList($request,Auth::user());//メーリス送信をキューに入れる
return view("org.authed.mail.send",["org"=>$org,"request"=>$request]);
}
コントローラーではこうやって、キューファクトリー(?)みたいな存在に処理を任せ、これで終わりにしちゃいます。
class QueueDispatchFactory {
static public function dispatchSendMailList(MailRequest $request,?User $sendUser){
$belongs=BelongDAO::getBelongsWithUserFromIdAndHierarchy($request["hierarchy"]??null,$request["belong"]??null);
if($belongs===null){throw new \Exception("メッセージ送信先なし");}
$delay=0;
foreach ($belongs as $b){
$tempRequest=$request->all();
$tempRequest["hierarchy"]=null;
$tempRequest["belong"]=[$b->b_id];//強制的にbelong単体のIDに書き換えてごまかす
SendMailList::dispatch($tempRequest,$sendUser)->delay(now()->addSeconds($delay));
if(app()->isLocal()){$delay=$delay+6;}//とりあえず6秒に1回送信させる
}
return;
}
}
で、その実体はこんな感じです。1回のdispatchに見せかけて、こちらで複数回発行するような形になってるのがミソなわけですね。
本物のキューは
SendMailList::dispatch部分です。こいつが複数回呼ばれることで、それぞれ別のキューとして処理できるわけですね。
とりあえずでかいのが終わったので一安心・・・・・・今度こそ集金計画モジュールを作りましょう・・・・・・。