備忘録 blog

Docker/Machine Learning/Linux

Java 軽量 Web フレームワーク調査

tl;dr

Java で利用できる軽量 Web フレームワークを調査した。特に、 Helidon について最後に説明する。

なぜ Web フレームワークが必要となるか

まずは冗長かもしれないが、 Web フレームワークがなぜ必要となってきたか、について1つの説明を考えてみたい。

1つのウェブサービスを実現するために、複数のパーツを組み合わせてそれぞれをうまく動かすことが必要となる。パーツというのは、例えばTwitter のようなサービスを作ると考えた時に、ユーザ情報やツイート情報などを管理しておくためのデータベース、データベースの内容をクライアントに伝えるサーバ、サーバから受け取った内容を人間が見やすい形にして表示する HTML、CSSJavaScript などの実装が必要になってくる。

これらは、大別してDBサーバ+アプリサーバ(バックエンド)+クライアント(フロントエンド)の3つの要素に分けることができるだろう。その中で、具体的にはバックエンドとフロントエンドにおいてはある程度の量の実装が必要である。これらの実装のためには、データベースの知識、 web の知識、プログラミング言語の知識、フロントエンドの知識などが必要となり、幅広い知識を薄く広く知っていないといけないというのが web 開発の全体的な特徴である。

そのため、バックエンド開発では一般にフレームワークと呼ばれる、webアプリケーションを実装する上で必要な機能を最初から実装してあって、利用者はその関数を呼ぶだけでwebアプリケーションを実装できるようになっているライブラリ群を用いることがある。これによって、同じ目的に利用するコードを別々の人が何度も書かなければならなくなってしまうことを防ぐうえ、最初からセキュリティに配慮されたシステムを構築することがある程度可能になる。

こうしたフレームワークの上で、バックエンドはMVCアーキテクチャによって実装することが、一つのプラクティスとして確立している。MはModel、VはView、CはControllerである。Modelはしばしばデータベースと密に連携し、データベースに入っているデータをオブジェクトとして扱えるような機能が実装されている。ViewはModelの内容をhtmlとしてレンダリングする。ControllerはModelのデータをViewに受け渡す媒介の役割を果たす。実際にはバックエンドの役割は多岐に渡り、ユーザ認証、アクセス認可、セッション管理、テンプレートエンジン、ルーティング、ORM(データベースとの連携)などが含まれる。

このとき一般には、車輪の再発明を防ぐためにフレームワークを用いて実装することになる。こうしたフレームワークは、「フルスタック型」と「軽量型」の2通りに分けられると考えられる。

フルスタック型と軽量型フレームワーク

フルスタック型

例: Ruby on Rails/HANAMI, Django(Python), Yesod(Haskell), Phoenix(Elixir), Spring boot(Java), Sails.js(Node.js), Revel(Go), ...

フルスタック型は、そのライブラリとそのプラグインを用いるだけで、基本的な web サービスの提供ができるように構成されたライブラリ群で、しばしば開発者が細かい設定をせずとも、デフォルトの設定項目をそのまま利用するだけでデータベースへの連携やログイン機能の実装など、Web サービスに必要な機能が提供される、といった特徴を持つ。

バックエンドとフロントエンドを密に実装することになる。例えば、1つのソースコードルートの中でバックエンドのコードとフロントエンドのhtml, jsが共存するような形になる。Webアプリはこれらのフレームワークの上でバックエンドからフロントエンドまで一括して作ることができる。このことでシステムは複雑になりがちだが、コードがモノリシックなので、開発の初期にはデータ構造の変更に対して反映が容易という利点がある。

軽量型

例: Sinatra(Ruby), Flask(Python), ...

基本的には、バックエンドのDBと接続して、jsonを返す簡易サーバーを書くことになる。データに対する処理のロジックを記述し、スマホ用のAPIとwebアプリ用のAPIだけをそれぞれ実装するというシンプルな構成になる。

バックエンドとフロントエンドをわけて実装することになり、これとは別にフロントエンドはAPIから受け取った情報をもとに、ビューを提供する。フロントエンドはバックエンドから独立して実装出来るので、かなり自由に実装出来る反面、認証などの連携が面倒であったり、バックエンドでAPIの変更した場合、フロントエンドに反映することが必要となる。

サービスの肥大化を防ぐことができるという利点がある一方で、こうした軽量型のライブラリでは先述の要件に対して必要最小限の機能しか提供されていないことも多く、コード量が増えてしまうという点もある。

Java における Web フレームワーク

Java でウェブサーバーを実装する場合、重厚なものとしては Spring があるが、さまざまな理由から Spring よりも軽量な(あるいは開発者の実装量が少ない)フレームワークを選択したくなる、ということがある。

Spring | Home

例えば、 Spring, Vert.x, Helidon, Quarkus の4つの web フレームワーク を比較したベンチマークによれば、起動時間や平均レスポンスタイムがSpring に比べてその他3つのフレームワークの方が早い、という結果が報告されている。

blogs.itemis.com

どうしてこのような差が生じているかということについては、さまざまな要因があるが、1つには利用しているサーブレットコンテナ( Web Engine )の違いという要因があると考えられる。サーブレットコンテナは、リクエストやレスポンス・セッション管理など、Web サーバーのコアになる機能を備えており、Web フレームワークは何らかのサーブレットコンテナ上に、さまざまなラッパー関数を提供していると考えることができる。

Spring framework 上で実装されている web フレームワークである Spring boot では、 Tomcat, Jetty, Undertow をサーブレットコンテナとして利用することができる。ベンチマークとしては以下である。

www.baeldung.com

Jetty は Tomcat の軽量版、のような捉え方をすることができる。実際に軽量フレームワークでは、 jetty と netty が多く使われている。この2つの違いは何だろうか。

stackoverflow.com

gist.github.com

これらを要約すると、 Jetty は軽量であること、Netty は non-blocking であることがそれぞれと特性であり、使い分けるとするならば、ネットワークプロトコルを頻繁に扱い、それを非ブロッキングにしたい場合は、Netty、軽量のHTTPサーブレットコンテナが必要な場合は、Jetty、というように使い分けることはできるだろう。とはいえ、開発の初期にはこれらの違いよりも、フレームワークとしての完成度であったり成熟度の方が問題になるケースが多いだろう。

軽量web フレームワークを比較したスライドとして、以下のものが詳しい。

https://www.jfokus.se/jfokus20-preso/The-ultimate-microframework-smackdown.pdf 

Helidon

Oracle 謹製フレームワーク Helidon についてここで少し紹介する。

https://helidon.io/#/

https://www.infoq.com/jp/articles/helidon-tutorial/

Helidon 2.0 が既にリリースされているが、ウェブ上の情報では Helidon 1.0 の情報も交じっていることが多いので注意が必要である。

Helidon MP と Helidon SE の違い

www.infoq.com

Helidon SE は最もプリミティブな機能を搭載したフレームワークであり、 javalin といったフレームワークと同じレベルのフレームワークであると infoq の記事では整理されている。

Helidon SE は上記のようにコア機能に絞っているのに対して、 Helidon MP では、 CDI (Context and Dependency Injection) extension と呼ばれる、主にデータベース接続のための一種のプラグインが提供されている。上記のページでは、下記の5つが紹介されている。