Jetty9.4を使ってAirsonicをSubsonicとほぼ同じ速度で動かす手順です。
Subsonicと同じように、AirsonicもTomcat組み込みのインストーラが配布されています。
(コミュニティではTomcatに慣れた技術者が多いので採用しているのでしょう)
ですがTomcatはAirsonicの要件的にレスポンス速度が少々遅いです。
そのためこの記事では「Jetty」を使ってAirsonicを動かす手順を扱います。
JettyはSubsonicも採用しているコンテナです。
作業項目の目次を見ていただくと、「Jettyアンインストール」というのがあると思います。
それを行うと、最初からそこまでに追加したファイルを全て消します。
(それ以降の「Airsonicインストール」で作成するデータディレクトリは削除しません)
Jettyインストール
ほとんどをrootユーザで操作します。
注意。
Jettyは、単純に動かすだけであれば1コマンドでスタンドアロンで起動が可能です。
そのしくみを使って簡単インストールをすることも、一応できます。
ただしこの方法では起動時に「非推奨だからマニュアル見てね」と警告が出ます。
サーバの長期安定稼働やバージョンアップの利便性を考慮した推奨方法がドキュメントに書いてあります。
この記事の手順はその推奨方法に則って、上から順に実行すれば動くように、またAirsonicの場合変更した方がいいような点を修正したものです。
作業のおおまかな流れは以下のようなものです。
- インストールディレクトリに解凍
- 起動時の環境変数設定
- 初期化コマンドで設定ファイル生成
- 設定ファイルを書き換えて起動
とてもポピュラーな作業内容なので、何かサーバをインストールしたことがある方であれば特に難しい内容もないと思います。
難しい設定は極力行わないようにしています。
これを基本に、何か困ったことがあれば肉付けしていけばよいと思います。
インストールディレクトリ作成
ディレクトリを作成します。
opt配下に配置する前提ですすめます。
|
|
mkdir -p mkdir -p /opt/web/mybase /opt/jetty/run |
- /opt/jetty
-
Jettyのディストリビューションを置く場所。
- /opt/jetty/run
-
pidやstartlogの出力場所。サーバをjetty権限で動かすので/var/runでなくここ。
- /opt/jetty/temp
-
Service LayerでJavaに割り当てられるtemp(java.io.tmpdirに渡す)。
サーブレットの作業ディレクトリになります。
標準的な一時ディレクトリは、長時間実行されるJettyサーバに混乱を引き起こすさまざまなクリーンアップスクリプトによって管理されることが多いので隔離するとのこと。WAKARU
このディレクトリはjettyユーザのホームディレクトリとして作成するので、ユーザ追加時に作成します。
- /opt/web/mybase
-
サーバーの設定ファイルおよび、Webアプリケーションセットを配置する場所。
今後バージョンによって変わるであろう構成は/opt/jettyに押し込み、汎用基本設定はこちらで行うというポリシー。
ユーザを追加
最小権限のユーザを追加します。
|
|
useradd --user-group --home-dir /opt/jetty/temp jetty |
ドキュメントには以下のように記載があります。
|
|
useradd --user-group --shell /bin/false --home-dir /opt/jetty/temp jetty |
falseはftpも禁止の、一番制限が大きいタイプです。
noshellやfalseだと動かないんじゃ、と思ったらやっぱり動かなかったので外して上の方のコマンドで。
ユーザをグループに追加、楽曲ディレクトリを作成
これはインストールとは直接関係ありませんが、この時点でユーザやグループを決めておくとよいと思います。
Airsonicをjettyユーザで動かすので、楽曲ディレクトリはjettyユーザが読み書きできる必要があります。
例えば以下のように楽曲管理用グループを作って、あとで特定ftpユーザ等もこのグループに追加する、など。
|
|
groupadd musicmaster usermod -aG musicmaster jetty mkdir -p /var/musicDir /var/podcastDir chown -R root:musicmaster /var/musicDir /var/podcastDir chmod -R 770 /var/musicDir chmod -R 770 /var/podcastDir |
OSのディストリビューションによっては初期インストール時にmusicディレクトリが存在します。
ストリーミングサーバの目的から言ってもバッティングさせないで隔離した方が良いと思いますが、個々の事情に合わせて。
OSによっては特殊な扱いをされるディレクトリである場合もあるので、リスクを避けるなら分けた方がよいかもしれません。
当初musicDirはスキャンできればよいので750、podcastDirは書き込みを行うので770としていましたが。
よく考えると画面側にタグ編集の機能があるので、両方とも読み書きできるディレクトリである必要があります。
タグ編集しないのであれば(あるいは元の楽曲データを改変できないようにしたいのであれば)musicDir側はRで動きます。
podcastDirはAirsonicが自動でCRUDするのでRWが必要です。
ダウンロード、解凍
/opt/jetty以下にJettyディストリビューションを配置します。
ディレクトリ名はそのままです。
バージョン違いのものを入れる場合、この階層に並列します。
|
|
cd /opt/jetty wget http://central.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.4.8.v20171121/jetty-distribution-9.4.8.v20171121.tar.gz tar -zxvf jetty-distribution-9.4.8.v20171121.tar.gz rm -f jetty-distribution-9.4.8.v20171121.tar.gz cd jetty-distribution-9.4.8.v20171121/ cp bin/jetty.sh /etc/init.d/jetty |
環境変数設定
起動スクリプト実行前に読み込まれる環境変数を設定します。
とりあえず以下のように。
VMのチューニングはここで。
|
|
export LANG=ja_JP.UTF-8 JAVA_OPTIONS="-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Xms128m -Xmx768m" JETTY_HOME=/opt/jetty/jetty-distribution-9.4.8.v20171121 JETTY_RUN=/opt/jetty/run JETTY_USER=jetty TMPDIR=/opt/jetty/temp |
- JETTY_HOME
- 最低限JETTY_HOMEが必要です。
稼働に使用するディストリビューションのパスを記述します。
バージョン違いのものを入れる場合、ここを書き換えます。
- LANG JAVA_OPTIONS
-
LANGとJAVA_OPTIONSはJavaのファイルシステムを日本語対応するためのものです。
ここで設定するのが楽かと思います。
エラーが見にくくなったり不要なトラブルを回避するため、普段極力不要な日本語化は行わないという方のために一応。
こういうディレクトリ・ファイルパスの楽曲ファイルも演奏できるか、というお話です。
|
|
[var]# ll musicDir/いきものがかり/なくもんか/ total 113088 -rwxr-x--- 1 root musicmaster 41055912 Aug 17 2016 01 なくもんか.flac -rwxr-x--- 1 root musicmaster 34882654 Aug 17 2016 02 オリオン.flac -rwxr-x--- 1 root musicmaster 39856364 Aug 17 2016 03 なくもんか -instrumental-.flac |
CDから自動でリッピングしてストレージにそのまま置く、のようなお手軽管理をしている場合、ほとんどのソフトはCDDBから取得した曲名で楽曲ファイルを吐くと思います。
つまり日本語タイトルの楽曲の場合、Windowsで生成した日本語名ファイルをLinuxに持ち込むことになります。
もしこのような設定がなくAirsonicが日本語ファイル名を表示できない場合でも、スキャンはされパスはDBに格納されます。
ただし画面上では文字化けをし、演奏操作時点でファイルエントリが認識できない、という不具合の出方をします。
それを避けるためにここで設定をしておきます。
タグエンコーディングの文字化けとはまた別の話です。
- JETTY_USER JETTY_RUN TMPDIR
-
稼働に使用するユーザ名やディレクトリです。事前に作成しているのでここに記述。
初期化処理
よくある解凍即起動のサンプルは、初期時に準備されている全部載せの設定ファイルで動かしています。
ここでは自分用の設定ファイルを作成します。
Airsonicを動かすのであれば以下のコマンドで。
|
|
cd /opt/web/mybase/ java -jar /opt/jetty/jetty-distribution-9.4.8.v20171121/start.jar --add-to-start=deploy,http,jstl,console-capture |
このようにモジュール再構成のための初期化処理が走ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
INFO : webapp transitively enabled, ini template available with --add-to-start=webapp INFO : server transitively enabled, ini template available with --add-to-start=server INFO : mail transitively enabled INFO : servlet transitively enabled INFO : jsp transitively enabled INFO : annotations transitively enabled INFO : transactions transitively enabled INFO : plus transitively enabled INFO : deploy initialized in ${jetty.base}/start.ini INFO : jstl initialized in ${jetty.base}/start.ini INFO : security transitively enabled INFO : apache-jsp transitively enabled INFO : jndi transitively enabled INFO : console-capture initialized in ${jetty.base}/start.ini INFO : http initialized in ${jetty.base}/start.ini INFO : apache-jstl transitively enabled MKDIR : ${jetty.base}/webapps MKDIR : ${jetty.base}/logs INFO : Base directory was modified |
この時点で、/opt/web/mybase以下に稼働に必要となる設定ファイルやディレクトリが自動で追加生成されます。
そのうちのひとつ、start.iniを一ヵ所だけ修正します。
start.iniはmoduleと呼ばれるライブラリセットと、プロパティの定義ファイルです。
だいたいPOMのparentのような役割です。
moduleは自作もできます。
サーバの処理を本気でいじくりだすとなると先のJarまで見に行く必要がありますが、動かすだけであれば大抵のことはここで設定できます。
|
|
cp start.ini start.org vi start.ini |
ポートの指定がコメントアウトされているので
|
|
## Connector port to listen on # jetty.http.port=8080 |
自分の環境に合わせて修正します。
Module: httpのところにあります。
まずはこれだけ。
|
|
## Connector port to listen on jetty.http.port=22020 |
start.iniを保存したら、ここまで配備したファイルのオーナーをjettyに変更し、再度環境設定ファイルを開きます。
|
|
chown -R jetty:root /opt/jetty chown -R jetty:jetty /opt/web/mybase vi /etc/default/jetty |
JETTY_BASEを追加します。
一応ドキュメントでも概ねこのような作業の流れになっています。
|
|
export LANG=ja_JP.UTF-8 JAVA_OPTIONS="-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Xms128m -Xmx768m" JETTY_HOME=/opt/jetty/jetty-distribution-9.4.8.v20171121 JETTY_BASE=/opt/web/mybase JETTY_RUN=/opt/jetty/run JETTY_USER=jetty TMPDIR=/opt/jetty/temp |
設定の確認
以下のコマンドで設定を確認できます。
基本設定ファイルとディストリビューションが分割配備されていて、起動時にちゃんと読みに行けるな、という確認をします。
何かトラブルがあったらここまで戻りましょう。
|
|
JAVA = /usr/bin/java JAVA_OPTIONS = -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Xms128m -Xmx768m -Djetty.home=/opt/jetty/jetty-distribution-9.4.8.v20171121 -Djetty.base=/opt/web/mybase -Djava.io.tmpdir=/opt/jetty/temp JETTY_HOME = /opt/jetty/jetty-distribution-9.4.8.v20171121 JETTY_BASE = /opt/web/mybase START_D = /opt/web/mybase/start.d START_INI = /opt/web/mybase/start.ini JETTY_START = /opt/jetty/jetty-distribution-9.4.8.v20171121/start.jar JETTY_CONF = /opt/jetty/jetty-distribution-9.4.8.v20171121/etc/jetty.conf JETTY_ARGS = jetty.state=/opt/web/mybase/jetty.state jetty-started.xml JETTY_RUN = /opt/jetty/run JETTY_PID = /opt/jetty/run/jetty.pid JETTY_START_LOG= /opt/jetty/run/jetty-start.log JETTY_STATE = /opt/web/mybase/jetty.state RUN_CMD = /usr/bin/java -Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 -Xms128m -Xmx768m -Djetty.home=/opt/jetty/jetty-distribution-9.4.8.v20171121 -Djetty.base=/opt/web/mybase -Djava.io.tmpdir=/opt/jetty/temp -jar /opt/jetty/jetty-distribution-9.4.8.v20171121/start.jar jetty.state=/opt/web/mybase/jetty.state jetty-started.xml |
ここまで理解しておけば仕事で他人がインストールしたjettyを触るときも大丈夫そうですね。
サービス登録
最新のLinuxは少し違うと思いますがそこは読み替えましょう。
|
|
chkconfig --add jetty chkconfig --level 345 jetty on |
起動
起動後の確認
正しく起動したか確認します。
先ほどのstatusとほぼ同じですが、一行目にpidが表示されていると思います。
このpidを使って確認します。
|
|
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND jetty 5129 0.5 4.3 3819116 89672 ? Sl 13:50 0:01 /usr/bin/java -Dfile.encoding=UTF-8...... |
rootではなくちゃんとjettyユーザで動いていればOKです。
Jettyアンインストール
一応削除方法を。
以降Airsonicをインストールするので、実行しては駄目です。
ここまでの手順をループできますよね、と確認するためのメモです。
|
|
service jetty stop chkconfig jetty off userdel -r jetty
rm -Rf /opt/web /opt/jetty /etc/init.d/jetty /etc/default/jetty
rm -Rf
/etc/init.d/jetty /etc/default/jetty
|
Airsonicインストール
ここから先の作業はAirsonicのサイトにあるインストール方法と少し似ていますが、もう少し簡単だと思います。
Airsonic配備
Airsonicのデータディレクトリを作成します。
|
|
mkdir /var/airsonic chown -R jetty:jetty /var/airsonic |
Airsonicをダウンロードします。
少し時間がかかるので気長に待ちます。
|
|
su - jetty cd /opt/web/mybase wget https://github.com/airsonic/airsonic/releases/download/v10.1.1/airsonic.war mv airsonic.war webapps exit |
ホットデプロイを使用します。
デプロイされていればログに出力されています。
|
|
tail /opt/web/mybase/logs/2018_03_05.jetty.log |
|
|
INFO:oejs.Server:main: jetty-9.4.8.v20171121, build timestamp: 2017-11-22T06:27:37+09:00, git hash: 82b8fb23f757335bb3329d540ce37a2a2615f0a8 INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///opt/web/mybase/webapps/] at interval 1 INFO:oejs.AbstractConnector:main: Started ServerConnector@27901369{HTTP/1.1,[http/1.1]}{0.0.0.0:22020} INFO:oejs.Server:main: Started @696ms INFO:oejw.StandardDescriptorProcessor:Scanner-0: NO JSP Support for /airsonic, did not find org.eclipse.jetty.jsp.JettyJspServlet INFO:oejs.session:Scanner-0: DefaultSessionIdManager workerName=node0 INFO:oejs.session:Scanner-0: No SessionScavenger set, using defaults INFO:oejs.session:Scanner-0: Scavenging every 600000ms INFO:oejsh.ContextHandler:Scanner-0: Started o.e.j.w.WebAppContext@4573f8cd{/airsonic,file:///opt/jetty/temp/jetty-0.0.0.0-22020-airsonic.war-_airsonic-any-4420617065774440408.dir/webapp/,AVAILABLE}{/airsonic.war} |
安定稼働できる確認が取れたら、デプロイは起動時に一回だけ行うよう変更もできます。
設定はstart.iniでできます。
|
|
# Monitored directory scan period (seconds) jetty.deploy.scanInterval=0 |
ログイン
ここから先の作業はほとんどSubsonicと同じです。
ブラウザから 「http://サーバのアドレス:ポート番号/airsonic」 で接続します。
初期パスワードはadminです。

楽曲ディレクトリの設定
Setting > Media Foldersを開きます。
初期設定では楽曲ディレクトリに/var/musicが割り当てられています。

私はこの手順の最初の方で /var/musicDir を作成しているので、それに書き換えます。
わかりにくいという方は何か適当に作ってオーナーとパーミッションを変更してください。
もしjettyユーザが読めない場所が指定されると、この画面にエラーが出ます。
おそらく楽曲をFTPで送信することになるので、そのあたりの運用も含めてユーザとグループの使い方を決めておくとよいです。
Podcastディレクトリの設定
Setting > Podcastを開きます。
初期設定では楽曲ディレクトリに/var/music/Podcastが割り当てられています。

私はこの手順の最初の方で /var/podcastDir を作成しているので、それに書き換えます。
初期設定では楽曲ディレクトリに入れ子になっていますが、このままでは索引作成時に楽曲とPodcastが混在してしまいます。
これを避けるため、/var/musicDirと/var/podcastDirのようにツリールートを分割するとよいです。
こうするとアカウントごとフォルダアクセス権を設定することで、楽曲ディレクトリの使い分けができるようになります。
携帯アプリ接続時は用途に合わせ楽曲とPodcastのどちらも表示できるアカウント、もしくは/var/musicDirしか表示できないアカウントのどちらかを使う。
PCから使うときは楽曲ディレクトリごとに表示したり全表示したりといったUIがあるので、両方アクセスできるアカウントを使う、といったようなことができます。
Podcastを使い始めてある程度データがたまってからでは移行が面倒なので、最初に運用を決めておくとよいです。
ffmpegインストール
Setting > Playersを開きます。
この時点ではtranscoders がインストールされてないよと警告が出ます。

ぐーぐる先生にffmpegインストールを聞けばたくさん出てくると思いますが、たとえば以下のように行います。
transcodeディレクトリに実行ファイルなりリンクを置く仕組みもSubsonicと同じです。
|
|
yum -y install epel-release rpm --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro rpm -Uvh http://li.nux.ro/download/nux/dextop/el6/x86_64/nux-dextop-release-0-1.el6.nux.noarch.rpm yum -y --enablerepo=nux-dextop,epel install ffmpeg ffmpeg-devel cd /var/airsonic/transcode ln -s /usr/bin/ffmpeg |
スキャン
楽曲フォルダに楽曲ファイルを格納後、Setting > Media Foldersを開きます。
Scan media folders nowをクリックすればスキャンが開始されます。
スキャンが終わったら再生して確認したり、PlayersからプレイヤーのMax bitrateを設定しておくとよいです。
付記
ここから先は技術者向けのおまけです。
JettyとTomcatの比較
どちらが早いとは一概に言えませんが。
Subsonicのようなタイプのサービスを動かす場合Jettyの方が向いていて速いです。
一般にTomcatは大人数からの安定的に多数の接続をする場合、Jettyはテキストやバルクの単純送出速度にこだわる場合といった使い分けがなされます。
たとえばベンダー製のWebアプリケーションサーバ製品では起動時に複数のコンテナを起動し、システム連携用のSOAPやRESTの口はJettyを使用しているような場合があります。
今回の作業でベンチは取っていませんが、自分のクライアントプログラムを動かすときに、JettyとTomcatの両サーバで動作確認をしています。
クライアントが同時に要求するコネクション数に着目する場合、Webアプリは画面更新時に一本、仮にAjaxを使ってもシングルスレッドなのでせいぜい数本です。
私のデスクトップアプリは処理によってはマルチスレッドでガンガン投げます。
(実際にはクライアントのOS側で通信数に制限が入るにしても、たて続けにリクエストを送る)
どの程度まで許容するかについては、標準インストールしたSubsonic相手にタイムアウト2s以内の設定でクライアントの機能が動かせる、といったところを目安としています。
このような強引なつくりでもJettyの場合問題なく動きますが、Tomcatは元の送出が遅いので過密処理では後続の処理がタイムアウトになる場合があります。
少なくとも一人が瞬間的にガンガン要求を投げてそれを高速にさばく、といった観点では未チューニング同士ならJettyにアドバンテージがあります。
速度に定評にあるSubsonicが、DBをアップデートしたり戻したり古いJettyを採用していたりするのは根拠あってのことなんだろうと思います。
SubsonicとAirsonicの比較
この記事にある設定方法であれば、同一のサーバマシン上での比較をした場合、SubsonicとAirsonicでほぼ似たような体感速度が得られるのではないかと思います。
ただしクライアントが同じリクエストを投げていても、SubsonicとAirsonicで返すレスポンスが一部が異なる場合もあります。
Jettyのchunkサポートは4.2.9あたり。
Subsonicの標準インストールの場合組み込まれているJettyは6.1.xです。
この記事でインストールしているのはAirsonicとJetty9.4.8です。
ファイルをstreamしてみると、Subsonic(6.1.x)でもAirsonic(9.4.8)でもchunk。
|
|
{Access-Control-Allow-Origin=[*], Expires=[Thu, 01 Jan 1970 00:00:00 GMT], Set-Cookie=[player-736374657374=74;Path=/;Expires=Wed, 06-Mar-19 04:30:39 GMT], Content-Type=[audio/mpeg] ETag=[18042], X-Content-Duration=[83.0], Transfer-Encoding=[chunked], Server=[Jetty(6.1.x)]} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
{X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[Thu, 01 Jan 1970 00:00:00 GMT], X-Frame-Options=[SAMEORIGIN], Access-Control-Allow-Origin=[*], Set-Cookie=[player-61646d696e=3;Path=/airsonic;Expires=Wed, 06-Mar-2019 04:28:23 GMT;Max-Age=31536000], Content-Type=[audio/mpeg] ETag=[236], Accept-Ranges=[bytes], X-Content-Duration=[276.0], Transfer-Encoding=[chunked], Server=[Jetty(9.4.8.v20171121)]} |
streamではなくdownloadの場合、Subsonic(6.1.x)だけchunk。
クライアントの動作には影響はありません。
そのうち調べます。
|
|
{Access-Control-Allow-Origin=[*], Expires=[Thu, 01 Jan 1970 00:00:00 GMT], Set-Cookie=[player-736374657374=74;Path=/;Expires=Wed, 06-Mar-19 02:45:24 GMT], Content-Type=[application/x-download], Last-Modified=[Wed, 17 Aug 2016 04:39:44 GMT], Content-Disposition=[attachment; filename*=UTF-8''EL%20MIRAGE.mp%34], Transfer-Encoding=[chunked], Server=[Jetty(6.1.x)]} |
|
|
{X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[Thu, 01 Jan 1970 00:00:00 GMT], X-Frame-Options=[SAMEORIGIN], Access-Control-Allow-Origin=[*], Last-Modified=[Wed, 17 Aug 2016 04:39:44 GMT], Set-Cookie=[player-61646d696e=3;Path=/airsonic;Expires=Wed, 06-Mar-2019 02:48:22 GMT;Max-Age=31536000], ETag=[3847], Accept-Ranges=[bytes], Content-Type=[application/x-download], Content-Disposition=[attachment; filename*=UTF-8''EL%20MIRAGE.mp%34], Content-Length=[15099762], Server=[Jetty(9.4.8.v20171121)]} |