Tomcat + DynamoDBでSesison管理を任せてみる

JavaTomcatなWebアプリケーションを作っている時に、Sessionの情報をどう管理しようか、というのが問題になります。
認証済み状態であることをSession上に設定しておき、認証されていないリクエストが来た時にログイン画面に遷移させる、というアプリケーションは多いと思います。
Tomcatのメモリ上にSession情報を配置するのはいいけど、もし、そのTomcatが死んでしまった時、Session情報も無くなってしまいます。
別のTomcatインスタンスにリクエストが飛ばされた時でもユーザに影響が無いようにSession情報を引き継ぎたい、という要望が少なくありません。


イマドキのシステムならDynamoDBで管理なのでしょうか。AWSもそんな事言ってます。

Manage Tomcat Session State with DynamoDB - AWS SDK for Java

少し前の記事ですが、クラスメソッドさんでも取り上げてます。
Amazon DynamoDBによるTomcatセッション永続化とフェイルオーバー | Developers.IO


確かにDynamoDBならEC2インスタンスを使っていなくてもアクセスできるので幅広いシステムに適応できそうです。
また、Tomcatの設定ファイルを変更するだけで、アプリケーション側では特に意識することは無さそうです。

ということで導入してみようと思ったのですが、思いの外ハマったので備忘録を残しておきます。

タイムアウトでないSessionが多数存在する場合、Writeのしきい値を軽く超える

数十ユーザがログイン状態なだけで、DynamoDBに関するエラーログが見られるようになりました。どうも書き込み上限を超えてしまった模様。えっ、早くね?

明らかに有効なSessionのはずなのに存在しないと判断される

また、最後にアクセスしてから明らかにSessionタイムアウトの時間を超えていないにも関わらずログイン画面へ強制遷移されることも発生しました。
DynamoDBと連携するようにしてからです。連携しないようにすると問題は全く起きません。




こんな使い物にならないものをAmazonさんが使えというわけもない、設定方法が悪いんだろうということでいろいろ調査をしてみることにしました。


maxIdleBackupの値を見直す

maxIdleBackupの値を設定しないようにしていました。この期間を超えたものはSessionが最後にアクセスされてからバックアップされる時間です。

今回のSession管理の方針としては、

ということにして、Tomcatが停止した時のSession情報の退避先としてDynamoDBを使用することにしているので、ぶっちゃけた話、稼働している間はDynamoDBに更新する必要はありません。
ということで、maxIdleBackupの値をSessionタイムアウトの時間に設定して、Tomcatが停止しない限りDynamoDBに書き込まないようにしました。

これでめでたしめでたし…とはいかなかった

上記設定を行うことで、

あるTomcatが停止→DynamoDBに書かれる→ロードバランサーが別のTomcatインスタンスにアクセス→メモリ上にSessionが無いのでDynamoDBに問い合わせ、メモリ上に展開→別Tomcatに振り分けられても問題なくアクセス可能

という当初の図式が完成しました。良かった良かった。

ですが、しばらくアクセスしていると、まだ明らかに有効なSessionのはずなのに存在しないと判断されてしまいます。これはおかしい。

設定上だけの問題ではないということで、ライブラリを見てみることにしました。問題は2つ。

一度DynamoDBにputされてしまうとDynamoDBの値でメモリ上のSessionが書き換えられてしまう

DynamoDBにputされたSessionに対して、値の同期をとる処理が走るが、DynamoDBの値でメモリ上のSessionを書き換える処理も走ってしまう。

1台のTomcatでSession情報をDynamoDBに保存する設定をして、簡単なJSPを使って実験してみました。
(AmazonDynamoDBSessionManagerForTomcat-1.0.1.jarを使用しています)

f:id:nemuzuka:20141223145639p:plain




更新する度にカウンタが上がります。
f:id:nemuzuka:20141223145650p:plain
ですが、しばらく繰り返していると…



f:id:nemuzuka:20141223145704p:plain
!?


カウンタが巻き戻っています。これはいけません。
ライブラリが一定時間アクセスされていないSessionをDynamoDBにputする処理の他に、定期的にDynamoDBに登録された情報をSessionに置き換えているようです。
今回のSessionの管理方針としては、Sessionの最新データはTomcat側で持つ、ですからこの処理は不要です。

DynamoDBからgetしてきた時のSessionの最終更新日時の値が不正

DynamoDBの値を元に生成したSessionの情報の最終更新日時の初期値が、「Sessionの生成日時」になっていました。
これだと、別のTomcatに振り分けられた時に生成日時からの経過時間がSessionタイムアウトの時間を超えた場合にSessionタイムアウトと判定されてしまいます。これもマズイ。DynamoDBに設定するのを、Sessionの最終更新日時に変更しました。



というわけで、本当にこれを運用しているところがあるのか?という疑問を持ちながらですが、TomcatのSession管理の仕組みを構築することができました。
ソースも公開しておきますが、根本的に使い方が間違っているとか無いですかね?心配になってきました。
悪いことは言わないからElastiCache使え、ってことでしょうか。
(そもそもTomcatを使うなんて…ということでしょうか)
他所様のシステムの設定情報を拝見したいものです。


どちらにせよ、有効なSessionデータが多数存在する時にTomcatが停止する際に、書き込み上限を超える可能性、というのはあるのですが
DynamoDBにおけるスループット超過対策 〜 Fallback-Queueingパターン | Developers.IO
を組み込んでおけばいいのかな、とも思います。*1

同じようなことで時間を取られている方の助けになれば幸いです。

*1:本当にクラスメソッドさんにはお世話になっております

第37回 長岡IT開発者勉強会に参加してきた #nds37

第37回勉強会(2014/08/09) - 長岡 IT開発者 勉強会(NDS)


すごく久しぶりに参加しました。
テーマは並列・並行処理、ということでしたが、いろんな言語でいろんな方法があるのよねぇ、と、とても勉強になりました。
Webアプリの場合、そんなに気にしなくてもAPサーバ側で打ちとってくれたり、各Threadの同期とかはRDBMSでやっちゃうので言語で頑張ることは意識しないのですが、バッチ処理とかで並列処理することあるしなーと思って聞いてました。

1つの言語に固執するのも良くないなぁ、と思いはじめておりますが...。JSON返せればいいと思ってるので、サーバサイドはまだJavaでいいや。

老害感いっぱいですが、LTもしてみました。

当面はビジネスに絡めて考えることに時間を割こう、と思いました。

TomcatをWindowsのサービスに追加する時のclasspathとかの設定方法

個人的にはTomcatみたいなのはLinuxサーバで動かすもんでしょ、と思っているのですが、規模や用途によってはわざわざサーバ立てずに、空いてるWindowsを使おう、ということもあります。
さらに、使う時にいちいちTomcatを起動するのが面倒だから、Windowsのサービスとして登録しておき、OS起動時に立ち上がるようにしておいてよ、となるのは自然な話です。

ということで、WindowsのサービスとしてTomcatを登録して起動した所、classpathが読み込まれないようです。
ちなみに私がアプリを作るときは、本番環境のDBの接続先やメールサーバ等の設定ファイルはリポジトリに含めたくないので、設定ファイルをwarに含めず、classpathが通った場所に配置することにしています。


Tomcatをstartup.batから起動する時は、classpathの設定を記述することで問題なく読み込まれます。
ですが、Windowsのサービスとして実行した時は、その情報は読み込まれません。
環境変数CLASSPATHに設定しても、読み込まれていないようです。


さて困りました。


startup.batをサービスとして登録しても良いけど、サービス停止する時は単純にプロセスを切られるんじゃないか?
と思われたので、ちょっと躊躇します。


とは言え、全てのリソース情報をWEB-INF/classesに配置するように構成を変えるのも違う気がします。
TomcatWindowsのサービスとした際に、classpathの設定するにはどうすれば良いか。


[答え]
tomcat7w.exe
を使用します。


f:id:nemuzuka:20140625204901p:plain


JVMのパラメータもここで設定できます。
Tomcatはいつも素のzipしか使ってなかったもので、Windows用のzipがある意味がわかりました。

第34回 長岡IT開発者勉強会に参加してみた #nds34

第34回勉強会(2013/11/16) - 長岡 IT開発者 勉強会(NDS)

Google アナリティクス基礎講座ということで、コスギス|新潟県長岡市のウェブ解析士×WordPressでサイト構築を運営されている、小杉先生(@)が講師の回でした。


なんとなく導入して、なんとなくアクセス数だけ確認してたGoogle Analyticsですが、今回の基礎講座を受けて、もっと役に立てそうだ、と再認識できました。別に高いお金を払ってコンサル様にお願いしなくても、ツールに対するちょっとの理解があれば、自分なりに考察してアクセス数を上げるための対策を打つことができそうです。なんでこんなものを無料で使わせてくれるんでしょう、Google様は。

あんまり突っ込んだ内容を書くと有償セミナーになった時に迷惑がかかりそうなので割愛しますが、要は、

  1. 目標を決める
  2. 純粋にサイトを訪れる「お客様」のデータだけ取るようにする
  3. 解析用コードを入れる
  4. 解析データを見て考察しましょう

という流れです。その中で、サイトの訪問者数やPV数が全てじゃないんですよ、と言っていたり、ツールの使い方よりも概念的な話が多かったので、胡散臭いSEOコンサル臭はしませんでした。むしろ、凄く為になりました。

「風が吹けば桶屋が儲かる」のように、PVが低かったり直帰率が高い場合の対策として、仮設を立ててその結果、ユーザが望む状態になるように導くスキルがあれば、Web解析者として良い結果を出せるんだろうな、と感じました。その辺は場数を踏まないとだめでしょうからね。
まぁ、システム提案する時にも必要なスキルだと思うので、いろいろ通じるものがあるなぁ、と。


企業用のHPなんて、「無いよりまし」な感じでとりあえず作ってみました、ってのが多いんでしょうけど、作ってからがスタートなんですよね。見に来てくれた人に対して効果的にアピールできなければ意味が無い。アピールポイントが変わればそれに合わせて変更しなきゃいけないし、もっと掘り下げてアピールすることも必要なコンテンツもあるはずで、企業によってそれは別物なはず。その企業にあったコンテンツの拡充に向けた提案ができるようになれば、ただ1ページX万円とかいう作業スタイルからより付加価値の高いビジネスができるはずです。

うん、作ってからがスタートってのは、システムも同じ。
HPって誰でもできるからやらなくていいやと思ってたけど、やりようによっては面白いかな、と感じるいい機会でした。


今回は初参加のデザイナーさん(特に女子)が多いと思ったのでそれ用に作ったけど空振りに終わったLT資料も公開しておこう...

第33回 長岡IT開発者勉強会に参加して感じたこと #nds33

第33回勉強会(2013/09/28) Scala入学式 - 長岡 IT開発者 勉強会(NDS)
久しぶりにちゃんと参加しました、NDS
今回は、Scala入学式ってことで、意外にも静的型付け言語がメインです。
私が参加した会では、大体PerlRubyさんたちのような動的型付け言語が主流だったのでNDSって奴はそういう会だと思ってたのですが、静的型付け言語+IDE使用と来たもんです。事前準備のIntelliJ IDEAのインストールに「げっ」と思った人は少なからずいるのではないでしょうか。


勉強会自体は、猫型先生(@)さすが、という感じでした。
若い開発者の心を鷲づかみにする技はとても真似できません。*1
ただ、私はbotの写経の早い段階で躓いてしまい、声を上げるのも憚られたので大人しくしてました。
他の人たちは何事も無く終わって「ガッ」してたので、これが若さか...とか勝手に寂しがってたのは秘密です。


静的型付け都市
ニー型!!!!!

圏論都市
ニー型!!!!!


熱いじゃないですか、Niigata。
静的型付けは面倒なことも多いかもしれませんが、強力なパートナー『IDE』がいます。
IDEの機能を使って、他人の書いたclassやメソッドも追いやすくなりますよ。
どこにあるかすぐに見つかりますよ。
そのソースコード、自分が死ぬまでメンテするわけでもないでしょうに。
IDE使うとPCが重い?メモリを積めばいいじゃない!PC買い換えればいいじゃない!
時間は金で買うんです!*2


LTも個性的なものばかりで、大いに楽しませてもらいました。
開発してて楽しければ使う側も楽しいものになりやすいでしょうし、東京とかの仕事を持ってきて受託開発やるだけじゃなくて、新潟からのサービス立ち上げに力を入れていけば技術好きな人もこっちに残ったり、来たりしてくれるんじゃないかなーと。

毎度のことながら、NDSに参加した後は新潟に元気のある人いっぱいいるよなーと感じます。
この勢い、県内の他の業界にも波及できたらもっと良いですよねー。

*1:そっかーゲームかー。下手なオブジェクト指向を解説する教本よりも楽しく深く学べます

*2:まぁ、IDEに限った話じゃありませんが

現場で役立つCSS3デザインパーツライブラリ

現場で役立つCSS3デザインパーツライブラリ

現場で役立つCSS3デザインパーツライブラリ

こういうのはネットで収集するべきなのかもしれませんが、そこまで手が回らないって人にまとまった情報として読むことができるので、お勧めだと思います。
今どきのWebアプリを目指すには抑えといて良いかもしれません。


特に業務用アプリって、「見た目よりまず動くこと」になりがちで、見た目は特に指定が無ければダサいものになりがちだと思うんです。
「俺、デザイナーじゃねーし」って心のどこかで思っているデベロッパも少なからずいると思います。でも、それは甘えかも。
確かにデザインまで口出しできる状況にならないこともあるかと思いますが、使う人は、内部ロジックがいかに優れていようともUI/UXで評価してしまうことが多いのも事実です。見た目にちょっと気を付けることで評価が変わってしまうことも大いにあると思いますよ。


素のCSSを使っているので、使っているフレームワーク関係なく導入できると思います。自分で思うようにカスタマイズすることもできるので、知っておいて損はないと思いますし。
っていうかCSS3の表現力はすごいなー、と今更ながら思います。


凝ったデザインにしなくても、多少の手間で見栄えを良くする為のヒントが乗っている良書です。ちょっと言い方は強引ですが、所詮見た目なのでブラウザによって多少見た目が崩れても大した問題じゃないことも多いですし。*1
価値あるシステム*2の為にお手元に1冊いかがでしょうか。


あわせてよみたい

ノンデザイナーズ・デザインブック [フルカラー新装増補版]

ノンデザイナーズ・デザインブック [フルカラー新装増補版]

*1:特にIEっていうやつ

*2:と思わせる

SSL + Apache HttpComponents + ファイルアップロードで嵌ったことなど

相も変わらず、Javaを書き続けております。
今日は、そんな中でのお話。


公開しているサーバにファイルをアップロードするバッチ機能を実装する機会がありました。
ブラウザから呼び出すのと同じように、multipart/form-dataでリクエストを投げてファイルを登録する方式です。
Apache HttpComponents(4.2.3)を使って実装することにして、検証用サーバ(http)に対するファイルアップロードは問題なく実装できました。あ、まだ技術者としてやっていけるじゃん、俺。


本番サーバは当然のことながら通信経路の暗号化がされています。
接続先の変更のみでうまくいくことだろうとタカをくくり、余裕ぶっこいていたのですが・・・。
リクエストパラメータが、nullになってて、登録できません。


正確に言うと、ファイルデータとともに、必須項目でid的なものとか付帯情報もリクエストパラメータに乗せてたのですが、
そのidが設定されていない、とvalidationのエラーになってしまいました(ファイルデータもnull)。
そのidも、サーバに問い合わせて取得するようにしており、そこに至るまでにデータのやり取りを行っている為、
バッチ機能のリクエストがすべてnullになっているわけでもなさそうです。


前述のとおりhttpであれば問題なく動作します。また、httpsでブラウザから同じリクエストを送信しても登録できます。
SSL + multipart/form-data + HttpComponentsの時に思った通りに動かない?


頼みのgoogle先生からも有効な情報を見つけることはできませんでした。
ブラウザからのリクエストとHttpComponentsからのリクエストの差異から、
cipher suiteがAES128-SHAだったりDHE-RSA-AES256-SHAだったりしてたので、もしかして、
この辺を変えなきゃいけないの?と思って調べてみたりしたのですが、有効な情報は得られず。
ブラウザでは動作しているからApacheの設定ってわけでもなさそうだし・・・。


とまぁ、打つ手がなくなってきて「httpだけどhttpsを使っていると言い張ろう*1」とよぎりましたが、
罪悪感もあり、どこまでリクエストが来ているのか検証してみようか、と思いとどまることにしました。


その結果、
SSL + multipart/form-dataで送る時、ファイル情報だけ送れば想定通りに送れる
ことを確認。


それに対する理由を調査するのも骨が折れそうだったので*2、そうとわかれば話は早い。
1回のリクエストでファイルと登録に必要な情報を送っていた箇所を、
登録情報(POST)でSessionに格納し、その後ファイルのみ送信する(multipart/form-data)、の2回に分けて送るように修正し、思った通りに動作するようになりました。


もしかしたら、Apacheの設定とかだったんでしょうか?Javaだから嵌ったの?*3(未だに)S2使ってるから?
あの・・・誰か知っている人いたら教えてください。

*1:もうコイツ技術者とは言えない

*2:もうコイツ技術者とは言えない

*3:被害妄想