More Related Content Similar to サンプルアプリケーションで学ぶApache Cassandraを使ったJavaアプリケーションの作り方 (20) More from Yuki Morishita (11) サンプルアプリケーションで学ぶApache Cassandraを使ったJavaアプリケーションの作り方7. The Apache Cassandra database is the right choice when
you need scalability and high availability without
compromising performance.
7
11. KillrVideo
Apache Cassandra™ / DataStax Enterprise リファレンスアプリ
ケーション
– https://killrvideo.github.io
– オープンソース (APLv2)
– スケーラブルなマイクロサービスアーキテクチャ
• サービスは Node.js/C#/Java の各言語で実装
– データベース層はDSE / Apache Cassandra
• データモデリングのサンプル
• ドライバーの利用サンプル
11
21. マイクロサービスの実装
- gRPCを利用 (https://grpc.io/)
– プログラミング言語に非依存でサービスの記述
– 様々なプログラミング言語をサポート
• Java / Python / C++ / Go / Ruby …
• KillrVideoではJava / Node.js / C# でサービスを実装
- Apache Cassandraへのアクセスにドライバーの様々な機能を
利用
– ステートメントの非同期実行
– データマッパーの利用
– バッチ実行
- サービス間の連携にイベントバスを利用
- デモ用に一つのプロセスに複数のサービスを定義しているので、
GuavaのEventBusを利用
- プロセスを分ける場合はApache Kafkaなどを検討
21
23. gRPCサービスの定義
(./lib/killrvideo-service-protos/src 以下)
// Manages comments
service CommentsService {
// Add a new comment to a video
rpc CommentOnVideo(CommentOnVideoRequest) returns (CommentOnVideoResponse);
// Get comments made by a user
rpc GetUserComments(GetUserCommentsRequest) returns (GetUserCommentsResponse);
// Get comments made on a video
rpc GetVideoComments(GetVideoCommentsRequest) returns (GetVideoCommentsResponse);
}
23
24. gRPCサービスの定義
(./lib/killrvideo-service-protos/src 以下)
// Get a page of comments made by a specific user
message GetUserCommentsRequest {
killrvideo.common.Uuid user_id = 1;
int32 page_size = 2;
killrvideo.common.TimeUuid starting_comment_id = 3;
string paging_state = 16;
}
// Response when getting a page of comments made by a user
message GetUserCommentsResponse {
killrvideo.common.Uuid user_id = 1;
repeated UserComment comments = 2;
string paging_state = 3;
}
24
26. サービスの実装
- target/generated-sources 以下に自動生成されたクライアント
とサービスインターフェースのJavaコードが出力される
- 基本はStreamObserverを利用した非同期呼び出し
- クライアントは非同期実行、同期実行、Futureを利用する非同
期実行の3種類が生成される
26
// CommentServiceGrpc.java
public static interface CommentsService {
public void commentOnVideo(
killrvideo.comments.CommentsServiceOuterClass.CommentOnVideoRequest request,
io.grpc.stub.StreamObserver<killrvideo.comments.CommentsServiceOuterClass.CommentOnV
ideoResponse> responseObserver);
…
}
29. Apache Cassandraに接続する
ドライバーのセットアップ
– Apache Cassandra用
• DataStax Java driver for Apache Cassandra
• https://github.com/datastax/java-driver
• APLv2
– DataStax Enterprise用
• Java driver for DataStax Enterprise
• https://github.com/datastax/java-driver-dse
• ライセンスは独自(DSEにしか接続できない)
• DSE固有の機能を使うためにJava Driverを拡張
– 外部認証、グラフデータベースなど
29
30. Apache Cassandraに接続する
java-driver と java-driver-dse の基本的な使い方は一緒
接続するために使うクラスが違う
– Cluster (java-driver) VS DseCluster (java-driver-dse)
– Session (java-driver) VS DseSession (java-driver-dse)
30
31. Apache Cassandraに接続する
- Apache Cassandra用ドライバーのダウンロード
- Group ID: com.datastax.cassandra
- Artifact IDs:
- cassandra-driver-code
- 本体
- cassandra-driver-mapping
- オブジェクトマッパー (オプション)
- cassandra-driver-extra
- 追加の型マッピング (オプション)
31
40. コメントサービスのモデリング
CQL DDL
40
CREATE TABLE comments_by_video (
videoid uuid,
commentid timeuuid,
userid uuid,
comment text,
PRIMARY KEY (videoid, commentid)
) WITH CLUSTERING ORDER BY (commentid
DESC);
CREATE TABLE comments_by_user (
userid uuid,
commentid timeuuid,
videoid uuid,
comment text,
PRIMARY KEY (userid, commentid)
) WITH CLUSTERING ORDER BY (commentid
DESC);
Q1: SELECT commentid, videoid, comment FROM comments_by_user WHERE userid = ?
Q2: SELECT commentid, userid, comment FROM comments_by_video WHERE videoid = ?
43. バッチ登録
コメントを登録する
– コメントが登録されるとふたつのテーブルに登録しなければならない
• バッチ機能を利用してINSERT
43
PreparedStatement commentsByUserPrepared = dseSession.prepare(
"INSERT INTO killrvideo.comments_by_user (userid, commentid, comment, videoid)
VALUES (?, ?, ?, ?)"
).setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
…
BoundStatement bs1 = commentsByUserPrepared.bind(userId, commentId, comment,
videoId);
BoundStatement bs2 = commentsByVideoPrepared.bind(videoId, commentId, comment,
userId);
…
final BatchStatement batchStatement = new BatchStatement(BatchStatement.Type.LOGGED);
batchStatement.add(bs1);
batchStatement.add(bs2);
batchStatement.setDefaultTimestamp(now.getTime());
FutureUtils.buildCompletableFuture(dseSession.executeAsync(batchStatement))
…
44. クエリビルダーの利用
コメントを取得する
– ステートメントの組み立て
44
PreparedStatement getVideoComments_startingPointPrepared = dseSession.prepare(
QueryBuilder.select()
.column("video_id")
.column("comment_id")
.column("user_id")
.column("comment")
.fcall("toTimestamp", QueryBuilder.column("comment_id"))
.as("comment_timestamp")
.from("killrvideo", "comments_by_video")
.where(QueryBuilder.eq("video_id", QueryBuilder.bindMarker()))
.and(QueryBuilder.lte("comment_id", QueryBuilder.bindMarker()))
).setConsistencyLevel(ConsistencyLevel.LOCAL_QUORUM);
45. ページング
コメントを取得する
– 取得件数の指定とページング
45
final Optional<String> pagingState = Optional.ofNullable(request.getPagingState())
.filter(StringUtils::isNotBlank);
…
statement = getUserComments_startingPointPrepared.bind()
.setUUID("userid", fromString(userId.getValue()))
.setUUID("commentid", fromString(startingCommentId.getValue()));
…
statement.setFetchSize(request.getPageSize());
pagingState.ifPresent( x -> statement.setPagingState(PagingState.fromString(x)));
…
Optional.ofNullable(commentResult.getExecutionInfo().getPagingState())
.map(PagingState::toString)
.ifPresent(builder::setPagingState);
46. 実行結果の取得
コメントを取得する
– クエリの実行結果の取得
– 自動ページング(デフォルトページサイズ: 5000)
46
FutureUtils.buildCompletableFuture(dseSession.executeAsync(statement))
.handle((commentResult, ex) -> {
…
int remaining = commentResult.getAvailableWithoutFetching();
for (Row row : commentResult) {
CommentsByUser commentByUser = new CommentsByUser(
row.getUUID("userid"), row.getUUID("commentid"),
row.getUUID("videoid"), row.getString("comment")
);
commentByUser.setDateOfComment(
row.getTimestamp("comment_timestamp"));
builder.addComments(commentByUser.toUserComment());
if (--remaining == 0)
break;
}
…
47. データマッパー
エンティティクラスの準備
47
@Table(keyspace = KEYSPACE, name = "video_ratings")
public class VideoRating {
@PartitionKey
private UUID videoid;
@Column(name = "rating_counter")
private Long ratingCounter;
@Column(name = "rating_total")
private Long ratingTotal;
…
}
48. データマッパー
マッパーの準備
48
// DSECongifuration クラス
@Bean
public MappingManager getMappingManager(DseSession session) {
return new MappingManager(session);
}
// MappingConfiguration クラス
@Bean
public Mapper<VideoRating> videoRatingMapper() { return
manager.mapper(VideoRating.class); }
Editor's Notes 2009年よりApache Software Foundationにおいて開発されている分散データベース
現時点での最新バージョンはv3.11.1
KillrVideoの説明
DSEをデータベースとして使っているが、Apache Cassandraがコア デモ コメント機能デモ