ページ

2011年6月23日

tomcat7のEL式でsomeObj.class.name eq "soma.classname"でエラー

Tomcat7がでてしばらくたつので、そろそろ安定し始めるころかと思います。と言っても、実際に安定しているかどうかはともかく、そう信じ込みたいころです。もっと言えば、今から検討し始めるので、半年後に安定していればいいのですが・・・。ちなみに、僕はJavaはあんまり好きじゃない上に、ばかでかいTomcatとよりコンパクトなJettyを推しているのですが、会社の「いわがが」こと、id:kirisにjettyの検討をお願いしてもう、二年近くたちます。彼の知り合いの人は、是非「仕事をちゃんとするように」としかってあげてください。

どうでもよい前置きはこのぐらいにして、Tomcat7で会社の製品を動かすとJSPのEL式でsomaObj.class.nameとオブジェクトのクラス名を取得しようとしているところでエラーがでます。一言でいうと、会社の製品が全く動きません。ログには、

org.apache.jasper.JasperException: /WEB-INF/jsp/some.jsp (line: 4, column: 509) "${someObj.class.name eq 'java.lang.String'}" は無効な式を含んでいます: javax.el.ELException: The identifier [class] is not a valid Java identifier as required by section 1.19 of the EL specification (Identifier ::= Java language identifier). This check can be disabled by setting the system property org.apache.el.parser.SKIP_IDENTIFIER_CHECK to true.

と出ています。ログを見れば対処方法がわかりますが、org.apache.el.parser.SKIP_IDENTIFIER_CHECKをtrueにして起動すればいいようです。以前のバージョンのTomcat6ではこの値がtrueになっていて、デフォルト値が変わったらしいです。経緯はしりませんが、パフォーマンスかセキュリティの事情なんでしょう。

ということで、linuxとかmacであれば、

CATALINA_OPTS="-Dorg.apache.el.parser.SKIP_IDENTIFIER_CHECK=true"
export CATALINA_OPTS
./catalina.sh run

で動きます。Windowsは知りません。

2011年6月15日

RabbitMQのJavaクライアントをdisる

会社のプロダクトではRabbitMQを使い始めています。製品自体はJavaで作られているので、当然、RabbitMQへのアクセスはJavaのライブラリを使っています。幸い、本家でもJavaのライブラリは開発しているようで、ありがたく使わせてていただいています。でも、でもです。最初に製品にRabbitMQを組み込んだのは「いわがが」こと、id:kirisです。彼はまじめそうなそぶりを見せながら・・・。これ以上書くと怒られるので自重します。そして、このJavaのライブラリについてdisりまくります。挙げ句の果てに、「もう、こんなライブラリ、使っていられません!スクラッチから書き直します。nioで非同期処理して、結果はconcurrentのfutere使って取り出せるようにします。○×△※・・・」残念ながら、彼が言ったことをそのまま、インターネットで公開できないのが残念ですが、あまりにも・・・。さて、今回は彼がdisりまくったことのまとめです。

RabbitMQというか、MQのプロトコル自体のお話は以前書いたので、そっちを見てください。ある程度プロトコルを知っている、という前提です。RabbitMQのライブラリはcom.rabbitmq.clientのパッケージの中でインターフェースが定義されています。ネットワーク周りを中心に言うと、アプリケーションは最初にConnectionオブジェクトを作ります。ConnectionオブジェクトはTCP/IPのコネクションを管理するヒトです。仕様としてConnectionオブジェクトはスレッドセーフです。Connectionオブジェクトが実在するコネクションを管理するのに対して、ChannelオブジェクトはConnection上で論理的な接続を管理します。この辺りの概念はちょっとややこしいですね。実際にアプリケーションがMQとやりとりをするのはChannelオブジェクトを通してです。Channelオブジェクトもスレッドセーフということになっています。

アプリケーションに近いところから見ていくと、Channelの実装は、implの下にあるChannelNです。ネットワークへの送信だけにフォーカスすると、ChannelNの基底クラスのAMQChannelにある、transmitメソッドです。この中でchannelに対してsynchronizedしているので、channelがスレッドセーフと言うことになっています。さらにその先の、quiescingTrans内でネットワークの送受信が行われるので、channelを複数スレッドで使い回してそこそこの書き込みを行うと、ここの同期でブロックされます。なので、Channelって実は、書き込みがそこそこある場合は、スレッド間で共有しない方がいいです。

それじゃ、もう少しおっていきます。その後の処理はAMQCommandのtransmitメソッド中に入っていきます。このメソッドでAMQConnectionオブジェクト、一番最初に出てきたConnectionの実体ですね、その人のwriteFrameメソッドをコールして何回かに分けて書き込みを行っています。AMQCommandはAMQChannelのtransmitをコールするごとに作られたりするので、アトミックです。なので、AMQChannelのsynchronizedが本当に何を守ろうとしているのか、微妙です。しかもtransmit自体はvoidなので、送った後に何かする訳じゃないです。

で、面白いのはここじゃないのです。AMQConnectionの中で実際にTCPのコネクションを管理しているわけではなく、FrameHandlerが管理しています。正確にはこの人はインターフェースで、実体はSocketFrameHandlerです。たぶん、TCP/IPだけじゃなくUnixソケットとかをあつかう壮大な計画の一部なのでしょう。
複数のChannelで複数のスレッドからConnectionオブジェクトのwriteFrameが呼び出されます。なので、当然ネットワークへの書き込みは同期しないと悲しいことになります。SocketFrameHandlerのネットワークへの書き込みは、writeFrameです。このヒト、outputstreamをsynchronizedしています。えーと、ChannelでブロックするのでChannelを複数スレッドで使い回さないようにして、スレッドごとに一つのChannelで処理をしようとしても、悲しい現実につきあたります。結局複数のスレッドで書き込みを行っても、最終的に実際のネットワークへの書き込みでブロックされちゃいます。もっと悲しいことはvoidで結果を意識していないにもかかわらずです。これをサーブレットのRequstのスレッドの中でやると、負荷が集中し始めると世界がとまるんですねん。

本当にスループットを得たいなら、スレッドごとに一つのコネクションを管理する必要があります。でも、それはちょっとやり過ぎじゃ?僕たちの欲しいモノはブロッキングしないモノです。まあ、解の一つはスレッドを一つ作って、その中でゆっくりとデータを送信することです。そして、もう一つがid:kirisが作っているはず、僕はそう信じている高速でノンブロッキングなライブラリを待つことです。

でわでわ

2011年6月9日

IronPythonって速かったのね

普段はあまりWindowsは使わず、ほとんどの作業はMacとUbuntuで済ませています。積極的にWindowsを使う理由も見当たらない生活を送っていました。でも、動作確認をするためだったり、間違ってボタンを押してしまったりしてVMの中にいるWindowsが立ち上がってしまうことがあります。今日は間違ってVIrtualBoxの中にいるWindowsを起動しちゃいました。ついでなので、IronPython2.7をインストールして先日pypyで遊んだときのフィボナッチで計測しました。

結果はこんな感じです。
再帰するほうでは、2.2倍もCPythonより速いです。Javaには及びませんが、3割ぐらい遅くなる程度です。

ループ版では再帰版ほど速度差はです、1.4倍程度速くなりました。みんなが大好きなJavaとの速度差ですが、ループ版でも3割ぐらいの落ち込みなので、わりと安定したスピードです。

まあ、これらの結果をどう見るのかは微妙ですが、Windows環境ではCのライブラリを呼び出さない限り、IronPythonを使うのがいいのかな?

でわでわ

2011年6月6日

Re-render Tweet button via JS

stackoverflowの記事にあるように、以前はTweetButtonがあってJavaScirptで後からロードしたHTMLに対してTweetButtonをレンダリングすることができました。でも、気がついたらこれができなくなっています。@anywhereっていうライブラリを使えば、Tweet Boxっていうのは表示できるらしいですが、Tweet Buttonは表示できません。ちなみに僕はTweet Boxが何なのか知りません。で、いろいろ調べると、情報がありません。以前の方法でやるのはあきらめます。Tweet Buttonを表示するのはJavaScriptでやる方法とiframeを使う方法の二つがあります。JavaScriptを使うのはあきらめたので、iframeでがんばります。

JavaScriptはtwitter-share-buttonというクラスを当てたaタグをボタンに変身させる技を使っていました。iframeを使う方はもっと簡単です。次のhtmlの断片で実現できます。

<iframe allowtransparency="true" frameborder="0" scrolling="no"
        src="http://platform.twitter.com/widgets/tweet_button.html?url=エスケープしたURL"
        style="width:130px; height:20px;"></iframe>

これをDOM要素にあとから追加していけば、TweetButtonがあとからあとからどんどん現れていきます。めでたしめでたし。
タイトルにvia JSとあるのに、JavaScriptが全然書いていないので、ちょっとだけ書くと、jquery使っていれば上の断片をfragmentっていう変数に入れているとして、
$("#どこかの要素").append(fragment)
ほら、via JSになった。

これ、http://twwweet.liris.org/で使っているんですが、まだ、本番環境には入れていない・・・。


でわでわ

2011年6月1日

CentOS 5.4にmemcachedとRabbitMQをインストールするメモ

「CentOS 5.4にmemcachedとRabbitMQがインストールできません」って泣きつかれたので、数年ぶりにRedHat系のLinuxを触りました。で、CentOS 5.4にmemcachedとうさぎ年なのでRabbitMQをインストールするまでのメモです。

CentOS 5.4はServerとしてインストールしました。理由はGUIはいらんから、というだけです。以下、めんどいのですべてrootでの作業です。
インストール後、何はともあれ、「yum update」します。次にRPMForgeのパッケージを入れられるようにします。
curl -O http://apt.sw.be/redhat/el5/en/x86_64/rpmforge/RPMS/rpmforge-release-0.5.2-2.el5.rf.x86_64.rpm
rpm -ivh rpmforge-release-0.5.2-2.el5.rf.x86_64.rpm

それではmemcachedです。最近何かとはやりのstackoverflowのお告げによると
yum --enablerepo=rpmforge,rpmforge-extras install memcached

とインストールします。これだけです。

次にうさぎ年のRabbitMQです。まずは、Erlangをインストールします。RabbitMQのサイトによると、素の状態で入るR12Bは遅くてつかいものにならないので、最新版を入れろって書いています。なので、
wget -O /etc/yum.repos.d/epel-erlang.repo http://repos.fedorapeople.org/repos/peter/erlang/epel-erlang.repo
yum update
yum info erlang
yum install erlang

として、最新版っぽいヒトをインストールします。yum infoによるとR14Bって言うのがインストールされました。次に、RabbitMQのRPMを落としてきて、インストールです。
curl -O http://www.rabbitmq.com/releases/rabbitmq-server/v2.4.1/rabbitmq-server-2.4.1-1.noarch.rpm
rpm -ivh rabbitmq-server-2.4.1-1.noarch.rpm

終わりです。難しくないですが、Erlangのインストールにとっても時間がかかります。まあ、そんな感じ。


でわでわ


ちょっとどうでもいいことでは、SRPMのビルドの仕方。
yum install rpm-build
yum install gcc

として、ビルド環境を整えてから、「rpmbuild --rebuild なんとか.srpm」ってします。