【読書】詳解システム・パフォーマンス(イントロダクション)

はじめに

Oreillyからでている「詳解システム・パフォーマンス」を読んでいます。 この読書メモを記載していきます。

本について

上記から引用します。

本書はエンタープライズ環境とクラウド環境の両方を対象としたオペレーティングシステムおよびオペレーティングシステムのコンテキストにおけるアプリケーションのパフォーマンス分析と向上について解説します CPUやメモリ、ファイルシステムなど個別テーマごとに設けられた各章の前半では、用語、考え方、方法論について述べ、後半では実装の具体例を示しつつ、アーキテクチャ、分析ツール、チューニングなどを解説しています。

読書の目的

引用したところのアプリケーションのパフォーマンス分析だったり向上させようと思ったときに 何ができればそれが達成できるのか体系だった知識を得られると思ったからです。

実際本を手にすると分厚さがハンパないです!

今回の対象ページ

本が分厚いので短く記録していく感じで対象ページを書きます。 ここでは以下を対象に書いていきます。

  • イントロダクション

読書記録

全ては書かず、印象に残ったところを記載していきます。 (読むたびにかわりそう)

イントロダクション

職種

システムのパフォーマンス関連の作業を行うのは誰かということを言及していました。 * システム管理者 * サポートスタッフ * アプリケーションデベロッパー * データベース管理者 * Web管理者 * など

本の中では根本下人を見つけるためにすべてのチームの協力が必要になる場合があるということを 言われていました。 またはパフォーマンスエンジニアと言う職種を容易していて、このエンジニアチームが他のチームと共同して 全体的な視野で検討を行うということが書かれていました。

あるAPIのrequest結果を返すまでのパフォーマンスを評価するときに、APIの開発者だけではなく、 DBアクセスがあって、そのデータベース管理をしているチームがいたらそのチームと連携して パフォーマンスを測定したり、たしかにそういったことはあるなと。

パフォーマンスは主観である

バグについては簡単に判断できるが(エラーメッセージがでているなど形がある) パフォーマンスについては主観的な問題になることが多い。

そもそも問題なのかはっきりしないし、問題だったとしても、それが修正されたのかどうかも またはっきりしないということが書いてあった。さらにあるユーザがパフォーマンスが悪いと思っていて 別のユーザはパフォーマンスが良いと思うこともあると書いてあった。 こういったことは良く現実でもあって、「この部分が遅いからちょっと調べよう」となったときにそれを どこまで修正すれば良いのかという部分が定義しずらいことがありました。

システムは複雑である

パフォーマンスを分析し始めるために、明確なポイントがないことが多い。 そういったときに「ネットワークが悪い」という決めつけなど、推測から始まることがあり、 その出発点が正しい方向かどうかから明らかにしなければならないという記述があった。

まとめ

システムのパフォーマンスとして見るべき箇所についての概要が「イントロダクション」に記載されていました。 より詳細なことが次の章から続くようなのでこれも大変楽しみです!

IntelijIDEAのKotlinプラグイン・Kotlin Fill Class

はじめに

intelijIDEAのpluginについてです。 ここではKotlinで開発する際に便利なpluginのKotlin Fill Classについて書きます。

環境情報

使っている環境は以下の通りです。(M1macが気になる。。。)

  • mac book pro
  • intelijIDEA community

設定方法

pluginの設定で以下のようにするとでてくるのでこれをinstall

f:id:set21set21:20201201130952p:plain

使い方のイメージ

なんらかのオブジェクトを生成する際に便利なpluginです。 個人的にはunit testを書くときに、初期値をコンストラクタで生成するようなケースで よく使っています。

これがないとコンストラクタ引数をすべて書かないといけないのですが、 pluginを使うと、その手間がかなり減ります。

使い方を簡単に

作成するオブジェクト

data class FillClassTest(
    val param1: String,
    val param2: String,
    val param3: String
)

上記のクラスをpluginを使って作ります。

class名を書く。そして以下のようなメニューが出るので選択する。 f:id:set21set21:20201228212505p:plain

以下のように初期値を設定したコンストラクタが生成される。 f:id:set21set21:20201228212616p:plain

後は値を設定していけばよい。 どんなパラメータがあるか、調べなくて良いので非常に便利だと思います!

まとめ

  • fill class超便利
  • classを多く作成するときに良い

参考

fill classの作者様のページです。ありがとうございます! suzan2go.hatenablog.com

SpringBoot × KotlinでGraphQLに入門する

はじめに

GraphQLを理解するために、SpringBoot×Kotlinでためしてみることにした。

確認環境

確認する環境は以下で実施する。

開発環境

mac book pro

エディタ

intelij IDEA CM

ソフトウェア

  • Kotlin
  • SpringBoot(2.3.3)
  • Gradle
  • GraphQL Playground

環境構築

まずは環境構築。 雛形はspring initializrを使って作成する。 ここの手順は割愛する。

intelij IDEAのplugin

GraphQLのSchema定義を実装するのに便利なためJS GraphQLを導入する。 導入することでハイライトが効くようになる。

GraphQLの動作確認ツール

HomebrewでGraphQL Playgroundを導入する。

brew cask install graphql-playground

依存性の設定

まずはGraphQLのライブラリを使って実装をするため Gradleで設定を行う。 使うライブラリは以下の通り * GraphQL Java * GraphQL Spring Boot Starter * GraphQL Java Tools

implementation("com.graphql-java-kickstart:graphql-spring-boot-starter:5.7.3")
implementation("com.graphql-java-kickstart:altair-spring-boot-starter:5.7.3")
implementation("com.graphql-java-kickstart:graphiql-spring-boot-starter:5.7.3")
implementation("com.graphql-java-kickstart:voyager-spring-boot-starter:5.7.3")

GraphQLとは

以下を参照した。 https://ja.wikipedia.org/wiki/GraphQL

概要

概要は以下の感じだった。wikipediaから引用する。

GraphQLでは、クライアントが必要なデータの構造を定義することができ、サーバーからは定義したのと同じ構造のデータが返される。したがって、必要以上に大きなデータが返されるのを防ぐことができクエリの効率が良い。また、柔軟性と豊かな表現が可能なクエリ言語は複雑さを加えるため、シンプルなAPIには適さない可能性もある GraphQLは、型システム、クエリ言語、実行セマンティクス、静的な検証、型イントロスペクション(英語版)から構成される。 GraphQLはデータの読み込み/query、書き込み/mutation、購読/subscriptionをサポートする

もうちょっと詳しく

これも同じくwikipediaから引用

GraphQLでは、クライアントがクエリ内容を記したdocumentを送信し、GraphQLサービスがクエリを実行し結果を返信する。documentはDSLであるGraphQL query languageを用いて記述される。以下の例は明日の天気を取得するクエリのdocumentとGraphQLサービスから返されたクエリ結果のJSONである。

// document
{
  tomorrow {
    weather
    rainyPercent
  }
}
// response JSON
{
  "tomorrow": {
    "weather": "cloudy",
    "rainyPercent": 30
  }
}

さらに以下の記述があったこれでなんとなく、イメージがつかめた感がある。

リソースをURLパスで表現するRESTful APIと異なり、GraphQLはリソースをdocumentで表現する。GraphQL WebAPIの場合、単一のAPIエンドポイントへこのdocumentをPOSTする(例:https://API.internal./graphqlエンドポイントへdocumentをBodyとしてPOSTする)ことでクエリが実行される

掴んだイメージ

  • REST APIと同じく何らかのAPIである
  • クライアント側はPOSTでリクエストをする
  • そのリクエストの中にdocumentという要素があり、その中に欲しい情報を含める
  • API側はそのdocument情報にあった情報を返却するように処理を行う

実装

早速実装して動作を確認していく。

スキーマ定義を実装する

scheme.graphqlsを作成する このファイルはresource配下に配置しないとSpringBootの起動ができなかった。 (resource配下にある、*.graphqlsファイルの存在をチェックしているようだった。)

defaultが上記のようになっているようなので、これを設定する方法はあるかもしれない。 これを記載している時点では上記を確認していない。

この時点で定義したscheme.graphqlsは以下の通り。

type Book {
    id: String
    name: String
}
type Query {
    bookById(id: String): Book
}

Typeクラスを実装する

scheme.graphqlsで定義したBookクラスを実装する 今回は以下のように定義した。

package com.example.graphqlsample.graphql

data class Book(
        private val id: String,
        private val name: String
)

resolverを実装する

実際のデータ操作を行う処理がこのresolverとなる。 ここではBookResolverとする。

package com.example.graphqlsample.graphql

import com.coxautodev.graphql.tools.GraphQLQueryResolver
import org.springframework.stereotype.Component

@Component
class BookResolver: GraphQLQueryResolver {
    fun bookById(id: String): Book {
        return Book(
                id = id,
                name = "test"
        )
    }
}

ポイントは以下かなと。

  • @Componentをつける

  • GraphQLQueryResolverを継承する形で実装を行う

  • scheme.graphqlsのQueryに書いた定義をメソッドとして定義する。(ここではbookByIdメソッド)

動作確認

graphql-playgroundで動作確認を行う。 エンドポイントは以下を指定する。

http://localhost:8080/graphql

ここで指定するqueryを定義して実行する これがdocumentの送信にあたる。

query {
  bookById(id:"2") {
    id
    name
  }
}

結果

{
  "data": {
    "bookById": {
      "id": "2",
      "name": "test"
    }
  }
}

まとめ

SpringBoot × KotlinでGraphQLの実装をしてみた。 Controllerの実装をすることなく、APIが実装できた。 次はクライアント側の要求するものから柔軟にresponseを返却する部分について 実装をしていきたい。

参考

APIのバージョニングについて考える

はじめに

WebAPIを作る際にAPIのバージョンニングをエンドポイントに含めることがよくある。 これについてなぜやるのかをまとめてみる。

バージョニングとは

いくつかの種類があるようだが、イメージとしては以下のようなエンドポイントにv1などの バージョン情報が付与されているものを指す。

https://example.com/v1/foo/
https://example.com/v2/bar/

バージョニングの種類

いろいろ流派があるようだ。

URIにバージョンを埋める

上記にも書いたが以下のようなものを指す。

https://example.com/v1/foo/

個人的にはこれを使って実装している。

host名にAPIバージョンがある

以下のようなものを指す。

https://api-v1.example.com/foo/
https://api-v2.example.com/foo/

上記のようにサブドメインにバージョンを含めるパターン。 これは実際にやったことはないが、DNSを都度設定するのが大変?

headerに入れる

以下のようなものを指す。

https://example.com/foo/

http headerに以下のように
version:v1

これも実際にやったことがない。 エンドポイントにバージョンがないのでスマートのように見える。 疎通確認でheaderに設定を入れるのを忘れない意識が必要か。

どの方法を選ぶか?

自分の経験としては「URIにバージョンを埋める」の方法のみしか実践したことがない。

バージョンにvを含めるか

バージョニングする際にvを含めるかどうかを判断する必要があるかもしれない。 ここでもまずは個人的な経験範囲でいうと、vを含めて開発している。 理由を考えたことがなかったが、「Web API: The Good Parts」にわかりやすく意見が書いてあった。 P143から引用する。

筆者はvをつけるほうが好みです。なぜならそれがバージョンである、ということがはっきりわかるからです。

バージョニングのメリットとデメリット

メリット

APIを呼び出すクライアントの変更が容易にできない場合にバージョニングしておくことで 互換性を保つことができる。

例えば呼び出しているのがスマホアプリだった場合はアプリのアップデートが必要になる。 その場合に呼び出すAPIの改修をしてしまうと、アップデートをしないユーザのアプリが予期していない動きになる。

このようなケースの場合にバージョニングしていることでアップデート前のアプリの前は前のバージョンのAPIを参照、 アップデートしたユーザのアプリはアップデート後のAPIを参照することで後方互換を保つことができる。

デメリット

デメリットはバージョンごとに管理する必要がでてくること。 これは内部のロジックでこのバージョンの場合はこの処理みたいな分岐がでてきて 処理が複雑になる可能性がある。

まとめ

APIの開発をする際に何気なくバージョニングをしていたが、APIのクライアントの事情を考慮する必要がある場合に 有効な手段だと思う。 その際にAPI側でバージョンごとにロジックが複雑にならないようにするテクニックが必要だと思う。

参考資料

書籍

www.amazon.co.jp

WEB

バージョニング  |  Cloud API  |  Google Cloud

medium.com

ほしい

持っていないが気になっている本。いつか書く。 Web APIの設計 (Programmer's SELECTION)

www.amazon.co.jp

Spring×KotlinでOpenFeignを使う

はじめに

SpringBoot×KotlinでWebAPIを作る場合を想定して本記事を書きます。 WebAPIの中で別のAPIにアクセスするケースがあると思いますが その際のAPI clientとして、OpenFeignを使った実装をしてみたいと 思います。

環境構築

まずは実装のための環境構築をします。

前提

ここではintelijIDEAのコミュニティ版を使って行います。

Spring initializaで雛形作成

まずはプロジェクトの雛形を作成します。 以下にアクセスし、それぞれを入力するとプロジェクトの雛形が作成できます。 https://start.spring.io/

ここでは以下の感じで作成 f:id:set21set21:20200819235757p:plain

intelijIDEAで取り込み

よしなに作成した雛形を取り込みます。

実装

Controllerから

Controllerは以下のように実装。 ここではControllerから直接FeignClientを呼ぶことにします。

import com.example.demo.client.HelloClient
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class HelloController(
        private val client: HelloClient
) {
    @GetMapping("/hello")
    fun fetch(): String {
        return client.fetch()
    }
}

また今回はFeignのクライアントから呼び出すAPIを自前で用意することにし、 以下のようなControllerを作成します。

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController

@RestController
class DummyController {
    @GetMapping("/dummy")
    fun dummy(): String {
        return "dummy"
    }
}

クライアント作成

今度はAPIを呼び出すクライアントを作成します。 OpenFeignを使うと、interfaceを定義するだけで呼び出すことができるので お手軽で良いですね。

import org.springframework.cloud.openfeign.FeignClient
import org.springframework.web.bind.annotation.GetMapping

@FeignClient(name = "HelloClient", url = "http://localhost:8080")
interface HelloClient {
    @GetMapping(value = ["dummy"])
    fun fetch(): String
}

動作確認

ローカル環境で動作確認をします。 ローカルでサーバを起動して以下のエンドポイントにアクセスします。

確認コマンド

curl http://localhost:8080/hello

結果

dummy

まとめ

OpenFeignを使って外部のAPIにアクセスするWebAPIの実装をしてみました。 interfaceを作成するだけで簡単に外部APIにアクセスする機能が実現できました。 ここではTimeoutなどの設定をしていませんが、そのような設定周りも実装してみたいと思います。

今回実装したものは以下にあります。 github.com

Kotlin Coroutineに入門してみた

はじめに

KotlinのCoroutineを使ってみたく、入門してみました。

やること

KotlinのCoroutineを使うための設定を行い、実際に動かすまでをします。

Coroutineとは

Kotlinの非同期プログラミングをする仕組みのことです。 今回は主にKotlinの公式サイトを参考に実施します。

https://kotlinlang.org/docs/reference/coroutines/coroutines-guide.html

設定

まずは依存関係の設定をします。 ここではGradleを使って設定します。

Gradle設定例

以下のように設定します。(参考

dependencies {
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7'
}
repository {
    jcenter()
}

コードを書く

まずはじめに以下のようなコードを書いてみました。

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch

fun main(args: Array<String>) {

    GlobalScope.launch {
        println("test1")
    }
    println("test2")
}

このように書くとtest1と出力された後、test2と出力される? というイメージをしましたが実際の出力は以下のようになりました。

test2

test1の出力は??? ここでGlobalScope.launchとはなにかを確認しました。

GlobalScope.launchとは?

バックグラウンドで新しいCoroutineを起動して実行という処理のよう。 公式サイトで説明をみたところ以下のような記述がありました。 結果、メイン処理が終了しているためtest1が出力されなかったということかと。

以下の実験で試します。メイン処理が終了しないように遅延を仕込んでみました。

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main(args: Array<String>) {

    GlobalScope.launch {
        println("test1")
    }
    println("test2")

    runBlocking {
        delay(2000L)
    }

}

結果は以下のようになりました。

test1
test2

Coroutineを待つ

先程はdelayさせることでCoroutineの処理を待つことができましたが 待ち合わせする方法が用意されています。

コード例

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main(args: Array<String>) {

    runBlocking {

        val job = GlobalScope.launch {
            println("test1")
        }
        println("test2")

        // ここでjobの待ち合わせをする
        job.join()

    }

}

実行結果

test2
test1

動き方のメモ

runBlockingを使うと現在のthreadをブロックする動きとなるよう。 上記の例でrunblockingがないと処理が先に終了してtest1が出力されないという 動きになりました。

他の方法

asyncを使う方法があります。

asyncとは

こちらもCoroutineを作成する処理のようでDeferredを返す処理です。 DeferredはCoroutineの処理が終わった後にその結果が格納されているという動きのようです。

async実装

実装は以下です。

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async

fun main(args: Array<String>) {

    println("before")
    GlobalScope.async {
        println("test")

    }
    println("after")

}

出力結果は以下になります。

before
after
test

上記の実装の場合、何回か実行をした場合に、testが出力される前にプログラムが終了する場合もありました。 よってここでもCoroutineの結果を待つ方法を使うことですべての実行ができるようになります。

awaitを使う

awaitを使うことでCoroutineの結果待ちができます。

awaitを使う実装例
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking

fun main(args: Array<String>) {

    runBlocking {

        println("before")
        GlobalScope.async {
            println("test")
        }.await()
        println("after")

    }

}

出力結果は以下です

before
test
after

まとめ

ここまででCoroutineの機能について触ってみました。 今回はSuspendingについては割愛したので次回こちらに触ってみたいと思います。

Java11の勉強

はじめに

Java8環境での開発をしばらくやっていてすっかりJavaのアップデートについていけていないので Java8環境からJava11環境での差分を勉強してみます。

検証環境

下環境で実施しています。

端末

Java version

java -version
java version "11.0.7" 2020-04-14 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.7+8-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.7+8-LTS, mixed mode)

本記事で検証すること

主にJava8からJava11の間に追加されたものについて触ってみるという目的で実施しました。 今回は言語についての変更点についてとし、以下について触ってみたことについて書いていきます。

  • var
  • try-with-resourceの変更
  • 匿名クラスのダイアモンド演算子を省略できる
  • 変数の_が使えない

var

ローカル変数に型推論が使えるようになりました。 具体的には以下のように書けるようになっています。

Java11前

String beforeDeclaration = "宣言時に型を指定";

Java11

var afterDeclaration = "varで宣言できる";

宣言はできるが、method引数にはできない模様。 試しに以下のように実装しましたがコンパイルエラーになりました。

private static void sampleArgVar(var a) {
        
    }

varまとめ

宣言時はvarでできるようになったので簡潔に書けるようになりました。 一方でソースを読むときに何の型がわからないケースがでてきそうなので 乱用すると保守が難しくなりそう。。。

以下の記事に指標についてまとめられているので参考になります。

qiita.com

try-with-resourceの変更

リソースのclose方法の進化を見ていきます。 ここでは、Java1.7より前の方法から。

java1.7より前

        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream(path)));
            System.out.println(br.readLine());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            br.close();
        }

java1.7

        BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream(path)));
        try (BufferedReader brTmp = br) {
            System.out.println(brTmp.readLine());
        }

java1.9で変更

Java1.9の変更点は一時変数に入れた場合に、別変数に入れる必要があったところが その必要がなくなった部分になります。

        BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream(path)));
        try (br) {
            System.out.println(br.readLine());
        }

まとめ

リソースのclose方法はかなり簡潔に書けるようになっている。

匿名クラスのダイアモンド演算子を省略できる

こちらもJava1.9から。 匿名クラスにダイアモンド演算子での定義ができるようになったようだ。

        List<String> anonymousDeclarationTests = new ArrayList<>() {{
            this.add("aaa");
            this.add("bbb");
        }};

まとめ

変更点について見ていきました。 varについてはなんとなく知っていたのですが、他の変更点も確認でき良かったかと思います。 次はJavaの標準ライブラリについての変更点について見ていければと思います。