AWS SDK for Node.jsを用いたSQSの操作

AWS Lambda を使ってお手軽に SQS を操作するために、普段はあまり使わないが Node.js 経由で試してみることにした。本当はJavaのほうが慣れているし、好きなのですがいかんせん Lambda にいちいち jar をアップロードするのは非常に厳しい気持ちになるので、まあたまには…。

自分のためのメモ書きなので、各位はドキュメントを見るべきだと思います
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SQS.html

SQS へのメッセージの送信

SQS メッセージの受信

SQS 受信メッセージの削除

Node.js 書くのかったるい…

PHPの private なメソッドを無理やりテストする方法

Java の package プライベートとかほんと素晴らしい仕組みだと思うのですが、他言語はなかなかないですね…。最近だと Swift2 でようやく @testable import などという形で、モジュールプライベート的なメソッドをテストできるようになっていますが、まだまだ Java に比べると柔軟ではない感じはあります。そもそも、Swift1系はアクセス修飾子の設計普通にミスってるんじゃないかと思いますが、まあ言語を作っている方々は自分よりもはるかに知識がある方々だと思うので、いろいろあったのかな…という感想。自分はコードを読み書きするときにアクセス修飾子を基準にどこから読み進めるかとか、このメソッドは重要だなとかを判断するので、やっぱりこのあたりの機能が柔軟な言語のほうが好き。

本題。PHPの private メソッドを無理やりテストする方法。まあリフレクションしかないですよね。ということで、以下のような具合(自分のための備忘録)。

にゃ〜ん…って感じだ。

AWS Lambda を用いた Amazon SNS のエンドポイント整理

 Amazon SNS の Application に紐付いているエンドポイントには Enabled という Attribute がついています。文字どおり、有効なエンドポイントかどうかを表すものです。Amazon SNS は賢いので APNSGCM からのフィードバックを受けて、プッシュトークンが無効になった場合に自動的に false にしてくれます。しかし、基本的には Enabledfalse になったエンドポイントは不要です。したがって、これらを自動的に削除するような仕組みがあったほうがよさそうです。そこで、この記事では AWS Lambda を用いて、それを実現する方法について説明します。

AWS Lambda で動かすスクリプト

 コード自体は非常に簡単で以下のような形です。AWS Lambda Function を素早く立ち上げるとしたら本来は pythonnode.js のほうが良いかと思いますが、今回の作りたいものに関してはそれほど即時性を求められるようなものではないと思うので、好きな言語(もちろん Lambda でうごくもの)で問題ないと思います。

 json4s とか typesafe config とか scala logging とか使ってます。よい塩梅に jar に固めて、 Lambda Function を作って、アップロードしてください。これで AWS Lambda 側の準備は完了です。

Amazon SNS 側の連携設定

Amazon SNS側の作業は次の2点になります。

  1. 先ほど作った AWS Lambda Function の実行をフックさせるためトピックを作成し、購読(Subscribe)させる
  2. エンドポイントの整理をしたい対象の Application の持つエンドポイントの状態変化をフックして、トピックに通知を送るように設定する

それぞれについて見ていきましょう。

トピックおよび、サブスクリプションの作成

 トピックの作成は通常どおり普通にやってあげれば大丈夫です。とりあえず適当に「endpoint-updated-topic」とでも名前をつけておきましょう。続いて作成したトピックを先ほど作成した Lambda Function に購読させましょう。以下のような形で Protocol で AWS Lambda を選択すれば Endpoint のプルダウンリストに先ほど作成した Lambda Function の ARN がでてくるでしょう。

スクリーンショット 2016-05-26 0.41.37

Application のイベント設定

 最後に Amazon SNS の Application に紐づくエンドポイントの Attributes が変化したイベントをトピックに通知させるように設定します。 Application 一覧の画面で設定対象のアイテムを選択し Actions から configure events を選択してください。

スクリーンショット 2016-05-26 0.46.00

 そして、次に表示されるダイアログの Endpoint Updated の項目に、先ほどつくったトピックの ARN を入力すれば、すべての作業は完了です。

スクリーンショット 2016-05-26 0.48.25

動作確認テスト

 設定が終わったら適当に Application へ Endpoint を追加してみて、Edit Endpoint Attributes から Enable を false にしてみてください。きっと自動的にエンドポイントが削除されるはずです。

スクリーンショット 2016-05-26 0.50.55

 Application エンドポイントの Attribute 変更以外にも、作成イベント、削除イベントなどをフックすることができるので、やり方次第では様々な連携を AWS Lambda を用いて行うことができると思います。

Immutable パターン

結城先生のデザパタ本マルチスレッド編 第2章のまとめ

問題

複数のスレッドがインスタンスを共有しているが、インスタンスの状態は変化しない。
このときに、何も考えず Single Threaded Execution パターンを使うとスループットが落ちてしまいます。

解決方法

インスタンスが状態変化しないのであれば Single Threaded Execution パターンを使う必要はありません。
その場合は意図しない状態変化を防ぐためにフィールドが変化しないようにする Immutable パターンを用います。
不変性を保つのは大変なので、気をつけて実装する必要があります。またドキュメントにクラスが不変であることを明示する方が良いでしょう。

関連するパターン

  • 状態を変更するスレッドが参照するスレッドの数よりも少ないときは、Read-Write Lock パターンが使えます

コード例はあまりにもつまらないので割愛

適用可能性

  • インスタンスの生成後、状態が変化しない時
  • インスタンスが共有され、頻繁にアクセスされるとき

余談

mutable なクラスと immutable なクラス

Java 標準ライブラリには mutable なクラスと immutable なクラスが対になっているものがある。

  • java.lang.String
  • java.lang.StringBuffer

不変性を守るために

  • フィールドの保持している mutable なインスタンスをそのまま返すようなことをすると不変性が破られる可能性がある
  • コンストラクタに引数として渡したインスタンスをそのままフィールドに代入した場合、不変性が破られる可能性がある

Single Threaded Executionパターン

結城先生のデザパタ本マルチスレッド編 第1章のまとめ

問題

複数のスレッドがインスタンスを共有している状態で、
それぞれのスレッドがインスタンスの状態を勝手に変更すると、インスタンスの安全性が失われます。

解決方法

そこで、そのままではインスタンスが不定な状態に陥ってしまう範囲を定めます。
これをクリティカルセクションと呼びます。
このクリティカルセクションを synchronized を用いて1つのスレッドだけが実行できるようにガードします。
このようなパターンを Single Threaded Execution パターンと呼びます。

関連するパターン

  • インスタンスの状態が変化しないときには Immutable パターンを使うことにより、スループットを向上させることができる
  • インスタンスの状態を参照するスレッドと変更するスレッドが分かれている場合は Read-Write Lock パターンを使うことにより、スループットを向上させることができる

以下のようなカウンターがあったとします

これを次のように複数のスレッドから呼び出すとおかしなことが起こります

実行すると同じ値がログ出力されたり、
300回 increment が呼び出されているはずなのに、最後のログ出力が 300 になっていなかったりするはずです。
increment はインスタンス内部の状態を変化させるメソッドであり、複数のスレッドから呼び出すと予期せぬ挙動をとります。
この部分をクリティカルセクションとして保護してあげれば良いでしょう。単に synchronized をつければよいだけです。

パフォーマンスのために、一般にクリティカルセクションは小さく保つほうが良いでしょう。

適用可能性

  • 複数のスレッドからクラスへのアクセスが行われるとき
    • シングルスレッドで実行するプログラムにはこのような対応は必要ない。
  • クラスが状態変化する可能性を持っているとき
  • 安全性を保つ必要があるとき

余談

long と double はアトミックに扱われない

Javaのprimitive型の参照や代入は基本的にアトミックです。
しかし、longdouble に関してはアトミックではありません。

例えば、同じ long フィールドに対して、異なる2つのスレッドから同時に 5678, 1234 という形で代入をおこなったとします。
するとこのフィールドがどのような値をとるか保証されません。この場合は synchronized を使うか volatile キーワードを使うという手があります。

まとめると

  • 基本型・参照型の参照・代入はアトミック
  • long, double に関しては例外
  • long, double をスレッド間で共有する場合は synchronizedvolatile を使う

計数セマフォ

synchronized を使うと、ある領域を1つのスレッドのみが実行できるように制限できますが、
n個のスレッドのみが実行できるようにしたいなどという場合には計数セマフォというものが用意されている。

使い方は簡単で次のような感じ

Java言語で学ぶデザインパターン 〜マルチスレッド編〜 読書メモ (1)

結城先生のデザパタ本マルチスレッド編がサクッと読めそうだったので読み進めているので、自分用メモ。ほとんど知っている内容ではあったけど、体系的に知識が入っていなかったので、よい確認になる…。

Java 言語のスレッド

スレッドの起動方法

スレッドを起動する方法は2つある

  1. Thread クラスのサブクラスのインスタンスを使ってスレッドを起動する
  2. Runnable インターフェースの実装クラスのインスタンスを使ってスレッドを起動する

それぞれ run() メソッドを実装している。これらを直接呼び出した場合、呼び出したスレッドで処理が実行される。別のスレッドで実行させたい場合は、start() を呼び出す必要がある。以下のテストを実行すると、run() の呼び出しはメインスレッドから行なわれているのが観測できる。

スレッドの生成には ThreadFactory なるものを使うこともできる。これにより Threadnew せずに済む。

Thread の排他制御

  1. synchronized メソッドを使う
  2. synchronized ブロックを使う

あるスレッドから synchronized インスタンスメソッドが呼ばれている間は、
他のスレッドは同じインスタンスの synchronized メソッドを呼び出すことができない。
非 synchronized インスタンスメソッドについては問題なく呼び出すことができる。
ロックはインスタンスごとにされる点に注意。

次の synchronized メソッド と synchronized ブロックを使ったメソッドは等価になります。

また、次の synchronized クラスメソッドと synchronized ブロックを使ったクラスメソッドは等価になります。

スレッドの排他制御をする仕組みを モニタ と呼びます。
また、ロックをとっていることをモニタを所有する、あるいはロックをホールドすると呼ぶこともあります。

スレッドの協調

各インスタンスはウェイトセットという仮想的な概念を持っています。
あるインスタンスがあるスレッドに wait メソッドを呼び出されたとき、インスタンスのウェイトセットにそのスレッドが追加されます。
スレッドは notify, notifyAll, interrupt の呼び出しが発生するか wait のタイムアウトが発生するまで停止します。
synchronized 文やブロックの外側で オブジェクトの wait を呼び出すと IllegalMonitorStateException が発生がスローされます。

スレッドには処理をキャンセルする機能があったり、優先度を設定できたり、スレッドの終了待ちをできたりもする。

マルチスレッドプログラミングの評価基準

  • 安全性(safety): オブジェクトを壊さないこと
  • 生存性(liveness): 必要な処理が行われること
  • 再利用性(reusability): クラスを再利用できること
  • パフォーマンス(performance): 処理を高速・大量に行えること

うち安全性と生存性を守るのは必須

ラズパイ3 にRASPBIANを入れるまで

man dd

PHPのスコープについて

自分用メモ。PHPのblockはスコープの単位とならない。この特徴を示す単純なコードは以下のようなもの。

つまり以下のようなコードは冗長だということになる(ただ、個人的には上のコードわかりづらいと思う…)。

他言語を書きながら、ふと PHP を書くと混乱するのでメモ。

無効になったSNSのエンドポイントを削除してくれる AWS Lambda コードを書いた

無効になったSNSのエンドポイント、別に放っておいてもよいのですが、定期的にお掃除したほうがよさそうだったので、お掃除用のAWS Lambdaコードを書きました。Amazon SNSは、アプリケーション(SNSのデバイストークンが登録できるやつ)に紐付いているエンドポイントの状態が変化したときに通知してくれる機能があります。その状態変化通知を受け取るトピックを適当に作っておいて、AWS Lambda コードがそいつを購読するようにしてあげればよいことになります。

ソースコード

Amazon Simple Notification Service(SNS)の基本的な仕組み

用語や概念が少し分かりづらい部分があるため、最初に基本的な仕組みや用語の説明を自分用にまとめました。

通知を送る方法

Amazon SNSを用いて、モバイルPUSH通知を実現するためには、 トピックを利用した通知 と デバイスを直接指定した通知(直接アドレス指定) の 2 種類の方法があります。

トピックを利用した通知

  • Amazon SNSには 通知を送りたいクライアント(Publisher)と通知を受け取りたいクライアント(Subscriber)の間に入る トピック という概念が存在します。
    • 通知を送りたいクライアント(Publisher)は、トピック に対してメッセージを送信します。
    • 通知を受け取りたいクライアント(Subscriber)は、トピック を購読(Subscription)します。
  • 通知を受け取るクライアントが利用できるプロトロルは、 AWS Lambda / SQS / HTTP/S / Email / SMS と後述する Application のエンドポイントです。
  • モバイルデバイスのPUSH通知を可能にするためには、Application を作成する必要があります。
    • Application には、APNsのPUSH証明書やGCMの証明書を紐づけます。
    • Application には、対応するプラットフォームにおける デバイストークン を登録することができます。

デバイスを直接指定した通知(直接アドレス指定)

  • モバイルデバイスのPUSH通知を可能にするためには、Application を作成する必要があります。
    • Application には、各プラットフォームのデバイストークンを登録します。登録するとそのデバイストークンに対応する エンドポイント がSNSから発行されます。
    • Publisher は、この エンドポイント に直接メッセージを送ることができます。これを 直接アドレス指定 とよびます。
    • これは トピック を経由せずメッセージを送信できる例外的な仕組みです。
    • 通知を送りたいデバイスが明確に決まっている場合にこの手法を使います。
    • 利用するAPI:Amazon SNS>>Actions>>Publish / PHP DOCS
  • パラメーター: Message: String, Subject: Option[String], TargetArn: TopicArn | EndpointArn
    • ドキュメント:https://docs.aws.amazon.com/ja_jp/sns/latest/dg/SNSMobilePush.html

SNSの制限

  • トピック数上限: 100,000 トピック/アカウント
  • サブスクリプション数上限: 10,000,000 サブスクリプション/トピック
    • ただし、AWSにお問い合わせフォームから連絡を入れるだけで無料で上限解放してもらえるようです(ソース)
  • 送信データ:最大 256 KB のテキストデータ(XML、JSON、未フォーマットのテキストなど)

通知状況のモニタリング

  • 発行したメッセージ数、通知に成功した数、通知に失敗した数、発行したデータサイズを、AWS CloudWatch API経由で確認することができる

有効期限切れのトークンに対する処理

  • 自動的に無効化される
  • 無効化された際にサーバー側へ通知を送ることができる