LoginSignup
6
4

More than 5 years have passed since last update.

GoogleCloudで汎用Database構築4 - kubernetes & SpringBoot & Apache Camel その1 -

Last updated at Posted at 2016-11-14

GYAOのtsです。
我々のチームは、オールパブリッククラウドで、Microservice Architectureを採用した次期バックエンドを設計中です。

経緯

前回はSpringBootのアプリケーションを作成してDockerイメージ化し、GKEで立ち上げてみましたが、実際に永続化層に保存するためにWebserviceを作成する。何を使っても作成できると思うが、 個人的にApacheCamelが気に入っている。
日本ではあまり普及していないようだが、EIPのimplementationであり、様々なコンポーネントを再利用して構築する。
コード量はかなり少なくて済む。今回はCamelに関してはあまり掘り下げないで、SpringBootAppに統合することに主眼を置く。

pom.xml

pom.xmlに必要なライブラリをimportする。FullExecutableJarでパッケージングするので、これらのライブラリはすべて1つのjarに含まれる。

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>jp.co.yahoo.zeolite.agent</groupId>
    <artifactId>storeagent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>storeagent</name>
    <description>Spring Boot Application</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <camel-ver>2.17.1</camel-ver>
    </properties>

    <dependencies>

        <!-- Spring Boot -->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Apache Camel -->

        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-core</artifactId>
            <version>${camel-ver}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-restlet</artifactId>
            <version>${camel-ver}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-spring-boot</artifactId>
            <version>${camel-ver}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.camel</groupId>
            <artifactId>camel-swagger-java</artifactId>
            <version>${camel-ver}</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>1.3.6.RELEASE</version>
                <configuration>
                    <mainClass>jp.co.yahoo.zeolite.agent.StoreagentApplication</mainClass>
                    <layout>WAR</layout>
                    <executable>true</executable>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

追加したのは主に下記のライブラリ。

  • spring-boot-starter-jetty
    • 組み込みtomcat→組み込みjettyにコンテナを切り替え。
  • spring-boot-starter-thymeleaf
    • 可視化のためのtoolも作ろうと思うので、テンプレートエンジンにthymeleaf(タイムリーフ)
  • camel-core
    • Apache Camelのコアライブラリ
  • camel-restlet
    • restAPI作成に使用します。他にいくつかCamelコンポーネントがあるが、今回はこれを使用。
  • camel-swagger-java
    • api-docを作ってくれるのが嬉しい。
    • API Gatewayや、Apigeeとつなぐ際にも便利。今後はこれがrest定義のスタンダードになりそうですね。
  • camel-spring-boot
    • Apache CamelのSpringBootサポート

Route定義

前述のように、Apache Camelは各camelコンポーネントを組み合わせてアプリケーションを構築する。
コンポーネントは主にオフィシャルサイトに集約されている。
RouteBuilderクラスのconfigureメソッドをオーバーライドしてcamelルートと呼ばれるフローを下記のように実装していく。

example
from("jetty:https://0.0.0.0/myapp/").to("bean:myBean?method=methodName");

例えばこの場合だと、localhost:80/myappでアクセスできるwebserviceを立ち上げることができる(ようは組み込みjettyが立ち上がるだけだが)。
header情報やbodyの情報はMessageにバインドされ、ほぼそのままto()で呼び出すコンポーネントに渡される。(この場合はbeanコンポーネントを使用しているので、myBeanでcontextに登録されたインスタンスのmethodNameメソッドに渡される)

今回はcamel-spring-bootを使用する、RouteBuilderクラスを継承した、FatJarRouterクラスが用意されている。

SpringBootアプリケーションの起動クラスがこのFatJarRouterクラスを継承するかたちにすればよい。

StoreagentApplication.java
@SpringBootApplication
@ComponentScan("jp.co.yahoo.zeolite")
public class StoreagentApplication extends FatJarRouter {

    public static void main(String[] args) {
        SpringApplication.run(StoreagentApplication.class, args);
    }

    @Override
    public void configure() throws Exception {

        //handling Exceptions.
        onException(Exception.class)
                .handled(true)
                .setBody(exceptionMessage().prepend("{\"error\":\"").append("\"}"))
                .convertBodyTo(String.class)
                .log(LoggingLevel.ERROR, "${body}")
                .to("direct:setWebserviceResponseHeaders")
                .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(500));

        errorHandler(deadLetterChannel("direct:deadlog").maximumRedeliveries(0));

        from("direct:deadlog").routeId("DEAD_LOG").log(LoggingLevel.ERROR, "${body}");

        from("direct:setWebserviceResponseHeaders")
                .routeId("SET_WEB_HEADERS")
                .setHeader(Exchange.CONTENT_TYPE,
                        constant("application/json; charset=utf-8"));

        //Rest Configurations.
        restConfiguration().component("restlet")
                .enableCORS(true)
                .apiContextPath("/api-docs")
                .apiProperty("api.title", "Zeolite StoreAgent APIs")
                .apiProperty("api.version", "1.0.0")
                .apiProperty("cors", "true")
                .contextPath("/service").host(InetAddress.getLocalHost().getHostName()).port(21001)
                .dataFormatProperty("prettyPrint", "true")
                .bindingMode(RestBindingMode.off)
                .componentProperty("disableStreamCache", "true");

        //Rest API
        rest("/upsert")
                .description("Upsert API. 対象ItemをUpsertします。")
                .produces("application/json").consumes("application/json")
                .post().description("upsert the item.")
                .param().name("itemKey").type(RestParamType.header).description("The key of an item.").dataType("string").endParam()
                .param().name("item").type(RestParamType.body).description("The item to store.").dataType("string").endParam()
                .responseMessage().header("http Status code").endResponseHeader().endResponseMessage()
                .to("direct:upsert");

        from("direct:upsert")
                .setBody(constant("{\"message\" : \"OK!\"}"))
                .to("mock:upsert");



    }
}

今回はcamel-swaggerも使用するので、下記のように仕様書に必要なコメントも一緒にも書いておく。

        //Rest API
        rest("/upsert")
                .description("Upsert API. 対象ItemをUpsertします。")
                .produces("application/json").consumes("application/json")
                .post().description("upsert the item.")
                .param().name("itemKey").type(RestParamType.header).description("The key of an item.").dataType("string").endParam()
                .param().name("item").type(RestParamType.body).description("The item to store.").dataType("string").endParam()
                .responseMessage().header("http Status code").endResponseHeader().endResponseMessage()
                .to("direct:upsert");

あとはmaven経由で立ち上げるだけ。
Postmanを使用してリクエストしてみる。

スクリーンショット 2016-11-14 22.03.01.png

普通に返ってくる。

また、Camel Swaggerも試してみる。
Swaggerに関しては下記を参照

camel-swaggerはデフォルトで/api-docsに定義書を作成する。
この場合、http://localhost:21001/service/api-docs
にアクセスすると、定義書が返ってってくる。
これをSwaggerUIや、つなぐ先のAPIGateway等に食わせると、楽チン。

スクリーンショット 2016-11-14 22.09.36.png

所感

これだけのコーディングで定義書付きのrest apiが立ち上がる。しかもhttpのheaderやbodyはそのままMessageのheader、bodyにバインドされるので、使い勝手がいい。

何より、実績のあるものを使いまわせるのが嬉しい。

以前はCamelBootクラスを独自に作ってFullExecutableで機動するように作っていたが、SpringBootの登場によって、一切いらなくなった。jarは一個で起動スクリプトもいらない。置いて叩くだけ。なのでDockerとも相性がいい。macだろうがWindowsだろうがIDEで開発し、maven経由でそのまま立ち上げデバッグが可能。
自分の中では鉄板構成になりつつある。
知っておくといざという時にPJを救う強力な武器になる。

私は幾度となく救われました。
基本、何がこようがSpringBoot+ApacheCamel+Thymeleaf+Commonsで短期間でどうにかなったりします。

POJOなのでアウトソーシングしやすかったりもします。
なんでもっと流行らないかなぁ。。。

次回

上記をもっと掘り下げてみようかと。

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4