Spring Boot 2.4.0 でMultiple Propertyファイルを適用させる
Spring Boot 2.4になって、application.ymlの書き方がちょっと変わったのでメモ。色々便利になったようなのですが、k8sとか使っていないのであんまり恩恵がない 😑
基本設定をapplication-base.ymlに記述し、環境ごとの差異をそれぞれのapplication-xxx.ymlに記述します。
application.yml
# default spring: config: import: - classpath:application-base.yml --- spring: config: activate: on-profile: development import: - classpath:application-base.yml
application-base.yml
すべての環境で共通の設定を記述する
server: port: 8081 shutdown: graceful
application-development.yml
some.property.label: development
application-default.yml
local起動の場合 (特にprofileを指定しない場合) defaultになります。
some.property.label: default
configuration
テスト用に適当なConfigurationを用意
@Configuration(proxyBeanMethods = false) @EnableConfigurationProperties(SomeProperty::class) class SomeConfiguration( private val prop: SomeProperty ) { fun prop() = prop.label } @ConfigurationProperties("some.property") @ConstructorBinding class SomeProperty( val label: String = "" )
@RestController class ConfigTestController( val config: SomeConfiguration ) { @GetMapping("test") fun test(): Mono<String> = mono { config.prop() } }
コンテナにします
$ ./gradlew bootBuildImage
defaultで起動
$ docker run -m "1024M" -it -p 8081:8081 --rm demo:0.0.1-SNAPSHOT $ curl http://localhost:8081/test -> default
developmentで起動
$ docker run -m "1024M" -it -p 8081:8081 -e "SPRING_PROFILES_ACTIVE=development" --rm demo:0.0.1-SNAPSHOT -> The following profiles are active: development $ curl http://localhost:8081/test -> development
上書きしたい場合
以前と同様に、後からimportしたproperty fileが一番強くて、設定をoverrideします。
application-override.yml
server: port: 8080
application.yml
--- spring: config: activate: on-profile: development import: - classpath:application-base.yml - classpath:application-override.yml
$ docker run -m "1024M" -it -p 8080:8080 -e "SPRING_PROFILES_ACTIVE=development" --rm demo:0.0.1-SNAPSHOT -> Netty started on port(s): 8080
前より書き方が面倒になった気もするが、これであっているのだろうか 😑
profile.groupsを使うよりはこっちのほうがまだ楽だと思うけど、情報待ちデス
参考
goroutineを使ってdatabaseに並列でInsertするサンプル
databaseにgoroutineで並行にinsertするサンプルプログラムです。仕事で必要になって実験しました。
database 接続
sql.Open() を使ってDB Instanceを取得します。DB Instanceは並行安全で、このInstanceがConnection Poolを管理するので Singletonにしてアプリケーション全体で共通利用することにします。
// database.go // かんたんsingleton var singletonDB *sql.DB var lock sync.Mutex func Connect() *sql.DB { lock.Lock() defer lock.Unlock() if singletonDB != nil { return singletonDB } // サンプルなのでerr無視 singletonDB, _ = sql.Open( "postgres", fmt.Sprintf( "host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", os.Getenv("host"), os.Getenv("port"), os.Getenv("user"), os.Getenv("password"), os.Getenv("dbname"), ), ) // connection poolingの設定をする singletonDB.SetMaxOpenConns(10) singletonDB.SetConnMaxIdleTime(10 * time.Second) singletonDB.SetConnMaxLifetime(10 * time.Second) return singletonDB }
goroutine fan-out
Sample tableを適当に用意してInsertしていきます。PrepareするとDB InstanceはConnectionを発行して、Stmt Instanceを返します。
Stmtも並行安全なので、goroutineで共用します。自分のLocal環境のテストでは結果は変わらなかったのですが、
きちんとした環境ではgoroutineごとにStmtを発行 (= goroutineごとにConnectionをもつ) ほうが早いかもしれません。ただ、Transaction管理とかしだすと辛いと思います。
あと、公式の説明によるとgoではDB InstanceをCloseするということは基本行わないらしいのでStmtのCloseしかしていません。StmtをCloseするとConnectionはIdleになります。
func () Insert(entityCh <-chan entity.SampleEntity, gophers int) <-chan error { errCh := make(chan error) // singleton DB instanceを取得する db := Connect() // stmtは並行安全なのでgoroutineで共用する stmt, err := db.Prepare("insert into sample (id, label) values ($1, $2)") if err != nil { log.Fatal(err) } var wg sync.WaitGroup wg.Add(gophers) // fan-out. goroutineを複数生成する for i := 0; i < gophers; i++ { go func() { defer wg.Done() // 並行処理でInsertしていく... for ent := range entityCh { _, err := stmt.Exec(ent.Id, ent.Label) if err != nil { errCh <- err } } }() } go func() { defer stmt.Close() wg.Wait() close(errCh) }() return errCh }
性能
以下の環境で試験しています。
software | version |
---|---|
os | macOS Catalina |
go | 1.15 |
postgres | postgres:13-alpine |
50,000件Insertしてみた結果は以下
並行数 | 時間 |
---|---|
1 | 101 sec |
3 | 52 sec |
goroutine の数を増やしたらちゃんと早くなりました 😌
参考
ポンコツ宣言
産業医面談を経て、自分のストレスがかなり高まっていることを認識したので、上司に、心がポンコツになったので今後の仕事内容はポンコツになります、とポンコツ宣言をしました。上司の心配そうな顔の影に「何いってんだこいつ?」感がほんの少しでているように思いました。自分が同じ立場だったらそうなると思うので残当です。もしくは、他人の表情を悪く捉える自分の認知に問題があるのかもしれません。なんにせよ、こういう宣言をすることができる会社にいる、というのはかなりの救いです。仕事の進め方ややり方を一緒に考えていけるようになりました。
人と話すことが嫌いなのでプライベートでは一切人と関わらないのですが、テレワーク主体となって仕事でも人と基本関わらなくなった結果、人間に対する耐性が極限まで低下してしまったのだと思います。そのため、新しいプロジェクトに参加した際の、よく知らない人間たちとのコミュニケーションの嵐に耐えられなくなってしまったのだと自己分析しています。今の自分の人間耐性は、アパレル店員と少し話しただけで翌日1日は寝込む、くらいに低下しています。仕事したくない。。
雑記
仕事の話
社会人である以上、話したくない人間とコミュニケーションを取らないといけない場面というものは往々にしてある。ある程度は仕方ないと諦めているが、できるだけそういう会話の機会は減らしたいと思っている。特に、自分と比較して、頭の回りが極めて悪い人間とはできるだけ距離を取りたい。 しかし、最近は技術力の高い同僚たちが次々と会社を去っていったため、地頭の良い人達と会話する機会が減り、相対的にその話したくない人間たちとのコミュニケーションの機会が増えており、働くのがどんどん辛くなってきている。自分より頭の良い人達とだけ会話したい 😑
登山の話
先日奥多摩で何年かぶりに道迷いをしてしまい、ガチで遭難か!?と結構あせった。
右の尾根に降りるつもりがなぜか左の尾根に入ってしまい、正規の登山道から外れてしまった。しかし自分では右の尾根を歩いていると認識していたので、道を間違えたことに気づいた後、誤った認識のままで道を引き返してしまってどんどん泥沼にはまってしまい、気づいたら取り返しがつかないほど方向感覚が崩壊していた 😥
自分がわけのわからないところにいる、と認識した瞬間、気持ちの良かった山道がとたんに何やら恐ろしいものに感じ、視界が狭くなり、呼吸が浅くなり、、山はやっぱり怖いなあと思った。なんとか正しい道に戻れてよかったが、反省したい。
Multi Module ProjectのSpring Boot × Kotlin Applicationにdetektを導入する
仕事で使うことになったので勉強しました 😌
前回の記事 に適用します。
point
- ktlintはdetekt-formattingを利用する。そのためにmaven kotlinx repositoryを追加する
- multi moduleすべてを見るようにinput files を設定する
- checkでdetektも走るようにする
build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile // use kotlin 1.4.10 at dependecyManagement // https://github.com/spring-gradle-plugins/dependency-management-plugin/issues/235 extra["kotlin.version"] = "1.4.10" // detekt version (pluginとversionをあわせる) val detektVersion = "1.14.2" plugins { id("org.springframework.boot") version "2.3.4.RELEASE" apply false id("io.spring.dependency-management") version "1.0.10.RELEASE" id("com.google.protobuf") version "0.8.13" apply false id("java") // pluginを追加 id("io.gitlab.arturbosch.detekt") version "1.14.2" kotlin("jvm") version "1.4.10" apply false kotlin("plugin.spring") version "1.4.10" apply false } repositories { mavenCentral() jcenter() // for detekt org.jetbrains.kotlinx:kotlinx-html-jvm // これを入れないとdetekt pluginをinstallできない maven(url = "https://kotlin.bintray.com/kotlinx") } allprojects { group = "com.example" version = "0.0.1-SNAPSHOT" } subprojects { // 省略します ;-) } // ここからdetektの設定 detekt { toolVersion = detektVersion // default settingをoverrideする config = files("detekt/detekt.yaml") buildUponDefaultConfig = true // subprojectsのsrc/main|test/kotlinをチェックする val sources = subprojects.map { listOf("${it.projectDir}/src/main/kotlin", "${it.projectDir}/src/test/kotlin") }.flatten() input = files(sources) } // ktlintはdetekt pluginで設定可能なのでこちらを利用する project.dependencies { detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:$detektVersion") } // detektをcheckで実行 tasks.named("check") { dependsOn(tasks.named("detekt")) } tasks.withType<io.gitlab.arturbosch.detekt.Detekt> { jvmTarget = "11" }
kotlin
@SpringBootApplication class CleanApplication // detekt 検出を防ぐ @Suppress("SpreadOperator") fun main(args: Array<String>) { runApplication<CleanApplication>(*args) }
detekt-formattingを使っている記事が見つからなかったのでちょっとハマった 😔
Multi Module ProjectのSpring Boot × Kotlin ApplicationにgRPCを導入する
Clean Architecture (Multi Module Project) のSpring BootでgRPCを使えるようにします。 仕事で使いそうなので勉強しました。本当はGoで作りたかったけど、誰も賛同してくれなかった。人望/Zero 😉
続きを読む