2015年7月1日水曜日

S3のあるバケットにファイルがアップされたら違うバケットにコピーするのを、SQS+fuelPHPのtasksを使ってやってみる的なお話

  • このエントリーをはてなブックマークに追加

S3のあるバケットにファイルがアップされたら違うバケットにコピーしたいってことをしたいときに、
通常だったらcronで定期的にS3のあるバケット内を監視するなりなんなりが考えられるけど、
それってまぁあまりエコじゃなかったりとかそもそもそこまでする必要はないんじゃないかとか、
というかそもそもとあるバケットからとあるバケットにコピーする場面ってぶっちゃけあまりないよねとは思うけど。

けどまぁそこらへんをうまく解決するにはS3のイベント通知をSQSに向けてあげることで解決出来るかと。
ということで今日はその辺のお話をば。

■SQSのキューを作成

AWSのコンソールでSQSを選択して、新しいキューの作成をするとこんな感じでポップアップが出てくるので、
キュー名を適当に入力して後はデフォルト的な感じでキューの作成を行う。
※リージョンは普段自分が使っているリージョンにしておく
作成したキューにアクセス許可を追加する。
こんな画面が出てくるので諸々設定をする。
ちなみにアクションはSendMessageにチェックをする。
で、arn:aws:s3:::s3のバケット名とs3のbucket名を入れてアクセス許可の追加をする。


■S3のバケットにイベント通知を設定する

S3のコンソール上から追加出来るので追加。
名前は適当で、イベントはput(更新)とpost(追加)の2種類を入れてあげる。
送信先をSQSキューとしてあげ、SQSキューのARN(SQSコンソールの詳細に書いてある)を入れてあげて保存。


■SQSのキューを見にいくプログラムの作成

namespace Fuel\Tasks;

require_once(APPPATH . "vendor/aws/aws-sdk-php/aws-autoloader.php");

use \Aws\Common\Aws;
use \Aws\Common\Enum\Region;
use Aws\DynamoDb\DynamoDbClient;
use Aws\Sqs\SqsClient;

class S3tasks{

public static function check_sqs(){
  $time1 = time();

  $client = SqsClient::factory(array(
    "key" => "xxxxxxxxx",
    "secret" => "xxxxxxxxx",
    "region" => "ap-northeast-1"
  ));

  while(true){
    $queueUrl = $client->getQueueUrl(array(
      'QueueName' => 'check-getstage-s3-upload'
    ));

    $result = $client->receiveMessage(array(
      'QueueUrl' => $queueUrl['QueueUrl'],
      'MaxNumberOfMessages' => 1
    ));

    $messages = $result->getPath('Messages/*/Body');

    if(!empty($messages)){
      foreach($messages as $message){
        $data = json_decode($message);

          if(isset($data->Records)){
            foreach($data->Records as $d){
              if(!preg_match("/^logs\/.+&/i",$d->s3->object->key)){
                echo "{$d->s3->object->key}\tcopy item\n";
                \S3::copy_object("移動元のパケット",$d->s3->object->key,"移動先のパケット",$d->s3->object->key,"public-read");
              }
              else{
                echo "{$d->s3->object->key}\tnot copy item\n";
              }

              $client->deleteMessage(array(
                'QueueUrl' => $queueUrl['QueueUrl'],
                'ReceiptHandle' => $result->getPath('Messages/*/ReceiptHandle')[0]
              ));
          }
        }
      }
    }
    else{
      exit;
    }
  }
}

}
とりあえずこんな感じ。
'MaxNumberOfMessages' => 1ってところで一回で引っ張ってくるキュー数を設定出来る。
ちなみにデフォルトは1。
自分としても一回一回処理が終わってキューの判定をしたいので、あえてここは1にしている。

S3にログがアップされたらこちらも通知されてしまうので、これを除外するためにpreg_matchしている。
\S3となっているのはfuelphpでS3に簡単にファイルをアップする仕組みを作る的なお話で紹介しているfuelphpのパッケージ。

受け取ったキューメッセージは削除したいので、deleteMessageを使って削除する必要あり。

で、これはwhile(true)で永続的に回しているけど、もちろんキューがない場合もあるわけで。
これを判断するためにif(!empty($messages))として、無かった場合にプログラムを削除するようにしている。
なのでcronでやってもプログラムが2重3重に走る事はないという感じ。

pull型なキューの取得になるけど、push型でやりたい場合はLambdaを使って一度Lambdaで何が変更されかたを取得し、
それでEC2というかあるURLに引数付きでアクセスするとコピー出来るようにするっていうのがあるんじゃないかと。

どちらにしろawsは色々と便利なものがあって使いこなすにはもちろん大変だし、
色々と設定しないといけないものがあって大変だけどいいんじゃないかと思ったり。
けど使いこなすことによって色々と便利になるからいいよ的なみたいな。

というか今回はなんか量も多いし、文章もかなり適当ではあるなぁと思った。

参考にしたサイト
S3 Event NotificasionsをAmazon SQSで受け取る - Glide Note - グライドノート
Amazon SQSを使った非同期処理の実装 ‹ ワンダープラネット株式会社(Wonderplanet Inc.)
Amazon Simple Queue Service(SQS)をPHPでいじる - Qiita
Amazon SQSを使ってみた② | Septeni Engineers' Blog | セプテーニ エンジニアブログ

Adsense