JJUG CCC Fall 2017に行ってきました
JJUG CCC Fall
まえがき
自分用のメモではありますが、役に立つ人がいるかもしれないと思いメモったことと簡単な感想をブログに乗せておきます。 間違いに気づいたら後から訂正しますが、速度優先と自分用メモなので正確な情報は登壇者があとからあげているスライドを見ることをおすすめします。
Selenide or Geb ? あなたはその時どちらを使う?
はじめに
Codezin
- selenideとgebの本が出る
概略
Selenide
- エストニアのcodeborne社のandreiが作ったJava製WebDriverラッパー
- DSLを用いたjQuery風セレクタ
- IDEによる補完機能を駆使
- 要素取得時の待ちを暗黙的に行う
- AJAXなどの非同期処理の返りの処理記述が容易
- 呼び方は?
- せれないど?せれにど?
- 英語だと「せれにど」、ロシア語だと「せれないど」
- 製作者的には「どっちでもいい」
- 日本語だと「せれにど」だからせれにどでいいのかなぁ
- 英語だと「せれにど」、ロシア語だと「せれないど」
- せれないど?せれにど?
Geb
- 読み方は「じぇぶ」
- Groovy製のSeleniumラッパー
- バージョンは2.0
- DSLを用いたjQuery風セレクタ(selenideと一緒)
- groovyの機能をふんだんに利用した、簡潔な記述が売り
- selenideよりもより簡潔にかける
selenide Pros and Cons
Pros
Cons
ちょっと質問
geb Pros and Cons
Pros
- 簡潔な記述
- 豊富な機能
- 豊富なエコシステム
- 特にgradleか
- 日本語情報はselenideよりも多め
- 簡単に利用できる
Cons
体験談
初めてのSelenium(Selenide)
Jenkinsで不安定
- 手元で通るのにJenkinsだと失敗
- デフォルトで待ち処理が入ってるのに。
- 録画機能などを使わないと原因追求が困難
- 生のSeleniumよりましだけど、安定化はやはり大変
- 画面キャプチャが残る
- キャプチャじゃわからないような原因でfailしていたりする
- waitがうまく入らない、でハマる事が多い
- 都度都度waitを差し込んで対策していく
geb/selenideが話が出て来る文脈
- レガシーなシステムのQAコスト下げたい、とか
- QAは6時間が20分になるかもしれないけどシナリオのメンテナンスはしなきゃなので、それはそれで辛い
- シナリオを作り込むのにも時間かかる
マルチブラウザテストの導入
- マルチブラウザのテストがしたい
- クラウド上のブラウザでやりたい
- sauce labs
- GebConfigの書き換え(Driverの生成処理の変更)で実現でき、対応が楽
- Selenideも似たようなことができる
- Configと起動引数のどちらかが使える
- Selenideも似たようなことができる
ちょっとQA
- phantomjsが早い?
- seleniumはE2Eテストなんだからユーザが使う本物のブラウザを使いなさい
Selenide or Geb
外的要因
- groovyが使えるか
- 知名度/利用実績
- 実績はある
- Selenideは弱い
- 商用サポートの有無
- Selenideは日本語ではないが、商用サポートがある
- 今は問合せベースなので、サポートプランが具体的にあるわけじゃない
- gebはまったくない
- Selenideは日本語ではないが、商用サポートがある
読むか書くか
学習コスト
テスティングフレームワーク
さくっと動かす
- 対話環境
- Gebはgroovysh
まとめ
- コンテクストはさまざま
- その時時で良し悪しは変わる
感想
- 学習コストの低さという意味ではSelenideのほうが導入しやすそう
- jQueryライクということなので、どちらを使うにしてもそこは勉強すべきか
- QAコストを下げるために導入したとしても今度はメンテナンスコストが発生する
- ただし、シナリオが変わればテストケースも変更が入るのだからそこは相殺な気がする
- ただ、シナリオをコードに落とし込むところが時間がかかりそう
- エクセルでテストケース書くのに比べたらそれすらも楽しい気はする
Aapache Camel + howtio + Spring Bootによるモダンなインテグレーション・マイクロサービス
howtio
- 「ほーとあいおー」と読むらしい
AGENDA
- インテグレーションマイクロサービス
- Apache Camelとは
- インテグレーションマイクロサービスの作り方
- howtioによるモニタリング
インテグレーションマイクロサービス
- 賢いエンドポイント単純なパイプ(Smart endpoints and dumb pipes)
- マイクロサービスの中に連携するためのロジックが入り込んでくる
- 連携するためのプロトコルにはhttpやメッセージキューイングなどのパイプ役をライトウェイトな技術をを使っていく
- SOAはEnterpriseService Busと言われるプロセスが間に入って、各サービスを統合する
- MSAでは各サービスの中にルーティングのロジックが入ってる
- 他のサービスをいろいろ叩いて一つのサービスを提供するようなものを出て来る
- そういうのを指してインテグレーションマイクロサービスと呼んでいる
- SOAではインテグレーションスパゲッティと揶揄されてアンチテーゼだった流れからESB導入された
- 揺り戻しっぽい
- 他のサービスをいろいろ叩いて一つのサービスを提供するようなものを出て来る
- MSAを推し進めると各サービスが十分小さくなり、それを担当するチームなら十分複雑さに立ち向かえるだろうという流れがありそう
Apache Camel
- 軽量なインテグレーションフレームワーク
- camel-core.jar 4.8M
- 直感的なルーティングDSL
- 流れるようなインターフェース
- Enterprise Integration Patterns
- 軽量だが汎用性も高い
- 280+の接続コンポーネント
- まだ日本で流行ってないようなものでも既に公式サポートされてたりする
- GUI開発環境(Eclipse)
- マイクロサービスのサポート
Camelでできることの例
Java DSL
- RouteBuilderを継承してconfigure()メソッドをoverrideする
- fromは一つ、toは複数書ける
- ルートの定義は、fromから始まるメソッドチェーンをまた書けば良い。
- 別のクラスを書いてもよい
- ALT JAVAでもOK
- Scalaとか
- Spring XML DSL
Enterpise Integration Patternsをサポートしてる
- システム間連携のベストプラクティス
- 65のパターン
例えばCONTENT-BASED ROUTER
- choice/whenで条件分岐できる
280+のコンポーネント
- 殆どの物があるので見つかると思う
ECLIPSE プラグイン
- Intellijにはない
インテグレーションマイクロサービスの作り方
CAMEL Spring Boot
- Spring Bootプロジェクトを作成
- CamelコンポーネントStarterを依存に追加
- @ComponentをつけたCamelルートを定義
ComponentをつけたCamelルートを定義
- RouteBuilderをかいて@Componentをつければよい
- xmlでやるときは@ImportResourceで。
howtioによるモニタリング
JVMの監視といえば
howtio
howtio x Spring Boot
- 依存に追加するだけ
Demo
- Camelのサポートが充実している
- グラフィカルにルートを見ることができる
QA
- 単体テストのサポートはどんなふうになってる?
- あるよ。CamelTestSupport
- 失敗談や落とし穴は?
- コンポーネント毎に癖がある
- 認証コードをどうやって渡すかとか、そこが超えなきゃいけないハードルではある
- コンポーネント毎に癖がある
感想
- パイプとなるprotocolを強く意識せずに透過的に扱えそう
- 調べる価値がある ‐ちゃんとCamelのサポートが熱い監視ライブラリの紹介があったのがとてもgoodだと思った
DDD x CQRS - コマンドとクエリでORMを使い分けた話
感想
- モデルが参照に引っ張られるって話は、「そうだよね!」って思った
- 参照にモデルが引っ張られてるな‐と思ったらCRQS(チックな)パターンを導入してみようと思う
劇的改善 CI4時間から5分へ〜私がやった10のこと〜
スライド
対象のアプリ
テストクラスの責務の明確化
Controllerテストのアノテーション変更
- こんな感じの表があった
@SpringBootTest | @WebMvcTest | |
---|---|---|
概要 | Spring applicationを起動してテストする | Spring MVC infrastructureをAuto-configurerdしてテストする |
bean | 全てのBean | @Controller,@ControllerAdvice,@JsonComponent, Filter,WebMvcCOnfigurer,HandlerMethodArgumentResolver |
テスト時間 | Webサーバまで起動するため非常に遅い | Spring MVCに必要なBeanしか登録されないため早い |
類似 | - | @JsonTest, @DataJpaTest, @JdbcTest ... etc |
ServiceテストでDIを使わない
- Service層はアプリケーション外とのやり取りがないようにする
- Springを使わない、シンプルなJunitテスト
- 依存しているオブジェクトはMock
RepositoryはfilterによるBean選択
- @MyBatisTestで必要なBeanのみを登録できるようになった
- 必要ならBeanをフィルタリングする必要はあると思う
負債となったテストの改善
- テストはある程度の規模の開発では爆発的に増える。結果、負債となる。
- ひとつひとつ潰すしかないよ
テストの削除
- 時にはテスト自体を削除する決断も必要
- 不安定
- 保守しにくい
- テスト責務が広すぎる
- 過度なもの
CIの再構築&並列テスト環境の構築
- 性能が良くない社内サーバからWAWSへ(性能&安定性UP)
- ジョブ数に応じて、AMIからNodeを自動生成するようにして自動でスケール
- RDS内部でスキーマを切り、Node毎に別の仮想DBを用意
- テストの並列実行が可能に
テストサイズの導入
- こんな感じの表があった
Small | Medium | Large | |
---|---|---|---|
時間的目標(メソッドごと) | 100ms未満で実行 | 1s未満で実行 | 出来る限り高速に実行 |
ネットワーク | モック | localhostのみ | ○ |
データベース | モック | ○ | ○ |
他システムへのアクセス | モック | 非推奨 | ○ |
実行タイミング | プルリクエストごと | ベースブランチにマージ | 任意、またはリリース毎に |
$ mvn -P Small $ mvn -P Mediam $ mvn
プルリクエストFeedBack
- プルリクエスト単位でCIの結果をフィードバックできるようにした
- Lintの指摘(lint-review)
- カバレッジの可視化(CodeCov)
- 負債の指摘(SonarQube)
- チーム独自のルール(Danger)
感想
- テストサイズという概念を知ることができてよかった
- 普段テスト書くときにモックするかどうか悩んだりするけど、自分がどのサイズのテストを書きたいと思っているのかで判断してもいいかも
Spring Securityにできること・できないこと
- スライドが既に公開されているので細かいメモは割愛
感想
- とても良くまとまっていて自身の知識の整理になった
- Spring Securityが具体的に何に対してよしなにやってくれてるのか把握してなかったが、そこが明確化された
- opengl 8080さんのQiitaの存在をしれたことも良かった
- いろいろなことをよくまとめてあるので、眺めると勉強になりそう
入社してから運用しているサービスの運用改善奮闘記
- スライドが既に公開されているので細かいメモは割愛
感想
- The Twelve-Factor Appという言葉をしれたことが良かった
- frontend maven pluginなるもののgradle版はあるのか疑問
- あとで調べてみる
- guageが良さそう
- 部品をエンジニアは書くけど、それを組み合わせたシナリオを日本語で非エンジニアにシナリオ書いてもらえたりする
- それでも抵抗はあるだろうけど、非エンジニアにテスト参加させられる
- まだベータなのが残念
- 部品をエンジニアは書くけど、それを組み合わせたシナリオを日本語で非エンジニアにシナリオ書いてもらえたりする
Spring BootとKafkaでCQRSなアプリを動かしてみる
CQRS
- Command and Query Responsibility Segregation
- コマンドクエリ責務分離
- named by Greg Young
- だいたいマーチン・ファウラーがかかわってる
- コマンドとクエリを別のオブジェクトに分ける
- 更新するやつはvoid、クエリは副作用無いようにする
- クラスを分ける
- コマンドとクエリを分けるという点だけがCQRSの定義
今日のお話
- 業務で使いたいことを勉強してることを発表
今日のゴール
- CQRS面白そうっておもらいたい
第1話:僕とDDD
DDDの印象
- 依存関係を切り離して複雑さを閉じ込めているっぽい
境界づけられたコンテキスト
- コンテキストを切り離す
ドメインモデル
- DBやUIに関係することを切り離す
集約
- 他のモデルから切り離す(トランザクションの単位)
まとめ
- 複雑さを凝縮
感想
- DBからの開放、最高
- UIからの開放、むずい
- 複数の集約を繋いだ情報が必要だったり
- 集約から導出した値で検索が必要だったり
第2話:CQRS
- 書き込みも参照も一つのモデルにとらわれる必要がない!
- DB分けても良いし
- Query側にはDomain Modelを置かないという方法も取れる
- 書き込み用のモデルが参照のモデルに引っ張られてたな、というのはある
- クエリからの開放!
第3話:僕とDomain Event
- コンテキストや集約を切り離したら、これまでトランザクションで繋いでた情報をどうやって伝えよう?
- ドメインイベントでつなぐのが良さそう
- 何かが実際に起こった、という事実をモデル化
- CoffeeOrdered
- CoffeeBrewed
- CoffeeDelivered
- 発生したイベントを受け取って情報を更新していく
- 内部の動きを想像するんじゃなくて起きた事象をベースにするとビジネスの人とでも会話できそう
- ドメインイベントでつなぐのが良さそう
第4話:僕とEvent sourcing
- 状態自体を書き換えるのはステートソーシング
- きっとみんながやってる方法
- イベントの積み重ねによって今の状態があるという考え方がイベントソーシング
- でもこれやるとクエリ困る
- ここでCRQSが出てくる
- イベントストアにイベントを入れてき、そのイベントを拾って今の状態を持つデータベースを持つ
A Decade of DDD, CQRS, Event Sourcing
- CQRSはEvent SourcingへのStepping-Stone
- CQRS/Event Sourcingを全体に適用しない
- いろんな実現方法が合って良い
- 複雑なので実現にはコストがかかる
- 必要な箇所だけに局所的に適用していったほうが良さそう
CQRSとEvent Sourcingはセットなの?
- 共生関係にはあるけど、別々の考え
- 組み合わせると2PCなくて良さそう
第5話:僕とCQRS
- 似たようなことやってる
- クエリ油のDB
- レポート用のテーブル
- サーチは別のシステム
- トリガーはさまざま
- DBに保存するときに頑張る
- 定期実行のバッチ
- APIを呼び出したり
- それもいいね
- ドメインイベント駆動型でもやってみたい
第6話:CQRSとKafkaとSpring Boot
Messagin System
- kafka
- 過去の分も残ってるのがなんかいいなって。
感想
- domaはDDDと親和性が低いかと思っていたが、コマンドとクエリを分ける方法なら逆に親和性が高そう
- 2PCを回避する方法って何かないかな
CPUからみたG1GC
感想
- 難しくて正直ついていけなかったが、よくわからない(デフォルトだから)G1GCにしとこうという安易な考えは危険だってことだけはわかりました。
以上。
2017 Java One 報告会に行ってきた
2017 Java One 報告会
行ってきました。
後半疲れてメモが取れていませんが、 雑なものでも助かる人はいるかもしれないのでメモを残します。
リリースモデルの変更
従来
- OpenJDK
機能リリース
長期サポート
- 更新リリース
- 3ヶ月毎
- メンテナンス用、機能更新は行われない
新しいリリースモデル
OpenJDK
機能リリース
- 6ヶ月に一度、固定周期でリリース
- バージョン表記:$YEAR.$MONTH (18.3,18.9)
- 完成した機能からリリース
Open JDKのバイナリ
で配布- 後継バージョンまでの6ヶ月の無償サポート
- GPlv2 + Vlasspath Exception
- 長期サポート
- 更新リリース
- 3ヶ月毎
- メンテナンス用、機能更新は行われない
公式アップデート終了のスケジュール
- 8
- 2018/9終了
- 9
- 2018/3終了
- 18.3
- 2018/9終了
Java 9 and Future
今日のお話
Java 9の調べ方
- Java Enhancement Proposal(JEP)を見る
- http://openjdk.java.net/projects/jdk9
- bugzilla
- fixVersion = "9" AND labels = release-note
移行する際に注意するポイントは?
- Migration Guideを読む
- https://docs.oracle.com/javase/9/migrate/toc.htm
- メジャーバージョン毎の非互換性ポイントが書いてある
より細かく見る
- SpecificagtionやRelease Noteを読む
メリットの一部
- モジュール化(Project Jigsaw)
- RPEL(Jshell)
- ライブラリ改善
- Collection 初期化、Stream機能拡張
- セキュリティ強化
- ALPN,DRBG,SHA-3
- 付属ツールの刷新
- jcmd, jhsdb, jaot(AoT Comppilation)
- G1 GCやコンパイラなどの性能改善
Demo
Jigsaw
- module名のディレクトリを切って、module-info.javaを書く
- 同じpublicでも、module-info.javaで公開するしないをきめ細やかに指定できる感じか
- 依存関係を書くイメージ
- publicだけど公開されていないAPIを利用しようとするとコンパイル時に検知して、エラーで教えてくれる
- 依存moduleをmodule-info.javaに書き忘れると、importしてるけどmoduleをmodule-info.javaにdeclareしてないよって教えてくれるよ
- ソースコードの依存関係が把握しやすくなる!
- DDDに有効活用できそう
- デモの内容はgithubにあるよ
jshell
Collection Factories
- List.of(...)
- Arrays.asList(...)
- Set.of(...)
- Collections.unmodifiableSet(new hashSet<>(Arrays.asList(...)))
- Map.of(...)
- new hashMap<>(){{put(...);...}}
Properties of Collection Factories
- imutableになってる
- フェイルセーフ
- NPE
- IAE: Set,Map(key)に重複する要素
- 最適化されている
Enhancement Stream API
- Stream.dropWhile
- Stream.takeWhile
- 無限loopに終了条件を与えることができる
- Stream.ofNullable
- Stream.iterate
- ほぼfor文
18.3のチラ見
Intel's persistent memory
- 揮発性じゃないメモリ、電源が切れてもデータが保存されてる
- 市場に出るのが2018
- Oracle Databaseで使えるように
Oracle DB + PM
- だいたい5倍早くなった
使いみち
- Volatile Strage
- Persistent Strage
- https://github.com/pmem.pcj
- 永続化可能なクラス、コレクション
- API,トランザクション
- クラスの定義方法
- PMを直接叩くAPI(Low levelAPI, Panama API)
まとめ
- OSS DBが出てくるのはまだまだになりそう
Java EE 8
概要
- 2017/9に出た
Java EE 8
source
- java.net -> githubに移行 ‐java.net : https://javaee.github.io/
Glassfish
Weblogic
- Java EE 8サポート版は来年出す予定になってる
Eclipse Enterprise for Java(EE4J)
- 以下にしたい
- 8 available
- open
- nimble
- eclipseに移管
- 移管プロジェクトの名前がEE4J
- Java EEの後がまではない(ブランド名は決まってない)
- もしかしたらそのままEE4Jになるかもしれないけど、現時点でそうとは決まってない
どうありたいか
- Open
- たくさんのひとに参加してもらいたい
- コミュニティドリブンなプロセスを作っていきたい
- oracleはリーダーの立ち位置ではなくメンバーに変わる
- Comppatible
- Flexible
- Modern open source process and licensing
- Nimble
- Morerapid evoution of the technology
- リリースサイクルを早くする、仕様作成プロセスとかも。
その他
- Micro Profileと(Next) Java EEはマージする予定
- JSRのやり方は踏襲する
まとめ
- Java EE 8 was released.
- EE4J has started.
Microservices topic & approach
ゴール
- いってみてわかったこと
- 今、全体的に何が議論されているのか
行ってみてわかったこと
- 10月のサンフランシスコはホテルは高い(普段の2〜3倍)
- 会場近くのホテルはすぐ埋まるし、高い
- 疲れたらすぐ戻れるように会場近くがよし
- 長丁場で、体力勝負
なるべく疲れないようにする
- 16時間の時差、慣れない環境で5日間は体力勝負
- きつければ、適宜休むこと
- 割と寒い
- 移動はUberが楽
必ずセッションは事前登録
- 優先で入れる
- 未登録の場合は、並んで空いてたら入れる
技術系・資料ありセッションはわかりやすい
- 技術系のお話や資料に沿って解説は英語でもわかる
- 資料がタイトル程度だったりパネルディスカッション系はしんどい
Microserviesの話題や取り組み事例
モノリスからMiroservicesへ
組織、文化
- システムのスケールに合わせて組織のスケール
- コンウェイの法則
- 組織のコミュニケーション構造がシステムに反映
- 逆法則も(Service間コミュニケーションの形が組織に反映)
- Automy(自主・自立性)を何より重視
Microservicesをとりまく仕組みについて
- CI/CD
- 小さく、頻繁にデプロイする
- Container, Orchestration
- Docker,kubernetesが常に登場
- Service Registry
- Serviceのホスト名・ポート番号を名前で抽象化
- Asynchronous
- 可能な限り同期より非同期
- Fault Tolerant
- 一部のエラーで全体が影響を受けないように
- Monitoring
- 現状がどうなっているかを可視化する
- Tracing
- どこにボトルネックがあるか追跡できるようにする
議論の対象の変化
- FrameworkやLibraryは自前で開発したりラッピングするものではない
- それらはConsumeするもの
- 議論の対象はServiceという部品をどう協調動作させるか?になっている
同期 < 非同期
データ処理パターン
Eventual Consistency(結果整合性)
- 異なるデータストア間の内容の整合性がEcentual Consistencyでも良い場合は非同期連携で各データストアに反映すれば良いため、相性が良い
Event sourcing
- データストアをイベント記録に使う
- insertのみし、updateはしない
- ひたすらログを取っていくやり方
CQRS
- 更新用のStrageと参照用のStrageは別だったりもする
- 更新用のStrageからEvent sourcingで参照用のStrageに現状のデータを反映する?
複数ServiceにまたがったTransaction
- Long running action
考察
必要性を考える
- スケーラビリティやリリースの速度アップの必要性に迫られてそうなった
- どこでもスモールスタートだった、そしてMicroserviceを想定していない
- 必要に迫られてるか?を自問したほうが良い
組織や文化とセットで考える
トレードオフも考える
以上! Java 9の話が特に勉強になりました。 パッケージの一つ上の階層としてモジュールって概念が登場して設計の選択肢が広がるのかなって思ったの一番強く残った印象です。
【第2回】ドメイン駆動設計のための オブジェクト指向プログラミングに参加しての個人的メモ
【第2回】ドメイン駆動設計のための オブジェクト指向プログラミングに参加して
超個人メモだけど、良い経験になったのでブログにメモを残します。
見てる人いるかわかりませんが、ユーザ定義型の数が多かったと発表したのは私です。 15-6かな?って言いましたが、19個ありました。 今日のテーマに沿ってやったつもりだったんですが、みんな全然着眼点が違かった…。 とても勉強になりました。
序章
モデルとは、人間が頭のなかに理解しているものをさす
- それを言葉や図などで表すのと同じように、コードでも表現すると言う発想をする
モデルをコードで表現する、というのは今日のキーワード
- 型!
問題領域の例として、アソビューのサービスを取り上げる
- それをどうやってモデル化するかについて取り組んでみる
UMLが厳密なものという意識を持っている人がいるかもしれない
- DDDの文脈ではUMLはラフスケッチ用
- コミュニケーションに使いやすいから使っているだけ
キーワード
型
型をプリミティブ型でどうやって表現しているのか
- StringやLocalDateなどの標準クラスを見るととても勉強になるよ
BigDecimalは型の教科書としては良くないよ
独自の方を定義する仕組み
- class
- interface
- enum
- (package)は厳密には型ではないけどそれを取りまとめる役目に使える
アンチパターン
- 基本データ型への執着
- これをやると、メソッドが長くなったり引数が増えたりクラスが巨大化したりする
- これが他の不吉な臭いに伝搬する根本原因(か?)
- 逆にユーザ定義型に執着するほど、オブジェクト指向の旨味が出てくるとも言える
- これをやると、メソッドが長くなったり引数が増えたりクラスが巨大化したりする
処方箋
- 値オブジェクト
- コレクションオブジェクト
- 区分オブジェクト
執筆した本では局所化すると言ってるが...
- 実際には変更の分散は起きる
- しかし、推測可能だったりツールが変更の影響箇所を教えてくれたりとか、変更の難易度は下がる
- 実際には変更の分散は起きる
型について
基本データ型をラップするとは?
- コンポジション、プロパティにもつ
ユーザ定義型がいい理由
- 正しい
- 表現力
- Find Usage
正しさ
基本データ型
- 汎用的
- 値の範囲
- 可能な操作
- 特定の目的のためには、間違った範囲、間違った操作を含んでいる
ユーザ定義型
- 値の範囲を用意に合わせて制限
- 可能な操作を用途に合わせて限定
- 間違いが減る
- 安心・安全
表現力
- 基本データ型
- いろいろな用途に同じ型を使う
- 意図が不明
- 10個とか多い単語で説明されてもわからない
意図が伝わらない
ユーザ定義型
- 用途が明確になる
- 引数の型、メソッドの型、シンプルのメソッド名
- 型をレビューすればコード内容が推測できる
- ドキュメント性がある
- 引数と返りの型をユーザ定義にすると、意図がわかりやすくなる
増田流レビュー
- else文がある場所やネストが深い箇所を探してピンポイントにレビューする
- ユーザ定義型を使ったコードだとメソッドまでみれば、その人が業務をわかっているかどうか判断できる
- 怪しいのはなんでこの型が入ってるの?何が正解なの?という感じで業務を取り違えている可能性があるので警戒する
Find Usage
- 基本データ型
ドメインに特化したユーザ定義型
- ユビキタス言語などから型を探す
- 汎用の型を用途限定にラップして型にする
- 既存アイデアの流用/カスタマイズ
- 即効性はないが、知識を積み重ねておくと後から効いてくるかも?
- 型の振る舞いの設計パターン
- 判定(同地、大小、最大/最小)
- 加工(型変換、短縮形、合成)
- 計算(四則演算)
- 他のところから考えた型を別の指標で検算してみることもある
勉強会で与えられた課題
- スライドの表を実装するにあたり必要となりそうな独自の型を定義してモデルと実装を一致させる(ために思考してみる)
- コードで書けるときにはコードでいきなり書いちゃえ、コードでかけないときは図などに頼る
- 英語が思いつかないなら、日本語でもいいかもね
エピローグ
たとえ1行だとしても、ロジックが出て来たらオブジェクトに抽出しておくと吉。
- どんなコードでも正しく動いてしまうと後から書き換えるのは心理的に辛い
現場に導入しづらい?
- 前段階として、リファクタリング
- 大きなクラスを分割してみるアプローチがいいと思うよ
- 前段階として、リファクタリング
- テスト駆動開発でいってた良いこと
- 『やりすぎて見てからちょうど良いところが分かるよ』
- どのへんで折り合いつけるのかわかってくるので、一度やりすぎてみるとよい
- 『やりすぎて見てからちょうど良いところが分かるよ』
以上!あー、楽しかった。
gradleで生成される依存ライブラリの並び順をlibrary名でソートする方法
は、以下の記述をbuild.gradleに追加してください。
eclipse { classpath { file { whenMerged { classpath -> def libs = classpath.entries.findAll { it.kind == 'lib' } libs = libs.collect { lib -> def baseDir = project.projectDir.getAbsolutePath().replace('\\', '/') if (lib.path.startsWith(baseDir)) { lib.path = lib.path.replace(baseDir, ".") } return lib } libs.sort(new Comparator<Library>() { public int compare(Library lib1, Library lib2) { String basename1 = new File(lib1.path).getName() String basename2 = new File(lib2.path).getName() return basename1.compareTo(basename2) } }) def others = classpath.entries.findAll { it.kind != 'lib' } classpath.entries = others + libs } } } }
ただし、eclipseにgradleプロジェクトとして認識させると.classpath
を使わずにプラグインの機能を使うようになってしまうため並び順が制御できなくなる。
gradleプロジェクトとしてeclipseに認識させていない場合、以下が不便です。
Docker on Centos7 on Vagrant with Chef
概要
掲題の通りのローカル開発環境を構築してみた。
コンセプト
目的
- RabbitMQやRiakはライブラリでノード指定時に、
config.addresses("192.168.33.10","192.168.33.11")
みたいな書き方をするので、それをローカル環境でやりたい - MySQLは更新系はMasterで、参照系はSlaveなんてケースあるだろうから、Master/Slave構成をローカル環境でやりたい
動かしてるミドルウェア
- Rabbit MQ
- Riak
- MySQL
何をやったのか
後述する参考サイトで、クラスタリングやら固定IPの割付やらは実現されている。
それぞれのdocker-compose.ymlをちょっと修正して、chefのrecipe化して、vagrant upだけで環境を構築できるようにしたところがミソ。
chefでdockerインストールしてdocker-compose upを実行してたりするところもやや難しかった。
成果物
参考にしたサイト
docker containerへの固定IPの割付
各ミドルウェアのクラスタリング
RabbitMQ
Riak
https://hub.docker.com/r/basho/riak-kv/
MySQL
終わり
誰かの役に立つことを願う。
recipe等の解説は、万が一誰かからリクエストがあれば追記する。
Spring AMQPでオレオレErrorHandlerを使ってみるテスト
@RabbitListene
を使っている時にデフォルトのErrorHandlerではなくて、独自処理を実装したErrorHandlerを使う方法を調べてみたので自分用メモ。
設計も汚いし、とにかく動くところまでしか確認してない。
package hello; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.amqp.rabbit.annotation.EnableRabbit; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer; import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; import org.springframework.amqp.rabbit.connection.ConnectionFactory; import org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler; import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar; import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.messaging.converter.MappingJackson2MessageConverter; import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory; import org.springframework.stereotype.Service; import org.springframework.util.ErrorHandler; @SpringBootApplication @EnableRabbit public class Application implements RabbitListenerConfigurer { @Autowired ConnectionFactory connectionFactory; @Bean MessageListenerAdapter listenerAdapter(Receiver receiver) { return new MessageListenerAdapter(receiver, "receiveMessage"); } @Bean public DefaultMessageHandlerMethodFactory myHandlerMethodFactory() { DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory(); factory.setMessageConverter(jackson2Converter()); return factory; } @Bean public MappingJackson2MessageConverter jackson2Converter() { MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); return converter; } public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Override public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) { SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setErrorHandler(errorHandler()); registrar.setContainerFactory(factory); registrar.setMessageHandlerMethodFactory(myHandlerMethodFactory()); } public ErrorHandler errorHandler() { return new MyHandler(); } @Slf4j static class MyHandler implements ErrorHandler { ErrorHandler errorHandler = new ConditionalRejectingErrorHandler(); @Override public void handleError(Throwable t) { log.error("==do something for error=="); errorHandler.handleError(t); } } } @Service @Slf4j class Receiver { @RabbitListener(queues = "spring-boot") public void receiveMessage(SampleDto dto) { log.info("Received <" + dto.toString() + ">"); } } @Data @NoArgsConstructor @AllArgsConstructor class SampleDto { private String key; private String value; }
build.gradleはこんな感じ
buildscript { repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:1.4.0.RELEASE") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'spring-boot' jar { baseName = 'messaging-rabbitmq-oreore' version = '0.1.0' } repositories { mavenCentral() } sourceCompatibility = 1.8 targetCompatibility = 1.8 dependencies { compile("org.springframework.boot:spring-boot-starter-amqp") compile group: 'org.projectlombok', name: 'lombok', version: '1.16.10' testCompile("junit:junit") } task wrapper(type: Wrapper) { gradleVersion = '2.3' } ext.mainClass = 'hello.Application'
あとテストように作ったpublisherも貼っておく
package hello; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.messaging.converter.MappingJackson2MessageConverter; import org.springframework.stereotype.Service; @SpringBootApplication public class Application implements CommandLineRunner { public static void main(String[] args) { SpringApplication.exit(SpringApplication.run(Application.class, args)); } @Bean Queue queue() { return new Queue("spring-boot", true); } @Bean DirectExchange exchange() { return new DirectExchange("direct"); } @Bean Binding binding(Queue queue, DirectExchange exchange) { return BindingBuilder.bind(queue).to(exchange).withQueueName(); } @Bean public MappingJackson2MessageConverter jackson2Converter() { MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); return converter; } @Autowired private Sender sender; @Override public void run(String... args) throws Exception { for (String i : args) { ObjectMapper mapper = new ObjectMapper(); SampleDto dto = null; try { dto = mapper.readValue(i, SampleDto.class); sender.sendToRabbitmqAsJson(dto); } catch (Exception ex) { sender.sendToRabbitmqAsString(i); } } } } @Service class Sender { @Autowired private RabbitMessagingTemplate rabbitMessagingTemplateAsJson; @Autowired private RabbitMessagingTemplate rabbitMessagingTemplateAsString; @Autowired private MappingJackson2MessageConverter mappingJackson2MessageConverter; public void sendToRabbitmqAsJson(final SampleDto dto) { this.rabbitMessagingTemplateAsJson.setMessageConverter(this.mappingJackson2MessageConverter); this.rabbitMessagingTemplateAsJson.convertAndSend("direct", "spring-boot", dto); } public void sendToRabbitmqAsString(final String message) { this.rabbitMessagingTemplateAsString.convertAndSend("direct", "spring-boot", message); } } @Data @NoArgsConstructor @AllArgsConstructor class SampleDto { private String key; private String value; }
rasbperry pi3でNAS構築(とついでにbonding)
ご無沙汰してます。
サーバの勉強にもなるかなと思ってraspberry pi3を手に入れて自作(かつ自宅)NASサーバを構築してみました!
このraspberry pi3なんですけど、無線LAN付きになったんですよ。
USBドングル買わなくても無線化できるので少し財布に優しいー!
さて、そのNASサーバの構築ですがざっくりこんな流れです。
この中の(1)〜(3)の手順は検索すればraspberry pi2のものですが情報がザクザク(でもないかもしれないけど)出てくるのでは書くのはやめるとして、、、
(4)の『bondingの設定をする』に絞って手順をご紹介します。
bondingの設定自体はraspberry pi bondingで検索すると、 以下のページが出てくるので、主にこの2つのページを参考に設定しました。
補助記憶: Raspberry PiでUSB-Wifiアダプタを使う
しかーし、紹介されたとおりに設定してみても紹介されている通りに動作しない… どう、動作しないかというと
- 上記のページで書かれている通り、bond0にだけIPが振られるわけじゃなかった(eth0とwlan0にもIP振られちゃうけど、無視して設定進めたらbondingは動作した)
- LANケーブルさしてないとsshが繋がらない
- LANケーブル挿すとping返ってくるけど通信速度が変わらない
という具合です。
tcpdump見たわけじゃないんですが、有線LAN抜くと動作しないってことは、 bonding用のインターフェース(bond0)に割り付けたIPに対するリクエストの返りが有線LANのインターフェース(eth0)から出ていってるんじゃないかと推測して ルーティングテーブル周りの設定変更を試してみたら、うまく動作した!っていうメモです。
たぶん、正しい手順の解決策ではないと思いますが、自分用のメモ(と、他に困ってる人の参考)に参考になったページのリンクを残しておきます。
似たような現象を探してて見つけたページがこちら。
このページを見て、ルーティングテーブルか何かがおかしいのでは、と気づき ip ruleで検索してでてきた
このページに倣って設定したら、動作しました。
こんな感じ!
Before(といいながら有線LAN抜いた時の速度です。スクショ撮り忘れた…)
After