Kashibuchi’s blog

勉強のこと、趣味のこと、日々のこと

【備忘録】SpringのWebClientでのメモリリークの対処

「spring boot scheduled memory leak」でググったらScheduledとは関係がないけど以下の記事が。

techlab.bol.com

そういえばHTTP Interfaceは内部的にはどうしているんだろうか。

あと、関連してJavaメモリリークについてのBealdungの記事。

www.baeldung.com

あとこれも。本命。 やりそうー。

stackoverflow.com

SpringBoot3(Spring6)でのAPI呼び出し(HTTP Interface)について

WebClientかあ、Reactor難しいなあ……ちょっと楽できないかなあ。

なんてことを思っていたら、

docs.spring.io

「1.3. HTTP Interface」にWebClientによるノンブロッキングを意識しない書き方があるやんけと。

具体的な書き方は

tech-blog.yayoi-kk.co.jp

ik.am

を参照していただくとして、自分ではこれから別の気になる部分の検証をしようかなと。 後日更新します。

やっておきたいこと

  • HTTP Interfaceを使用してブロッキングな戻り値でAPIのレスポンスを得る際、
    呼び出し先のAPI応答時間が長い場合、HTTP Interface内部で動作しているWebClientのスレッドは枯渇して待機時間が伸びてしまうようなことはないのか?
    • 結局WebClientのMonoやFluxと格闘する可能性が出てくる。

とりあえずわかっている問題点①

github.com ↑にあるとおり、@HttpExchangeに指定するURLにapplication.propertiesの内容をセットできない。 回避策は上記のやり取りにあるとおり↓のやり方なんだけど、

github.com

API呼び出しの際にパラメータ扱いしたくないパスまで渡したくはないなあ…。

とりあえずわかっている問題点①

異なるドメインAPIを複数使用したい場合、HttpServiceProxyFactoryのBeanを複数作りたいが、無理っぽい。 WrapしたBeanを作って使用したらいけるんじゃないかなあ…なんて。

github.com

Reactorを理解する

Reactorを理解したい

ことの発端

「新規開発しましょう。」「Javaでよいですよ。」「任せるから好きに作ってね。」 からの - Spring 5以降なのでRestTemplateがメンテンナンスモードになっており、WebClientが推奨されている※1 - リアクティブプログラミングなんもわからん

※1: RestTemplateのJavadocを参照

spring.pleiades.io

最終的にやりたいこと

  • WebClientを使用してSpringBootアプリケーションからAPIの呼び出しを行い処理がしたい
  • デプロイ先はTomcatなので、アプリそのものはSpringWebFluxではなく、SpringMVCで実装※2

※2: 「SpringWebFluxはNetty使ってね。Tomcatでは動かないよ。」的な

spring.pleiades.io

↓こういうことも書いてあるからやりようはあるのかもしれないけども…

spring.pleiades.io

参考

qiita.com

speakerdeck.com

github.com

projectreactor.io

www.alpha.co.jp

以下、理解のためのメモ

『Reactive Streams 入門 #jjug』にて

2015のスライドで古いけど、基本的な考えを押さえていると思います。
個人的には理解のベースとして大変役立ちました。

キーワード、かな

  1. "データフロー"を記述するためのプログラミングモデル
  2. "変更の伝播"をプログラマに代わって実行するランタイム

組み合わせ可能(composable)

関数同士の組み合わせでデータのフローを作る、と理解

Future/Promise ~略~ データフローはあるが変更の伝播はない

そうなんだ!?

『reactive-streams』のREADMEにて

これはリアクティブプログラミングのための仕様であり、インタフェース。
詳細に理解する必要はあまりないように感じる(なぜなら普段使うのは実装されたSpringのReactorだから)。
おおよその理解をしておいて、実装のドキュメントで理解しきれなかったら改めて参照する、でよいかも?

  • Publisher

    • Google翻訳に突っ込むと『出版社』になる。 publish -er で「出す者」 程度の方がいいかな?
  • Subscriber

    • Google翻訳に突っ込むと『購入者』『加入者』などになる。

パブリッシャーは、サブスクライバーから受け取った要求に従ってそれらをパブリッシュする、潜在的に無限の数のシーケンスされた要素のプロバイダーです。

Google翻訳 データの受け側の処理能力による制限をうけるから、PublisherはSubscriber側の許可を得ながらデータを渡すイメージかな。

『PROJECT REACTOR』のハンズオンにて

FluxとMono

  • Reactive StreamsのPublisherである
  • Fluxは0~n個のonNextイベントを持ち、Monoはひとつ

参考: Javadoc projectreactor.io projectreactor.io

The recommended way to learn about the Flux API and discover new operators is through the reference documentation, rather than through this javadoc (as opposed to learning more about individual operators). See the "which operator do I need?" appendix.

ということでこれ↓も

projectreactor.io

公式のハンズオン解答例

github.com

こちらのsolutionブランチを参照。

アルファシステムズさんのブログは……

またあとで……

GradleのWrapperをバージョンアップする

Spring Initializrが作ったWrapperのバージョンアップ手順。

>gradlew.bat wrapper --gradle-version=8.0.2
Downloading https://services.gradle.org/distributions/gradle-8.0.1-bin.zip
...........10%............20%............30%............40%............50%............60%...........70%............80%............90%............100%

Welcome to Gradle 8.0.1!

Here are the highlights of this release:
 - Improvements to the Kotlin DSL
 - Fine-grained parallelism from the first build with configuration cache
 - Configurable Gradle user home cache cleanup

For more details see https://docs.gradle.org/8.0.1/release-notes.html

Starting a Gradle Daemon (subsequent builds will be faster)

BUILD SUCCESSFUL in 39s
1 actionable task: 1 executed

やりたいことメモ(備忘録)

アプリを作りながら勉強したい

なんのアプリ?

稼働時間入力アプリにしよう

実現したいこと

  • ログイン機能
  • プロジェクト名を入力して登録
  • 登録済プロジェクト名を選択して時間計測
  • カレンダーから週毎、月間の累積稼働時間を参照
  • 計測した時間を修正

やること

画面作成

  • HTMLとCSSのみでひとまず作成

アプリ機能作成

  • SpringBootを使用
  • DBはとりあえずインメモリ
  • TDDで開発

ちょっとリッチに

  • モーダルウィンドウで確認メッセージ表示(モーダルのライブラリ選定)
  • 時間計測を非同期で処理(JavaScriptJQuery?Vue.js?)

書籍の感想「テスト駆動開発」

shop.ohmsha.co.jp

以前、私が参加しているJavaのサロン(で合ってる?)JPINにて読書会があり、そこで感想を共有したのですが、業務を通じて自分の中で改めて持った感想をここにまとめようと思います。

まず、やってみよう

読み終えた最初の感想は、自分一人でもやってみたいし、ある程度テストが書ける人が相手であれば、ポイントを押さえた説明をすれば曲がりなりにも複数人で実践できそうな気がすると感じました。 読み終えたタイミングで、派遣先プロジェクトのリーダーさんにTDDを読んだうえでやってみる旨を話したところ、ぜひぜひということですぐに取り掛かれました。 そして、他のメンバーにもなるべく具体的になるよう作業説明をして実践していただきつつ、自分でもやってみました。

実際に作ってみると、書いた実装を読み解きながらテストを書くよりもスムーズかつ、漏れが起きないように感じました。 スムーズというのは、「処理としてはザルなんだけどとりあえず期待値を返す」状態から、動いてほしい処理のwhenやverify(JavaなのでMockitoを使っています)を書いてから実装に反映させるので、無駄なモックを作らない感じがしました。 漏れが起きないというのも、テスト対象が依存している処理の呼び出し有無判定が抜けないので、より正確に書けていると感じました。

利用しているクラスのテストはどうする?

ちょっと悩んだのはこの書籍のやり方だと、今まで自分がテストを書く際にやっていた、テスト対象ソースとテストソースが1対1になる、対になる状態にはならないことが気になりました。 ただ、テスト駆動であるおかげで必要な処理バリエーションはきちんと確認できているので問題はないはずなんですよね。 この後感想を書くつもりである『良いコード/悪いコードで学ぶ設計入門―保守しやすい 成長し続けるコードの書き方』も踏まえてテストを書いていくと、DTO(※的なものとしておきます)はただのGetter/Setterだけを持つクラスにはならずに判定処理やデータ加工のメソッドを持つようになったんです。 そして、このクラスに対してのJUnitテストソースが必要かなと思ったのですが、DTOの利用クラス側のテストで十分にパターンは網羅できたので、その結果としてテストソースの量が減るならば喜んでよいのではないかなと思うのです。 この辺はTDDの書籍を読んだことのある人が管理者側にいるか、自分がつよつよエンジニアになれば納得してもらえるようになるのかなあ、とぼんやり思いました。

ウォーターフォールにも合う

TDDって、正直アジャイル的な手法だと思っていました。 ですが、これはウォーターフォールにも適用可能な手法なんだと実際に試して理解することができました。 結局単体テストで動作を定義しながら実装を作っていくのって、デバッガを使用したり、ローカルでアプリそのものを起動して動作確認しながら実装していくのと変わらないと感じたんです。 それに、

  • 実装期間中に単体テストも着手できる
  • テストソースを書いているため、自分の実装した処理による結果が明確にされている
  • 手戻りがあっても、『テストを正しい方に変更→実装を変更』の流れで柔軟に対応できる

ということで、実装を終えてから単体テストソースを書くよりも良いことが多かったです。 「テストを書きながら実装をするんだから、工数的には多くなるじゃないか、余計に時間がかかるのではないか」という印象もあったのですが、ありませんでした。 前述した『デバッガを使用したり、ローカルでアプリそのものを起動して動作確認』の時間がテストソース作成の時間に置き換わるだけでした。

また、テストをすでに書いているので、処理をメソッドや別クラスに切り出すといったリファクタリングにも対応できるのが嬉しいポイントでした。

結論「TDD、怖くない」

テストを書いて動作を定義しながらの実装は良いことずくめでした。 最高。

仕事での開発で感じたこと

はじめに

ようやっと業務でJavaのソースを触れる、単なる修正ではなく機能追加を行うので構造を考えて実装ができる、ということで喜んでお仕事しましたが、既存のソースで気になる点があるため、自分ならどう作るのか考えをまとめることにしました。

ただし、具体的な実装方法は勉強しないとわからないので、そこは宿題。 つまり、以下は自分の宿題リストも兼ねる。

一応、前提はJava(SpringBoot)。

ログ出力の分離

設計にて実現したい機能とログ出力内容を定義するけど、ソース上は一つのメソッドに処理もログも書いてある。 その為、ログのメッセージ内容なんてものは単体テストレベルで文字の一致を確認できれば間違いがないのに、テストソースが煩雑になったりしてやりにくい。

FATAL、ERROR、WARN、INFOログの出力は処理を書いたメソッドとは別に実現したほうがテストもしやすいんじゃないかなあ。 ビジネスロジック内で書いてもよいのはDEBUGのみ。

リポジトリ(MybatisのMapper)の試験はインメモリDBではなく、実際に使用するDBで試験する

インメモリDBでSQLの試験ができるなんていいじゃん!と思っていたのですが、DDLが実際に使用するMySQLPostgreSQLと異なるので、インメモリDB用のschema.sql作成しないといけない。 異なる種類のDDLを保持するのはもったいし、間違えそうなのでテストでもインメモリDBは使いたくないなと感じた。 CIでどのように動作させようかな、とは思うが。