LoginSignup
1
3

More than 5 years have passed since last update.

SpringBoot+インメモリデータグリッド入門

Last updated at Posted at 2019-01-26

インメモリデータグリッドについて勉強してみたついでに、実際にアプリに組み込むとどうなるのか気になったので入門してみた。

インメモリデータグリッドとは

  • データを複数サーバで分散管理する仕組み。
  • 全サーバが重複してデータを持つこと(レプリケーション方式)もできれば、あるグループのサーバにとってのみ必要なデータを、そのグループ内で重複して管理する(パーティション方式)といった柔軟なデータの信頼性確保が可能。
  • DBのようにディスクI/Oが発生しないため、高速にデータのCRUD操作、P2Pのデータ同期が行える。
  • 信頼性、高速性を備えたアーキテクチャ。らしい。

下記の記事を参考に学習
- 概要を掴むのにおすすめ
- 具体的なアーキテクチャの種類

SpringBoot + Apach GEODEでアプリを作ってみよう

  • 色々と記事をザッピングしたところで実際に作ってみたくなったのでSpringDataGeodeというプロジェクトがSpringのgitリポジトリにあるので、それを使ってSpringBootでインメモリデータグリッドを体感してみようと思います。
  • ユーザ登録・検索を行うサーバアプリケーションを想定して作成します。

Apache GEODEのclient/serverモデルでアプリを作る

  • Spring Initializerでclient,serverのアプリの雛形を作成する。
    • Web,lombokのみ選択。
    • 今回はgradleでプロジェクトを作成。

client側アプリを作成

  • build.gradleの依存関係にspring-data-geodeを追加。
build.gradle
dependencies {
    implementation('org.springframework.boot:spring-boot-starter-web'){
      // log4jのライブラリがspring-data-geodeの依存するlog4jのライブラリと競合するため、除外
      exclude group: 'org.springframework.boot', module:'spring-boot-starter-logging'
    }
    // ドメインモデルを扱うプロジェクトを依存に追加
    compile project(':geodeCommon')
    compileOnly('org.projectlombok:lombok')
    compile(group: 'org.springframework.data', name: 'spring-data-geode', version: '2.1.3.RELEASE')
}
  • 起動クラスのコード
GeodeClientApplication.java
package spring.geode.client.geodeClient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.gemfire.config.annotation.ClientCacheApplication;
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
import org.springframework.data.gemfire.config.annotation.EnablePdx;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;

import spring.geode.client.geodeClient.repository.UserRepository;
import spring.geode.geodeCommon.model.User;

@SpringBootApplication
@ClientCacheApplication(name = "SpringGeodeClientApplication") //①
@EnableGemfireRepositories(basePackageClasses = UserRepository.class) //②
@EnableEntityDefinedRegions(basePackageClasses = User.class) //③
@EnablePdx //④
public class GeodeClientApplication {

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

}

  • アノテーションの説明

    • ①: Apache GEODEにおけるclientのアプリケーションとして起動する設定
    • ②: 指定したクラスをApache GEODEのデータアクセサとして機能させる設定
    • ③: 指定したRegion(RDBで言うところのテーブル)を自動的に作成する設定
    • ④: Apache GEODEの扱うデータのシリアライズ/デシリアライズに関する設定(必須ではなさそう)
  • Controllerクラス

UserController.java
package spring.geode.client.geodeClient.controller;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
import spring.geode.client.geodeClient.service.UserService;
import spring.geode.geodeCommon.model.User;
import spring.geode.geodeCommon.model.UserRequest;

@RestController
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;
    //nameでのユーザ検索API
    @RequestMapping(path = "/find/user/{name}", method = RequestMethod.GET)
    public User findById(@PathVariable String name) {
        return userService.findByName(name);
    }
    //ユーザ全件検索API
    @RequestMapping("/findAll")
    public List<User> findAll() {
        return userService.findAll();
    }
    //新規ユーザ登録API
    @RequestMapping(path = "/register/user", method = RequestMethod.POST)
    public String register(@RequestBody UserRequest request) {
        return userService.register(request).getName();
    }
}
  • serviceクラス
UserService.java
package spring.geode.server.geodeServer.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Service;

import lombok.RequiredArgsConstructor;
import spring.geode.geodeCommon.model.User;
import spring.geode.geodeCommon.model.UserRequest;
import spring.geode.server.geodeServer.repository.UserRepository;

@RequiredArgsConstructor
@Service
public class UserService {
    private final UserRepository rep; 
    public User findByName(String name) {
        User user=rep.findByName(name).get(0);
        return user;
    }

    public User register(UserRequest request) {
        User commited = rep.save(new User(request));
        return commited;
    }

    public List<User> findAll(){
        List<User> users=new ArrayList<>();
        rep.findAll().forEach(user -> users.add(user));;
        return users;
    }
}

Repositoryクラス

UserRepository.java
package spring.geode.server.geodeServer.repository;

import java.util.List;

import org.springframework.data.gemfire.repository.GemfireRepository;

import spring.geode.geodeCommon.model.User;

public interface UserRepository extends GemfireRepository<User, Integer> {
    List<User> findByName(String name);
}
  • 設定ファイル
application.properties
spring.data.gemfire.pool.locators=localhost[40404]
server.port=9000

clientアプリケーションが接続する先のlocatorのIP,portを設定。
今回はserverアプリケーションの設定でlocatorを起動するため、localhostを指定。

Apache GEODEにおけるclient,server,locatorの関係は以下の記事に記載。
Apache GEODE の概要

ここまででclientアプリケーションは実装完了。
扱うデータオブジェクト(Userクラス)はclient,serverプロジェクトで共通のクラスを使う必要があるため、Commonプロジェクトに集約して後ほど作成する。

server側アプリを作成

起動クラス以外は、clientアプリケーションのコードをそのままserverアプリケーションのプロジェクトに持ち込めば良い。

  • 起動クラス
GeodeServerApplication.java
package spring.geode.server.geodeServer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.gemfire.config.annotation.CacheServerApplication;
import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions;
import org.springframework.data.gemfire.config.annotation.EnableLocator;
import org.springframework.data.gemfire.config.annotation.EnableManager;
import org.springframework.data.gemfire.config.annotation.EnablePdx;
import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories;

import spring.geode.geodeCommon.model.User;
import spring.geode.server.geodeServer.repository.UserRepository;

@SpringBootApplication
@CacheServerApplication(locators = "localhost[40404]") //①
@EnableGemfireRepositories(basePackageClasses = UserRepository.class)
@EnableEntityDefinedRegions(basePackageClasses = User.class)
@EnablePdx
public class GeodeServerApplication {

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

    @Configuration
    @EnableLocator(port = 40404) //②
    @EnableManager(start = true) //③
    static class LocatorManagerConfiguration {
    }

}
  • アノテーションの説明
    • ①: Apache GEODEにおけるserverのアプリケーションとして起動する設定。接続してくるlocatorはlocalhostの40404ポートであることを設定。
    • ②: locatorを40404ポートで起動する設定
    • ③: client/serverのアプリケーションの監視を行うサービスを起動する設定

ここまででserverアプリケーションは実装完了。

データモデルの作成

  • Webアプリにおけるclientからのリクエストモデル(Apache GEODEにおけるclientとは異なる)
UserRequest.java
package spring.geode.geodeCommon.model;

import java.io.Serializable;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserRequest implements Serializable{
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
}
  • Apache GEODEに永続化するドメインモデル
User.java
package spring.geode.geodeCommon.model;

import java.io.Serializable;
import java.util.UUID;

import org.springframework.data.gemfire.mapping.annotation.Region;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Region("Users") //①
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private Integer id;
    private String name;
    private int age;

    public User(UserRequest request) {
        this.name=request.getName();
        this.age=request.getAge();
        this.id=UUID.randomUUID().hashCode();
    }
}
  • アノテーションの説明

    • ①: このモデルが紐付くRegionを設定する。
  • データモデルを実装したプロジェクトはclient,serverアプリケーションのプロジェクトに依存されるため、client,serverアプリケーションのsettings.gradleに以下の内容を記載

settings.gradle
// geodeCommonは自分の作成したプロジェクト名に読み換える
include ':geodeCommon'
project(':geodeCommon').projectDir = new File('../geodeCommon')

ここまでで、データモデルの実装は完了

起動してみる

clientアプリケーションは起動時にlocatorへ接続するため、先にserverアプリケーションを起動する必要がある。

  • serverアプリケーション起動(組み込みTomacatはポート番号9090で起動)
    スクリーンショット 2019-01-27 2.01.43.png

  • clientアプリケーション起動(組み込みTomcatはポート番号9000で起動)
    スクリーンショット 2019-01-27 2.02.58.png

正常に両方のアプリケーションが起動できれば、client,locator,serverの接続はできているはずです。

ユーザをclientアプリケーションに登録してみる

curl -H "Content-Type: application/json" -X POST -d '{"name":"John","age":23}' http://localhost:9000/register/user/;

curl -H "Content-Type: application/json" -X POST -d '{"name":"Bob","age":10}' http://localhost:9000/register/user/;

serverアプリケーションからユーザを検索してみる

curl -i http://localhost:9090/findAll

検索結果で、clientアプリケーションに登録したユーザが検索できればOK

HTTP/1.1 200 
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 26 Jan 2019 17:10:37 GMT

[{"id":-1174841827,"name":"Bob","age":10},{"id":-516984913,"name":"John","age":23}]

念のため、name指定での検索も行う

curl -i http://localhost:9090/find/user/John;

HTTP/1.1 200 
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 26 Jan 2019 17:12:33 GMT

{"id":-516984913,"name":"John","age":23}

client,serverでデータが同期されていることを確認できました。
以下の公式ドキュメントを参考に実装してみました。
https://geode.apache.org/docs/

今後はpeerモデルでのアプリケーション作成や、AWSでのアプリ構成、非同期永続化など作り込んでいってみようと思います。

1
3
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
1
3