Oracle Helidonを使用したマイクロサービス
1. 概要
Helidonは、最近Oracleによってオープンソース化された新しいJavaマイクロサービスフレームワークです。 J4C(Java for Cloud)という名前でOracleプロジェクトで内部的に使用されていました。
このチュートリアルでは、フレームワークの主要な概念について説明し、次にヘリドンベースのマイクロサービスの構築と実行に移ります。
2. プログラミングモデル
現在、the framework supports two programming models for writing microservices: Helidon SE and Helidon MP.
Helidon SEは、リアクティブプログラミングモデルをサポートするマイクロフレームワークとして設計されていますが、Helidon MPは、Java EEコミュニティがポータブルな方法でマイクロサービスを実行できるEclipse MicroProfileランタイムです。
どちらの場合も、HelidonマイクロサービスはJava SEアプリケーションであり、mainメソッドから小さなHTTPサーバーを起動します。
3. ヘリドンSE
このセクションでは、Helidon SEの主なコンポーネントであるWebサーバー、構成、セキュリティについて詳しく説明します。
3.1. Webサーバーのセットアップ
WebServer API,を使い始めるには、必要なMaven dependencyをpom.xmlファイルに追加する必要があります。
io.helidon.webserver
helidon-webserver
0.10.4
単純なWebアプリケーションを作成するには、we can use one of the following builder methods: WebServer.create(serverConfig, routing) or just WebServer.create(routing)を使用します。 最後のものは、サーバーがランダムなポートで実行できるデフォルトのサーバー構成を取ります。
これは、事前定義されたポートで実行される単純なWebアプリケーションです。 また、 ‘/greet'パスとGETメソッドを使用してHTTPリクエストのグリーティングメッセージで応答する単純なハンドラーを登録しました。
public static void main(String... args) throws Exception {
ServerConfiguration serverConfig = ServerConfiguration.builder()
.port(9001).build();
Routing routing = Routing.builder()
.get("/greet", (request, response) -> response.send("Hello World !")).build();
WebServer.create(serverConfig, routing)
.start()
.thenAccept(ws ->
System.out.println("Server started at: http://localhost:" + ws.port())
);
}
最後の行は、サーバーを起動してHTTPリクエストの処理を待つことです。 ただし、このサンプルコードをmainメソッドで実行すると、次のエラーが発生します。
Exception in thread "main" java.lang.IllegalStateException:
No implementation found for SPI: io.helidon.webserver.spi.WebServerFactory
WebServerは実際にはSPIであり、ランタイム実装を提供する必要があります。 現在、Nettyコアに基づくHelidon provides the NettyWebServer implementation。
この実装のMaven dependencyは次のとおりです。
io.helidon.webserver
helidon-webserver-netty
0.10.4
runtime
これで、メインアプリケーションを実行し、構成されたエンドポイントを呼び出すことで動作することを確認できます。
http://localhost:9001/greet
この例では、ビルダーパターンを使用してポートとパスの両方を構成しました。
Helidon SEでは、構成データがConfigAPIによって提供される構成パターンを使用することもできます。 これは次のセクションの主題です。
3.2. Config API
The Config API provides tools for reading configuration data from a configuration source。
Helidon SEは、多くの構成ソースの実装を提供します。 デフォルトの実装はhelidon-configによって提供されます。ここで、構成ソースはクラスパスの下にあるapplication.propertiesファイルです。
io.helidon.config
helidon-config
0.10.4
構成データを読み取るには、デフォルトのビルダーを使用する必要があります。デフォルトでは、application.properties:から構成データを取得します。
Config config = Config.builder().build();
src/main/resourceディレクトリの下に次の内容のapplication.propertiesファイルを作成しましょう。
server.port=9080
web.debug=true
web.page-size=15
user.home=C:/Users/app
To read the values we can use the Config.get() methodの後に、対応するJavaタイプへの便利なキャストが続きます。
int port = config.get("server.port").asInt();
int pageSize = config.get("web.page-size").asInt();
boolean debug = config.get("web.debug").asBoolean();
String userHome = config.get("user.home").asString();
実際、デフォルトのビルダーは、最初に見つかったファイルを次の優先順位で読み込みます。application.yaml, application.conf, application.json, and application.properties.最後の3つの形式には、追加の関連する構成の依存関係が必要です。 たとえば、YAML形式を使用するには、関連するYAMLconfigの依存関係を追加する必要があります。
io.helidon.config
helidon-config-yaml
0.10.4
そして、application.ymlを加える。
server:
port: 9080
web:
debug: true
page-size: 15
user:
home: C:/Users/app
同様に、JSON簡略化形式またはJSON形式であるCONFを使用するには、helidon-config-hocon依存関係を追加する必要があります。
これらのファイルの構成データは、環境変数とJavaシステムプロパティによってオーバーライドされる可能性があることに注意してください。
We can also control the default builder behaviorは、環境変数とシステムプロパティを無効にするか、構成ソースを明示的に指定します。
ConfigSource configSource = ConfigSources.classpath("application.yaml").build();
Config config = Config.builder()
.disableSystemPropertiesSource()
.disableEnvironmentVariablesSource()
.sources(configSource)
.build();
クラスパスから構成データを読み取ることに加えて、2つの外部ソース構成、つまりgitとetcd configsを使用することもできます。 このためには、helidon-config-gitとhelidon-git-etcdの依存関係が必要です。
最後に、これらの構成ソースのすべてが私たちのニーズを満たさない場合、Helidonは私たちが構成ソースの実装を提供することを許可します。 たとえば、データベースから構成データを読み取ることができる実装を提供できます。
3.3. Routing API
The Routing API provides the mechanism by which we bind HTTP requests to Java methods.これは、要求メソッドとパスを一致基準として使用するか、RequestPredicateオブジェクトを使用してより多くの基準を使用することで実現できます。
したがって、ルートを構成するには、HTTPメソッドを基準として使用するだけです。
Routing routing = Routing.builder()
.get((request, response) -> {} );
または、HTTPメソッドとリクエストパスを組み合わせることができます。
Routing routing = Routing.builder()
.get("/path", (request, response) -> {} );
RequestPredicateを使用してさらに制御することもできます。 たとえば、既存のヘッダーまたはコンテンツタイプを確認できます。
Routing routing = Routing.builder()
.post("/save",
RequestPredicate.whenRequest()
.containsHeader("header1")
.containsCookie("cookie1")
.accepts(MediaType.APPLICATION_JSON)
.containsQueryParameter("param1")
.hasContentType("application/json")
.thenApply((request, response) -> { })
.otherwise((request, response) -> { }))
.build();
これまで、機能スタイルでハンドラーを提供してきました。 Serviceクラスを使用して、より高度な方法でハンドラーを作成することもできます。
それでは、最初に、作業しているオブジェクトのモデル、Bookクラスを作成しましょう。
public class Book {
private String id;
private String name;
private String author;
private Integer pages;
// ...
}
Service.update()メソッドを実装することで、BookクラスのRESTサービスを作成できます。 これにより、同じリソースのサブパスを構成できます。
public class BookResource implements Service {
private BookManager bookManager = new BookManager();
@Override
public void update(Routing.Rules rules) {
rules
.get("/", this::books)
.get("/{id}", this::bookById);
}
private void bookById(ServerRequest serverRequest, ServerResponse serverResponse) {
String id = serverRequest.path().param("id");
Book book = bookManager.get(id);
JsonObject jsonObject = from(book);
serverResponse.send(jsonObject);
}
private void books(ServerRequest serverRequest, ServerResponse serverResponse) {
List books = bookManager.getAll();
JsonArray jsonArray = from(books);
serverResponse.send(jsonArray);
}
//...
}
また、メディアタイプをJSONとして構成したため、この目的のためにhelidon-webserver-jsonの依存関係が必要です。
io.helidon.webserver
helidon-webserver-json
0.10.4
最後に、we use the register() method of the Routing builder to bind the root path to the resource.この場合、サービスによって構成されたPathsには、ルートパスがプレフィックスとして付けられます。
Routing routing = Routing.builder()
.register(JsonSupport.get())
.register("/books", new BookResource())
.build();
サーバーを起動してエンドポイントを確認できます。
http://localhost:9080/books
http://localhost:9080/books/0001-201810
3.4. セキュリティ
このセクションでは、we’re going to secure our resources using the Security module。
必要なすべての依存関係を宣言することから始めましょう。
io.helidon.security
helidon-security
0.10.4
io.helidon.security
helidon-security-provider-http-auth
0.10.4
io.helidon.security
helidon-security-integration-webserver
0.10.4
helidon-security、helidon-security-provider-http-auth、およびhelidon-security-integration-webserverの依存関係は、MavenCentralから入手できます。
セキュリティモジュールは、認証と承認のための多くのプロバイダーを提供します。 For this example, we’ll use the HTTP basic authentication providerはかなり単純ですが、他のプロバイダーのプロセスはほとんど同じです。
The first thing to do is create a Security instance. We can do it either programmatically for simplicity:
Map users = //...
UserStore store = user -> Optional.ofNullable(users.get(user));
HttpBasicAuthProvider httpBasicAuthProvider = HttpBasicAuthProvider.builder()
.realm("myRealm")
.subjectType(SubjectType.USER)
.userStore(store)
.build();
Security security = Security.builder()
.addAuthenticationProvider(httpBasicAuthProvider)
.build();
または、構成アプローチを使用できます。
この場合、Config APIを介してロードするapplication.ymlファイルですべてのセキュリティ構成を宣言します。
#Config 4 Security ==> Mapped to Security Object
security:
providers:
- http-basic-auth:
realm: "helidon"
principal-type: USER # Can be USER or SERVICE, default is USER
users:
- login: "user"
password: "user"
roles: ["ROLE_USER"]
- login: "admin"
password: "admin"
roles: ["ROLE_USER", "ROLE_ADMIN"]
#Config 4 Security Web Server Integration ==> Mapped to WebSecurity Object
web-server:
securityDefaults:
authenticate: true
paths:
- path: "/user"
methods: ["get"]
roles-allowed: ["ROLE_USER", "ROLE_ADMIN"]
- path: "/admin"
methods: ["get"]
roles-allowed: ["ROLE_ADMIN"]
それをロードするには、Configオブジェクトを作成してから、Security.fromConfig()メソッドを呼び出す必要があります。
Config config = Config.create();
Security security = Security.fromConfig(config);
Securityインスタンスを取得したら、最初にWebSecurity.from()メソッドを使用してWebServerに登録する必要があります。
Routing routing = Routing.builder()
.register(WebSecurity.from(security).securityDefaults(WebSecurity.authenticate()))
.build();
セキュリティとWebサーバー構成の両方をロードする構成アプローチを使用して、WebSecurityインスタンスを直接作成することもできます。
Routing routing = Routing.builder()
.register(WebSecurity.from(config))
.build();
これで、/userおよび/adminパスのハンドラーをいくつか追加し、サーバーを起動して、それらにアクセスしてみることができます。
Routing routing = Routing.builder()
.register(WebSecurity.from(config))
.get("/user", (request, response) -> response.send("Hello, I'm Helidon SE"))
.get("/admin", (request, response) -> response.send("Hello, I'm Helidon SE"))
.build();
4. ヘリドンMP
Helidon MP is an implementation of Eclipse MicroProfileであり、MicroProfileベースのマイクロサービスを実行するためのランタイムも提供します。
すでにan article about Eclipse MicroProfileがあるので、そのソースコードをチェックして、HelidonMPで実行するように変更します。
コードを確認した後、すべての依存関係とプラグインを削除し、HelidonMPの依存関係をPOMファイルに追加します。
io.helidon.microprofile.bundles
helidon-microprofile-1.2
0.10.4
org.glassfish.jersey.media
jersey-media-json-binding
2.26
helidon-microprofile-1.2およびjersey-media-json-bindingの依存関係は、MavenCentralから入手できます。
次に、次の内容のwe’ll add the beans.xml file under the src/main/resource/META-INF directory:
LibraryApplicationクラスで、サーバーがリソースをスキャンしないように、getClasses()メソッドをオーバーライドします。
@Override
public Set> getClasses() {
return CollectionsHelper.setOf(BookEndpoint.class);
}
最後に、メインメソッドを作成し、次のコードスニペットを追加します。
public static void main(String... args) {
Server server = Server.builder()
.addApplication(LibraryApplication.class)
.port(9080)
.build();
server.start();
}
以上です。 これで、すべての書籍リソースを呼び出すことができるようになります。
5. 結論
この記事では、ヘリドンの主要コンポーネントについて説明し、ヘリドンSEとMPのいずれかを設定する方法も示しました。 Helidon MPは単なるEclipse MicroProfileランタイムであるため、これを使用して既存のMicroProfileベースのマイクロサービスを実行できます。
いつものように、上記のすべての例のコードはover on GitHubで見つけることができます。