毎秒1000リクエスト を捌く超高速CMS「adiary」
2019/07/05(金)JavaScriptから .png 画像を加工してCSSに適用する
png画像をJavaScript上で加工して、CSSに適用する方法。
背景
adiaryでは、UIアイコン(このサイトだとページ送りやタグ一覧に出ている▼等)を色指定に基づいて動的にロードしています。
仕組みとしては、予め128種類の色の異なる画像アイコンを用意して、指定色にもっとも近い色をロードするという感じになっていますが、いくつか問題があります。
- 画像ファイルを増やすには限界があり近似色になってしまう。
- 1KBにも満たないような小さなファイルをわざわざ別ファイルとしてロードさせるのは嫌。*1
色が固定で良いなら、base64で埋め込むだけの話なのですが……。
.ui-icon { background-image: url('data:image/png;base64,XXXX----XXX='); }
adiaryはテーマの色をユーザーが自由に変更できるという素敵な機能が付いておりまして、そういうわけにも行きません……。
正攻法
2019/07/02(火)ES2015(ES6)をminifyしたいけど、Node.jsに興味はない場合
ES2015(ES6)のJavaScriptを minify(compress) したかったときに苦戦したメモ。
JavaScript Compressor
古くから有名でお手軽なYUI Compressorをインストールして、圧縮してみたのですが、謎のエラーが出て困ってしまいました。
Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.yahoo.platform.yui.compressor.Bootstrap.main(Bootstrap.java:21) Caused by: java.util.MissingResourceException: Can't find bundle for base name org.mozilla.javascript.resources.Messages, locale ja_JP (以下略)
どうやら調べてみると、YUI CompressorはES6(let/const)に対応していないようです。
2019年にJavaScriptを書くのに「ES2015ではないスクリプトを書く」のは考えられないので、ES2015対応のJavaScript Compressorを探してみました。
Node.js に用はない
色々調べてみてもNode.js関連情報しか出てきません。
「Node.js」に興味はなく、純粋にJavaScriptを圧縮したいだけなのに……。
路頭に迷っていたのですが、Uglify-jsというNode.js関連ツールを使うことで、Node.jsとは関係なく単にJavaScriptを圧縮できることがわかりました。
Wabpackというツールもあるのですが、軽く調べた感じではNode.js専用ツールっぽい感じで、単なるJavaScriptの圧縮ツールとしては使いにくそうでした。
Uglify-es
残念ながらUglify-jsは「Uglifyjs 3 (Ver3)」でもES6には対応していません。超注意点です。
Uglify-jsからフォークされた「Uglify-es」というツールを使います。まずこのツールを入れるために、Node.js用のパッケージマネージャー npm をインストールする必要があります。
今回インストールしたサーバはDebainですが、普通にapt-getすると古いツールが入るため、「Debian9 (stretch) に Node.js をインストールする」を参考にVer10系の Node.js を導入しました。
curl -sL https://deb.nodesource.com/setup_10.x | bash - apt-get install -y nodejs
これで npm が使用できるようになったので、Uglify-esを導入します。
npm install uglify-es
Makefile
Uglify-js@2、Uglify-js@3、Uglify-esでみんなコマンドラインオプションが違うので、情報を探す際はご注意ください。
参考までに作成したMakefileを晒しておきます。
# # Makefile for adiary.min.js # COMPRESSOR = uglifyjs OPTIONS = -c -m --comments '/^!/' --source-map includeSources,filename OUTPUT = ../adiary.min.js MAP_FILE = ../adiary.min.map FILES = \ 01_global-variables.js \ 10_jquery-ext.js \ 20_init.js \ 30_css.js \ 90_PrefixStorage.js \ 91_jquery-storage.js all: $(OUTPUT) $(OUTPUT): $(FILES) Makefile $(COMPRESSOR) $(OPTIONS) $(FILES) -o $(OUTPUT) mv $(OUTPUT).map $(MAP_FILE)
- -c
- ソースファイルを圧縮します
- -m
- Mangleします。これをつけたほうが圧縮率が高くなります。
- --source-map includeSources,filename
- mapファイルを作成します。
- --comments '/^!/'
- 「!」マークで始まるコメント(ライセンスコメント)を残します。
terserになりました
uglify-esは更新が停止し、uglify-esをフォークした terser に乗り換えました。
インストール方法。
# apt-get install npm # npm install terser -g
使い方は uglifyjs と一緒です。
2019/02/11(月)Linux/Windowsで動く本格的なWebServerをPerlで作る
adiary Ver3.20から adiary.httpd.pl というスタンドアローンサーバが付属するようになりました。これを作成した際の知見メモ。
2019/01/20(日)Net::SMTPでSMTP AUTHのDIGEST-MD5に失敗する問題
さくらインターネットのメールサービスを使用して、SMTP AUTHのDIGEST-MD5認証を使うと送信に失敗する問題を調べてみました。
問題の詳細
PerlのNet::SMTPモジュール等を使用して、さくらインターネットのメールサービスを使い、SMTP Authでメールを送信する失敗します。
ネットで検索するとPerlのNet::SMTPの問題と書かれていたりもしますが、実際にはさくらインターネット側の問題のようです。
SMTPのログを取ってみるとこんな感じになります。
> 220 www1234.sakura.ne.jp ESMTP Sendmail 8.15.2/8.15.2; Sun, 20 Jan 2019 21:25:22 +0900 (JST) EHLO localhost.localdomain > 250-www1234.sakura.ne.jp Hello xxxx [192.168.0.1], pleased to meet you 250-ENHANCEDSTATUSCODES 250-PIPELINING 250-8BITMIME 250-SIZE 209715200 250-DSN 250-AUTH CRAM-MD5 DIGEST-MD5 LOGIN PLAIN 250-STARTTLS 250-DELIVERBY 250 HELP > AUTH DIGEST-MD5 (decoded) nonce="UOM8WDtDO5/ZOCm+tpQaxjRbHOCM5Y5CKekiBEVlmU8=", realm="www1234.sakura.ne.jp", qop="auth,auth-int,auth-conf", cipher="c4-40,rc4-56,rc4,des,3des", maxbuf=8192,charset=utf-8,algorithm=md5-sess >(decoded) authzid="smtp@example.jp",charset=utf-8,cipher=rc4, > cnonce="eedacf2b08dc6c60a2a799ee59188455", > digest-uri="smtp/XXXXXXXX.sakra.ne.jp", > nc=00000001,nonce="UOM8WDtDO5/ZOCm+tpQaxjRbHOCM5Y5CKekiBEVlmU8=", > qop=auth-conf,realm="www1236.sakura.ne.jp", > response=c54d120c415a6e3d5cd5469b36faa7e1 > username="smtp@example.jp" (decoded) rspauth=fc45f7aa080db4dfb73d1b65a1f48d50 > 235 2.0.0 OK Authenticated > MAIL FROM:<test@example.jp> Net::SMTP: Net::Cmd::getline(): unexpected EOF on command channel:
ソースに手を入れていろいろ調べてみたところ、
235 2.0.0 OK Authenticated
を受け取ったあと、どんなコマンドを送っても切断されているようです。コマンドを送るまでソケットは生きているのですが、改行してコマンドを確定した瞬間にソケットの強制切断を喰らいます。これを回避するために、なにか特殊な暗号をしゃべるべきなのかもしれませんが皆目検討が付きません。
問題の切り分け
問題を切り分けるために、自前でSMTP AUTH / DIGEST-MD5認証対応のpostfixサーバを構築して実験してみたところ、問題なく送信できました。これにより、さくらインターネット側の不具合の可能性がかなり濃厚になってきました。
なぜ問題が放置されているのか?
ほとんどのメーラー(メールクライアント)は、SMTP AUTHの認証方法を細かく指定することができません。内部で良きにはからってくれるので、仮にDIGEST-MD5認証に挑戦して失敗しても、CRAM-MD5等の他の認証方法を試します。
PerlのNet::SMTPモジュールではなぜ問題が起こるのか?
Net::SMTPモジュールは「EHLO」で返された「AUTH」の中で、最初に対応しているものを認証手段として選択します。失敗しても次の認証方法を試したりはしません。結果として、さくらインターネットのメールサービスと組み合わせて使用すると、SMTP Authに失敗します。
ひとこと
さくらインターネットちゃんとしてください(笑)*1