AuroraはI/OをシャーディングするためのJava/Scala向けライブラリです。
- Scala, Javaで利用可能
- Java版は
aurora-core
、Scala版はaurora-scala
を提供 - 他のJVM言語から利用できるはずです
- Java版は
- ストレージデバイスフリー
- コネクションやSQLなどのストレージデバイス固有の知識に依存しない
- 標準ではJDBC, Redis, Memcacheなどに対応
Copyright (c) 2014 GREE, Inc.
MIT License
- Java (SE 7以降)
- Scala 2.10.x, 2.11.x
- データソース・シャーディング機能
事前に定義したデータソース群とリゾルバによって、ヒントに応じたデータソースを解決できます(データソースからコネクションの変換は別途ロジックが必要。詳細は後述)。 - テーブル名・シャーディング機能
事前に定義したリゾルバによって、ヒントに応じたテーブル名を解決できます。
typesafe-activator
とsbt
をインストールする。
$ brew install typesafe-activator sbt
typesafe-activator
でプロジェクトのひな形を作成する。minimal-scala
を選択する。
$ activator new
Fetching the latest list of templates...
Browse the list of templates: http://typesafe.com/activator/templates
Choose from these featured templates or enter a template name:
1) minimal-java
2) minimal-scala
3) play-java
4) play-scala
(hit tab to see a list of all templates)
> 2
Enter a name for your application (just press enter for 'minimal-scala')
> example
OK, application "example" is being created using the "minimal-scala" template.
To run "example" from the command-line, run:
/Users/junichi.kato/temp/example/activator run
To run the test for "example" from the command-line, run:
/Users/junichi.kato/temp/example/activator test
To run the Activator UI for "example" from the command-line, run:
/Users/junichi.kato/temp/example/activator ui
build.sbt
にaurora-scala
ライブラリへの依存関係を追加する。
name := """example"""
version := "1.0"
scalaVersion := "2.11.1"
resolvers ++= Seq(
"Sonatype OSS Release Repository" at "https://oss.sonatype.org/content/repositories/releases/",
"Sonatype OSS Snapshots Repository" at "https://oss.sonatype.org/content/repositories/snapshots"
)
libraryDependencies += "net.gree.aurora" %% "aurora-scala" % "x.x.x"
- 以下の手順でコンパイルできることを確認する。
$ cd example
example $ sbt clean compile
- Mavenをインストールする
$ brew install maven
- Maven Archetypeでプロジェクトのひな形を生成する。
$ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart
[INFO] Scanning for projects...
[INFO]
[INFO] Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
<snip>
Define value for property 'groupId': : example
Define value for property 'artifactId': : example
Define value for property 'version': 1.0-SNAPSHOT: :
Define value for property 'package': example: :
Confirm properties configuration:
groupId: example
artifactId: example
version: 1.0-SNAPSHOT
package: example
Y: : Y
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: example
[INFO] Parameter: packageName, Value: example
[INFO] Parameter: package, Value: example
[INFO] Parameter: artifactId, Value: example
[INFO] Parameter: basedir, Value: /Users/user/
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /Users/user
[INFO] ------------------------------------------------------------------------
pom.xml
にaurora-core
ライブラリへの依存関係を追加する。
<dependencies>
<dependency>
<groupId>net.gree.aurora</groupId>
<artifactId>aurora-core</artifactId>
<version>x.x.x</version>
</dependency>
</dependencies>
- 以下の手順でコンパイルできることを確認する。
$ cd example
example $ mvn clean compile
まず最初に設定ファイル(conf/application.conf
)を用意します。Auroraはtypesafe config
を利用しています。
例えばJDBCの場合は、以下のように記述します。
aurora {
sharding-configs {
database {
type=jdbc
default {
driver-class-name="org.mysql.Driver"
prefix-url="jdbc:mysql://"
user-name=user1
password=user1pass
read-only-user-name=user2
read-only-password=user2pass
}
cluster-groups=[
{
main {
clusters=[
{
cluster1 {
master="192.168.1.2"
slaves=[
"192.168.1.3",
"192.168.1.4"
]
standby="192.168.1.254"
database="test_a"
}
},
{
cluster2 {
master="192.168.2.2"
slaves=[
"192.168.2.3",
"192.168.2.4"
]
standby="192.168.2.254"
database="test_b"
}
}
]
}
},
{
sub {
clusters=[
{
cluster1 {
master="192.168.3.2"
slaves=[
"192.168.3.3",
"192.168.3.4"
]
standby="192.168.3.254"
database="test_a"
}
},
{
cluster2 {
master="192.168.4.2"
slaves=[
"192.168.4.3",
"192.168.4.4"
]
standby="192.168.4.254"
database="test_b"
}
}
]
}
}
]
}
}
}
aurora/sharding-configs/database
の部分は、sharding-config-id
と呼び、開発者が自由に命名できます。
JDBCの場合はtype=jdbc
としてください。type
は必須項目です(他にもgenric
, redis
などが使えます。後述)
default
には、各データソースで共通な設定項目(ドライバクラス名やユーザ名など)を記述します。driver-class-name
, prefix-url
, user-name
, password
, read-only-user-name
, read-only-password
は必須項目です。
項目名 | 意味 | 補足 |
driver-class-name | JDBCドライバクラス名 | |
prefix-url | JDBC接続URLのプレフィックス | |
user-name | JDBC接続ユーザ名 | マスターデータソースに利用します |
password | JDBC接続ユーザのパスワード | |
read-only-user-name | JDBC接続ユーザ名(読込み専用) | スレーブデータソースに利用します |
read-only-password | JDBC接続ユーザ(読込み専用)のパスワード |
続く、cluster-groups
(クラスターグループ)には、後述するcluster
(クラスター)を複数個 登録します(1個でもよい)。クラスターグループは機能ごとに分割したい時に便利です。
aurora/sharding-configs/{sharding-config-id}/cluster-groups/(main|sub)
の部分は、cluster-group-id
と呼び、開発者が自由に命名できます。この二つのIDはデータソースを選択する際に必要になります。
aurora/sharding-config/{sharding-config-id}/cluster-groups/{cluster-groupo-id}/clusters/(cluster1|cluster2)
の部分は、cluster-id
と呼び、開発者が自由に命名できます。
続く、cluster
(クラスター)には、マスター用途(master
)とスレーブ用途(slave
)のデータソースを定義できます。1クラスターにマスターは1台とし、スレーブは複数台を登録します。database
にはデータベース名を指定します。(standby
はコメントです)
ヒントを受け取ってクラスタ名を返すリゾルバ(clusterIdResolver
)を実装し、AuroraShardingServiceを初期化します(auroraShardingService
)。
AuroraShardingService#resolveClusterByHint
メソッドを使って、ヒントに応じたクラスターを取得します。
クラスターにはデータソースが含まれているので、それを使って目的に応じてI/Oを行います(データソースからコネクションの変換はアプリケーション開発者が行う必要があります)
package net.gree.aurora;
import net.gree.aurora.application.AuroraShardingService;
import net.gree.aurora.application.AuroraShardingServiceFactory;
import net.gree.aurora.domain.cluster.AbstractClusterIdResolver;
import net.gree.aurora.domain.cluster.Cluster;
import net.gree.aurora.domain.cluster.ClusterIdResolver;
import net.gree.aurora.domain.datasource.DataSourceRepository;
import net.gree.aurora.domain.datasource.DataSourceSelector;
import net.gree.aurora.domain.datasource.JDBCDataSource;
import net.gree.aurora.domain.hint.Hint;
import net.gree.aurora.domain.hint.HintFactory;
import java.io.File;
import java.io.IOException;
import java.sql.*;
public class Main {
private static class User {
private final int id;
private final String name;
User(int id, String name){
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
// リゾルバを定義する
private final ClusterIdResolver<Integer> clusterIdResolver = new AbstractClusterIdResolver<Integer>("cluster") {
@Override
protected String getSuffixName(Hint<Integer> userIdHint, int clusterSize) {
return Integer.toString(((userIdHint.getValue() +1) % clusterSize) + 1);
}
};
// AuroraShardingServiceを初期化する
private final AuroraShardingService<Integer> auroraShardingService = AuroraShardingServiceFactory.create(clusterIdResolver, new File("./conf/example.conf"));
private final DataSourceRepository dataSourceRepository = auroraShardingService.getDataSourceRepository().get();
private User findByUserId(int userId) throws SQLException, ClassNotFoundException, IOException {
// sharding-config-idとcluster-group-idとヒントを引数に与えてclusterを解決する
Cluster cluster = auroraShardingService.resolveClusterByHint("database", "main", HintFactory.create(userId)).get();
// スレーブはランダム選択
DataSourceSelector<Integer> selector = cluster.createDataSourceSelectorAsRandom(dataSourceRepository);
// スレーブデータソースを選択する
JDBCDataSource dataSource = selector.selectDataSourceAsJDBC(null).get();
// 書き込み用途でマスターを利用する場合は、以下。
// JDBCDataSource dataSource = cluster.masterDataSource(dataSourceRepository).get();
System.out.println(dataSource);
// データソースからコネクションを取得する
Connection connection = getConnection(dataSource);
// コネクションを使ってSQLを発行する
Statement st = connection.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM user WHERE id = " + userId);
if (rs.next()) {
return convertToModel(rs);
}else {
throw new IOException("entity is not found.");
}
}
// ここでは都度コネクションを取得していますが、
// 必要に応じてコネクションプールからコネクションを取得してもよいでしょう
private Connection getConnection(JDBCDataSource dataSource) throws ClassNotFoundException, SQLException {
Class.forName(dataSource.getDriverClassName());
return DriverManager.getConnection(dataSource.getUrl(), dataSource.getUserName(), dataSource.getPassword());
}
private User convertToModel(ResultSet resultSet) throws SQLException {
return new User(resultSet.getInt("id"), resultSet.getString("name"));
}
public static void main(String[] args) throws SQLException, ClassNotFoundException, IOException {
User user = new Main().findByUserId(1);
System.out.println(user);
}
}
package net.gree.aurora.scala
import java.io.{IOException, File}
import java.sql.{Connection, DriverManager, ResultSet}
import net.gree.aurora.scala.application.AuroraShardingService
import net.gree.aurora.scala.domain.cluster.AbstractClusterIdResolver
import net.gree.aurora.scala.domain.datasource.JDBCDataSource
import net.gree.aurora.scala.domain.hint.Hint
import org.sisioh.dddbase.core.lifecycle.sync.SyncEntityIOContext
import scala.util.Try
object Main extends App {
case class User(id: Int, name: String)
// リゾルバを定義する
private val clusterIdResolver = new AbstractClusterIdResolver[Int]("cluster") {
override protected def getSuffixName(userIdHint: Hint[Int], clusterSize: Int): String = {
(((userIdHint.value + 1) % clusterSize) + 1).toString
}
}
// AuroraShardingServiceを初期化する
private val auroraShardingService = AuroraShardingService(clusterIdResolver, new File("./conf/example.conf"))
private implicit val dataSourceRepository = auroraShardingService.dataSourceRepository.get
private implicit val ctx = SyncEntityIOContext
private def findByUserId(userId: Int): Try[User] = {
// sharding-config-idとcluster-group-idとヒントを引数に与えてclusterを解決する
auroraShardingService.resolveClusterByHint("database", "main", Hint(userId)).map { cluster =>
// スレーブはランダム選択
val selector = cluster.createDataSourceSelectorAsRandom
// スレーブデータソースを選択する
val dataSource = selector.selectDataSourceAsJDBC().get
// 書き込み用途でマスターを利用する場合は、以下。
// val dataSource = cluster.masterDataSource
println(dataSource)
// データソースからコネクションを取得する
val connection = getConnection(dataSource)
// コネクションを使ってSQLを発行する
val st = connection.createStatement
val rs = st.executeQuery("SELECT * FROM user WHERE id = " + userId)
if (rs.next())
convertToModel(rs)
else
throw new IOException("entity is not found.")
}
}
// ここでは都度コネクションを取得していますが、
// 必要に応じてコネクションプールからコネクションを取得してもよいでしょう
private def getConnection(dataSource: JDBCDataSource): Connection = {
val url = dataSource.url
val userName = dataSource.userName
val password = dataSource.password
Class.forName(dataSource.driverClassName)
DriverManager.getConnection(url, userName, password)
}
private def convertToModel(resultSet: ResultSet): User =
User(
id = resultSet.getInt("id"),
name = resultSet.getString("name")
)
val user = findByUserId(1).get
println(user)
}
上記のサンプルコードは、以下の手順で実行することができます(MySQLサーバをlocalhostで稼働させている前提)。
$ git clone [email protected]:gree/aurora.git
$ cd aurora/tool
$ sh setup_testdb.sh
$ cd ..
$ sbt "project aurora-scala-example" run
- 対応しているデータソースタイプ
- 汎用タイプ(generic)
- JDBC(jdbc)
- Redis(redis)
TBD
- Java SE 7
こちらからダウンロードする。
- Scala 2.10.x, 2.11.x
sbtが自動的にダウンロードするのでインストールは不要
- sbt
以下の手順でインストール。
$ brew install sbt
$ git clone [email protected]:gree/aurora.git
$ cd aurora
aurora $ git config -l | grep user # 意図どおりか確認する
aurora $ sbt clean +package
target以下のパスにjarファイルが生成されます。
以下の手順を行い、IDEA上でOpen Projectを行う。
$ echo 'addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0")' > ~/.sbt/0.13/plugins/build.sbt
aurora $ sbt clean gen-idea
aurora $ sbt clean publish-local
- Sonatype JIRA のアカウントを持っていること。なければここからサインアップしてアカウントを取得する。
- Sonatype リポジトリへのデプロイ権限を持っていること。欲しい方は、Issueから申請してください。
- 以下の内容で
~/.ivy2/.credentials
を作成する。
realm=Sonatype Nexus Repository Manager
host=oss.sonatype.org
user=JIRAアカウント名
password=パスワード
- gpgで鍵を作る
鍵を生成する
$ gpg --gen-key
生成された鍵を確認する
$ gpg --list-keys
/home/user1/.gnupg/pubring.gpg
--------------------------------
pub 2038R/7544FE41 2014-06-15
uid XXXXX YYYYY <[email protected]>
sub 2038R/FD94AE63 2014-06-15
鍵をサーバに送信する
$ gpg --send-keys 7544FE41
aurora $ sbt clean +publish
# リリースブランチを作成する
aurora $ git flow release start v0.0.4
# commonSettingsのversionをあげる
aurora $ vi project/Build.scala
aurora $ sbt clean test
aurora $ git flow release finish v0.0.4
aurora $ git push origin master
aurora $ git push origin develop
aurora $ git push origin refs/tags/v0.0.4
# マスターブランチからデプロイを行う
aurora $ sbt clean
aurora $ sbt +publishSigned
aurora $ sbt +sonatypeRelease
Pull Requestをお待ちしております。