[Microservices]Hibernateを使ったHelidonマイクロサービスの作成とデプロイ パート1/ Building And Deploying A Helidon Microservice With Hibernate Part 1

原文はこちら


繰り返し述べていますが、ソリューションを選ぶ前にアプリケーションそれぞれが抱える独特の要件を評価すべきであり、そのためこのブログシリーズで紹介する私の意見はあなたの組織の選択とは異なってくるかもしれないことに留意してくださいね。あなたが自問すべき重要な問いは以下でしょう:

  • 私の仕事にとってマイクロサービスは適切なツールだろうか?
  • マイクロサービスは私の問題をメンテナンス可能な方法で解決するだろうか?
  • 我々はこのソリューションを実装するに十分な予算を持っているだろうか?
新しい考え方を導入するとき、後では解決しがたい問題を招き入れてしまうこともあるため、これらを自問してみることは重要です。

マイクロサービスパターン

コードに進む前にマイクロサービスでのデータ管理のいくつかのパターンを定義するところから始めましょう。マイクロサービスの場合の最もわかりやすいパターンは、共有データベースパターンとサービス毎のデータベース(あるいはスキーマ)パターンです。ここから紹介していきます。

共有データベース

モノリスでは、通常我々はデータを単一のリレーショナルデータベースに保持します。これによりJOINを使ったクエリを書いたり、ACIDトランザクションを使ってデータ一貫性を保証したりと、永続化とクエリにまつわる部分を簡単に済ますことができます。共有データベースのマイクロサービスパターンでは、複数のサービスが単一のデータベースを共有しており、それぞれがテーブルをJOINしながらクエリをしてデータを取得したり、トランザクションを使用して一貫性を保証できる信頼性のあるやり方でデータを更新したりします。このことにより、このパターンは新人開発者たちにも理解しやすいものになっていますが、一方でAPIが複雑になってくると課題も生まれてきます。カラムを追加したりデフォルト値を変更したりすると同じテーブルにアクセスしているサービスを壊してしまうかもしれないため、スキーマの変更は他のサービスの開発者たちと調整したうえで行わなければなりません。また、長時間に渡るトランザクションが共有テーブル上でロックを保持することにより、他のサービスをブロックしてしまうかもしれません。更に、このパターンでは全てのサービスがデータをトラディショナルなリレーショナルデータベースに保持する前提となっており、NoSQLやGraph DBでドキュメントを利用する可能性を排除してしまっています(ただし少し後に紹介するようなワークアラウンドはあります)。

サービス毎のデータベース(あるいはスキーマ)

サービス毎のデータベース(あるいはスキーマ)パターンは共有データベースパターンのいくつかの欠点に対処しています。サービスがそれぞれ自身のデータベースを持っており、これは要はデータベースがサービスの実装の一部であるということを意味しています。このやり方ではスキーマの変更は他のサービスには影響しないということになります。なお、サービスごとにデータベース・サーバーを持つ必要は必ずしもありません。多くの場合サービスごとに独立したテーブル(他のサービスからはアクセスを制限されたユーザー、パーミッションをそれぞれのテーブルに付与しておきます)というかたち、あるいはサービスごとにユニークなスキーマを割り振るというかたちで実現されます。サービス毎のデータベースを利用するということはすなわち、サービスそれぞれの必要に応じて最も適した種類のデータベースを使えるようになるということを意味します。もちろん、このパターンにはデメリットもあります。トランザクションの管理が難しくなります。参照整合性の保証は簡単ではなくなるでしょう。データをJOINしてのクエリは不可能ではないにしても難しいでしょう。

もうわかったからコード見せてよ!

ここまでこのシリーズではマイクロサービスとは何か、あなたはなぜそれを使うべきなのかもしれないのかについて長々と語ってきましたが、理論を理解するのに最良な方法はいつもコードを見ることですよね。ということでようやくコードを見ていくことにします。このシリーズでは、何回かに分けてソーシャルメディア形式のアプリケーションのためのAPIを作っていきます。この中ではクラウドの様々な機能を使うだけでなく、いくつかの異なったマイクロサービスパターンを活用でき、そこに付き物となる興味深い問題への対処方法を紹介していくことになります。

ユーザーサービスの構築

説明

このサービスではHelidon MPをHibernateとともに利用し、ユーザーをOracle ATPインスタンスの user テーブルに永続化します。始めるために、Helidon Mavenアーキタイプを使いサービスのためにいくつかのファイルと構成の土台を作っていきましょう。以下がコマンドです(お好みに応じてパスを修正してもいいし、そのまま使ってもいいです):
mvn archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.archetypes \
-DarchetypeArtifactId=helidon-quickstart-mp \
-DarchetypeVersion=1.1.1 \
-DgroupId=codes.recursive \
-DartifactId=user-svc \
-Dpackage=codes.recursive.cnms.user
view rawhelidon-archetype.sh hosted with ❤ by GitHub
生成されたコードを見たり直したりする前に、このマイクロサービス用のスキーマユーザーを作成しておきましょう。可動しているATPインスタンスにadminユーザーとして接続し、次のクエリを実行する必要があります。このポストシリーズで紹介されているSQL Developer Webを使うと簡単にできます。準備ができたら、以下を実行してください(パスワードを強いものに修正するのをお忘れなく)。
CREATE USER usersvc IDENTIFIED BY "STRONGPASSW0RD";
GRANT create session TO usersvc;
GRANT create table TO usersvc;
GRANT create view TO usersvc;
GRANT create any trigger TO usersvc;
GRANT create any procedure TO usersvc;
GRANT create sequence TO usersvc;
GRANT create synonym TO usersvc;
GRANT CONNECT TO usersvc;
GRANT RESOURCE TO usersvc;
GRANT UNLIMITED TABLESPACE TO usersvc;
view rawcreate-usersvc.sql hosted with ❤ by GitHub
SQL Developer Webを使っているなら、adminユーザーで以下のコマンドを使い各スキーマが有効化されていることを確かめておく必要があります:
BEGIN
 ords_admin.enable_schema(
  p_enabled => TRUE,
  p_schema => 'SCHEMA-NAME',
  p_url_mapping_type => 'BASE_PATH',
  p_url_mapping_pattern => 'schema-alias',
  p_auto_rest_auth => NULL
 );
 commit;
END;
上記のコマンドでは、プレースホルダの値は以下のように置き換えてください:
  • SCHEMA-NAME はデータベーススキーマ名、全て大文字
  • schema-alias はスキーマ名のエイリアスで、SQL Developer Webを使ってアクセスする際にURLに使われます。Oracleはスキーマ名を暴露しないほうがよいというセキュリティ上の理由から、スキーマ名をそのまま使わないことを推奨しています。
ユーザーアクセスを有効化したら、ADMINユーザーから当該ユーザーにSQL Developer WebにアクセスするためのURLを提供しましょう。このURLはADMINユーザーがSQL Developer WebにアクセスするためのURLと同じものですが、ただし admin/ の部分はschema-alias/で置き換えてください。
これ以降、私達は usersvc ユーザーを使っていくので、一旦SQL Developer Web(その他のツールをお使いならそのツール)からログアウトして先程作成した新しいユーザー名とパスワード、および上記で説明した適切なURLでログインし直しましょう。 usersvc でログインしたら、以下を実行してマイクロサービス用のテーブルを作成します:

CREATE TABLE users(
"ID" VARCHAR2(32 BYTE) DEFAULT ON NULL SYS_GUID(), 
"FIRST_NAME" VARCHAR2(50 BYTE) COLLATE "USING_NLS_COMP" NOT NULL ENABLE, 
"LAST_NAME" VARCHAR2(50 BYTE) COLLATE "USING_NLS_COMP" NOT NULL ENABLE, 
"USERNAME" VARCHAR2(50 BYTE) COLLATE "USING_NLS_COMP" NOT NULL ENABLE, 
"CREATED_ON" TIMESTAMP (6) DEFAULT ON NULL CURRENT_TIMESTAMP
CONSTRAINT "USER_PK" PRIMARY KEY ("ID")
);
view rawcreate-user-tbl.sql hosted with ❤ by GitHub
次に、依存モジュールを取得しておく必要があります。プロジェクトルートに /build-resource というフォルダを作成し、更にその配下に /libsというサブディレクトリを作成してください。ATPインスタンスと接続するために必要となるOJDBCドライバーをプロジェクトで使えるように、以下のJARファイルを取得する必要があります:
  • ojdbc8.jar
  • oraclepki.jar
  • osdt_cert.jar
  • osdt_core.jar
Download the JARs from Oracle and place them in /build-resource/libs.  We also need to publish these to our local Maven repo so that when we run the application locally they'll be properly resolved. The following commands should help you out with that:
OracleからJARをダウンロードし、 /build-resource/libsに置きましょう。また、ローカルのMavenリポジトリにこれらをパブリッシュしておき、ローカルでアプリケーションを稼働させたときにこれらが適切にリゾルブされるようにしておきましょう。以下のコマンドでできます:
mvn install:install-file -Dfile=/path/to/ojdbc8.jar -DgroupId=com.oracle.jdbc -DartifactId=ojdbc8 -Dversion=18.3.0.0 -Dpackaging=jar
mvn install:install-file -Dfile=/path/to/oraclepki.jar -DgroupId=com.oracle.jdbc -DartifactId=oraclepki -Dversion=18.3.0.0 -Dpackaging=jar
mvn install:install-file -Dfile=/path/to/osdt_core.jar -DgroupId=com.oracle.jdbc -DartifactId=osdt_core -Dversion=18.3.0.0 -Dpackaging=jar
mvn install:install-file -Dfile=/path/to/osdt_cert.jar -DgroupId=com.oracle.jdbc -DartifactId=osdt_cert -Dversion=18.3.0.0 -Dpackaging=jar
次にpom.xmlファイルを修正し、JPAに必要な依存ライブラリであるHibernate、JacksonおよびOJDBCのJARを含むようにしておきます。次のエントリをdependenciesセクションに追加してください:
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>javax.persistence</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.3.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.3.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>18.3.0.0</version>
</dependency>
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>oraclepki</artifactId>
<version>18.3.0.0</version>
</dependency>
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>osdt_core</artifactId>
<version>18.3.0.0</version>
</dependency>
<dependency>
<groupId>com.oracle.jdbc</groupId>
<artifactId>osdt_cert</artifactId>
<version>18.3.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.28</version>
</dependency>
そしてHibernateの永続化に係る設定を追加する必要があります。
/src/main/resources/META-INFに persistence.xml という名前で以下のようにファイルを作成してください:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
             http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">
    <persistence-unit name="UserPU">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="hibernate.connection.driver_class" value="oracle.jdbc.OracleDriver" />
            <property name="hibernate.archive.autodetection" value="class" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle12cDialect" />
            <property name="hibernate.format_sql" value="true" />
            <property name="hbm2ddl.auto" value="validate" />
        </properties>
    </persistence-unit>
</persistence>
view rawpersistence.xml hosted with ❤ by GitHub
そのディレクトリに行ってから、 microprofile-config.properties を以下のように修正します:
# Application properties.
datasource.username=
datasource.password=
datasource.url=
# Microprofile server properties
server.port=8080
server.host=0.0.0.0
これで事前に必要な設定は全て終えたので、アプリケーションコードに進めます。生成された GreetApplication.java を探してください。それを修正してもいいですし、削除して新しく作るのでもいいんですが、結局の所以下のコードを含む UserApplication.java が必要になります:
package codes.recursive.cnms.user;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import io.helidon.common.CollectionsHelper;
import org.glassfish.jersey.jackson.JacksonFeature;
/**
 * Simple Application that managers users.
 */
@ApplicationScoped
@ApplicationPath("/")
public class UserApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        return CollectionsHelper.setOf(
                UserResource.class,
                JacksonFeature.class
        );
    }
}
view rawUserApplication.java hosted with ❤ by GitHub
Use the same process (modify or replace) for GreetingProvider to end up with a UserProvider that looks like so:
同様に GreetingProvider についても修正または削除して再作成して以下のUserProvider を作ります:
package codes.recursive.cnms.user;
import java.util.concurrent.atomic.AtomicReference;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
/**
 * Provider for user config.
 */
@ApplicationScoped
public class UserProvider {
    private final AtomicReference<String> dbUser = new AtomicReference<>();
    private final AtomicReference<String> dbPassword = new AtomicReference<>();
    private final AtomicReference<String> dbUrl = new AtomicReference<>();
    /**
     * Create a new user provider, reading the message from configuration.
     *
     * @param dbUser
     * @param dbPassword
     * @param dbUrl
     */
    @Inject
    public UserProvider(
            @ConfigProperty(name = "datasource.username") String dbUser,
            @ConfigProperty(name = "datasource.password") String dbPassword,
            @ConfigProperty(name = "datasource.url") String dbUrl
    ) {
        this.dbUser.set(dbUser);
        this.dbPassword.set(dbPassword);
        this.dbUrl.set(dbUrl);
    }
    String getDbUser() { return dbUser.get(); }
    String getDbPassword() { return dbPassword.get(); }
    String getDbUrl() { return dbUrl.get(); }
    void setDbUser(String dbUser) { this.dbUser.set(dbUser); }
    void setDbPassword(String dbPassword) { this.dbPassword.set(dbPassword); }
    void setDbUrl(String dbUrl) { this.dbUrl.set(dbUrl); }
}
view rawUserProvider.java hosted with ❤ by GitHub
ここで UserProvider クラスはアプリケーション稼働時には生成された設定値を格納します。ここまででマイクロサービスの実ロジックを書き始める準備ができました。このシリーズの次のポストでは、コードを深く掘り下げていき、サービスをOracle Cloud上にデプロイ、稼働させてみます。

0 件のコメント:

コメントを投稿