Armeriaのチュートリアルを書いてみる
https://line.github.io/armeria/index.html
RESTサーバーとして使う
まずは、プロジェクトを用意します。
dependencyにcom.linecorp.armeria:armeria:0.62.0を追加します。
mavenの場合、次のようなpom.xmlを用意します。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany</groupId> <artifactId>armeria_tutorial</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>com.linecorp.armeria</groupId> <artifactId>armeria</artifactId> <version>0.62.0</version> </dependency> </dependencies> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>10</maven.compiler.source> <maven.compiler.target>10</maven.compiler.target> </properties> </project>
sourceやtargetは環境に合わせて設定してください。1.8以降で動くはず。
まずは、簡単なHTTP RESTサーバーを書いてみます。
package tutorial; import com.linecorp.armeria.common.HttpResponse; import com.linecorp.armeria.common.HttpStatus; import com.linecorp.armeria.common.MediaType; import com.linecorp.armeria.server.Server; import com.linecorp.armeria.server.ServerBuilder; import java.util.concurrent.CompletableFuture; public class BasicServer { public static void main(String[] args) { ServerBuilder sb = new ServerBuilder(); sb.http(8080); sb.service("/hello", (ctx, res) -> HttpResponse.of( HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "Hello!!")); Server server = sb.build(); CompletableFuture<Void> future = server.start(); future.join(); } }
http://localhost:8080/helloにアクセスしてみます。
詳しくはこちらを
https://line.github.io/armeria/server-basics.html
アノテーションでサービスを定義する
serviceメソッドでURLとサービスを結び付けていましたが、通常のクラスにアノテーションを付けたメソッドとしてサービスを定義することもできます。
package tutorial; import com.linecorp.armeria.common.HttpResponse; import com.linecorp.armeria.common.HttpStatus; import com.linecorp.armeria.common.MediaType; import com.linecorp.armeria.server.annotation.Get; import com.linecorp.armeria.server.annotation.Param; public class MyService { @Get("/service") public HttpResponse service() { return HttpResponse.of( HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "My Service"); } @Get("/greet/:name") public HttpResponse greet(@Param("name") String name) { return HttpResponse.of( HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, "Hello " + name); } }
BasicServerに追加します。
sb.annotatedService(new MyService());
詳しくはこのあたり
https://line.github.io/armeria/server-annotated-service.html
Thriftサーバーとして使う
ThriftのIDLを書きます。
/src/main/thriftにhello.thriftファイルを置きます。
namespace java tutorial.thrift.hello service HelloService { string hello(1:string name) }
これをthriftコンパイラでJavaコードを生成します。
ここからダウンロードします
https://thrift.apache.org/download
$ thrift -out src/main/java --gen java src/main/thrift/hello.thrift
こんな感じのソースが生成されます。
/** * Autogenerated by Thrift Compiler (0.11.0) * * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING * @generated */ package tutorial.thrift.hello; @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"}) @javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2018-04-10") public class HelloService { public interface Iface { public java.lang.String hello(java.lang.String name) throws org.apache.thrift.TException; } ...
実際はmavenのプラグインを使ってmaven上でコンパイルできるようにしたほうがいいけど、このあたりを見てがんばる感じで。
https://stackoverflow.com/questions/18767986/how-can-i-compile-all-thrift-files-thrift-as-a-maven-phase
できたinterfaceを実装したコードを書きます。
package tutorial; import org.apache.thrift.TException; import tutorial.thrift.hello.HelloService; public class HelloServiceImpl implements HelloService.Iface { @Override public String hello(String name) throws TException { return String.format("Hello %s!!!", name); } }
ArmeriaでThriftを使うためにdependencyにarmeria-thriftを追加します。
<dependency> <groupId>com.linecorp.armeria</groupId> <artifactId>armeria-thrift</artifactId> <version>0.62.0</version> </dependency>
そしたらserviceとして追加します。ここでは別サーバーとして追加してみましょう。
future.join()で処理がブロックしてしまうので、そこは消してまとめてjoinします。
CompletableFuture<Void> tfuture = new ServerBuilder() .http(8081) .service("/hello", THttpService.of(new HelloServiceImpl())) .build() .start(); CompletableFutures.allAsList(Arrays.asList( future, tfuture)).join();
詳しくはこちらを。
https://line.github.io/armeria/server-thrift.html
DocServiceを使う
しかしながら、クライアント作るまで動かせないのは不便ですね。
そこでArmeriaではDocServiceというのが用意されています。
CompletableFuture<Void> tfuture = new ServerBuilder() .http(8081) .service("/hello", THttpService.of(new HelloServiceImpl())) .serviceUnder("/docs", new DocService()) .build() .start();
serviceUnderにするのを忘れないように。
http://localhost:8081/docsにアクセスすると、呼び出しが行えます。
ただ、いきなりJSONを入力しろと言われても何を入れていいかわからないので、サンプルが欲しいところ。ということで、DocserviceBuilderを使います。
.serviceUnder("/docs", new DocServiceBuilder() .exampleRequest(new HelloService.hello_args("naoki")) .build())
詳しくはこちらを。
https://line.github.io/armeria/server-docservice.html
Thriftクライアントとして使う
さて、ではThriftサーバーを呼びだしてみましょう。
HelloService.Iface helloService = Clients.newClient( "tbinary+http://localhost:8081/hello", HelloService.Iface.class); sb.service("/thello/{name}", (ctx, res) -> HttpResponse.of( HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, helloService.hello(ctx.pathParam("name"))));
HTTPクライアントとして使う
さて、最後にHTTPクライアントとしても使ってみます。
まずはHttpClientライブラリを。
HttpClient httpClient = HttpClient.of("http://localhost:8080"); CompletableFuture<Void> hfuture = new ServerBuilder() .http(8082) .service("/hello", (ctx, res) ->{ CompletableFuture<AggregatedHttpMessage> msg = httpClient.get("/hello").aggregate(); return HttpResponse.of( HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, msg.get().content().toStringAscii()); }) .build() .start(); CompletableFutures.allAsList(Arrays.asList( future, tfuture, hfuture)).join();
Retrofitを使う
実際は、HTTP呼び出しは結構多いのでRetrofitでメソッドに結び付けたい。
ということで、armeria-retrofit2をdependencyに加えます。また、Retrofitで使うconverterやadapterも追加しておきます。
<dependency> <groupId>com.linecorp.armeria</groupId> <artifactId>armeria-retrofit2</artifactId> <version>0.62.0</version> </dependency> <dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>converter-scalars</artifactId> <version>2.4.0</version> </dependency> <dependency> <groupId>com.squareup.retrofit2</groupId> <artifactId>adapter-java8</artifactId> <version>2.4.0</version> </dependency>
HTTP呼び出しをメソッドとして定義します。
package tutorial; import java.util.concurrent.CompletableFuture; import retrofit2.http.GET; import retrofit2.http.Path; public interface MyHttp { @GET("/hello") CompletableFuture<String> hello(); @GET("/thello/{name}") CompletableFuture<String> hello(@Path("name") String name); }
このメソッドを使ってHTTP呼び出し。
Retrofit retrofit = new ArmeriaRetrofitBuilder() .baseUrl("http://localhost:8080/") .addConverterFactory(ScalarsConverterFactory.create()) .addCallAdapterFactory(Java8CallAdapterFactory.create()) .build(); MyHttp myHttp = retrofit.create(MyHttp.class); CompletableFuture<Void> hfuture = new ServerBuilder() .http(8082) .service("/hello", (ctx, res) ->HttpResponse.of( HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, myHttp.hello().get())) .service("/hello/{name}", (ctx, res) -> HttpResponse.of( HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, myHttp.hello(ctx.pathParam("name")).get())) .build() .start();