Concurrency-LimitsとOkHttpのIntegrationを書いてみた

Netflix/concurrency-limits とOkHttp3のIntegrationを書いた。 このライブラリの強みはNetflixの人が書いた、mediumの記事リポジトリのREADMEを読んでほしい。

GrpcのClientのInterceptor周りを参考に書いた。

以下コード。

public class OkHttpClientLimiterBuilder extends
  AbstractPartitionedLimiter.Builder<OkHttpClientLimiterBuilder, OkhttpClientRequestContext> {

  private boolean blockOnLimit = false;

  public OkHttpClientLimiterBuilder partitionByHeaderName(String headerName) {
    return partitionResolver(context -> context.request().header(headerName));
  }

  public OkHttpClientLimiterBuilder partitionByHost() {
    return partitionResolver(context -> context.request().url().host());
  }

  public <T> OkHttpClientLimiterBuilder blockOnLimit(boolean blockOnLimit) {
    this.blockOnLimit = blockOnLimit;
    return this;
  }

  @Override
  protected OkHttpClientLimiterBuilder self() {
    return this;
  }

  public Limiter<OkhttpClientRequestContext> build() {
    Limiter<OkhttpClientRequestContext> limiter = super.build();

    if (blockOnLimit) {
      limiter = BlockingLimiter.wrap(limiter);
    }
    return limiter;
  }
}
public class OkHttpClientLimitInterceptor implements Interceptor {
  private final Limiter<OkhttpClientRequestContext> contextLimiter;

  public OkHttpClientLimitInterceptor(
    Limiter<OkhttpClientRequestContext> contextLimiter) {
    this.contextLimiter = contextLimiter;
  }

  @Override
  public Response intercept(Chain chain) throws IOException {
    OkhttpClientRequestContext context = new OkhttpClientRequestContext(chain.request());
    Optional<Limiter.Listener> listerOpt = contextLimiter.acquire(context);
    if (listerOpt.isPresent()) {
      Limiter.Listener listener = listerOpt.get();
      try {
        Response response = chain.proceed(chain.request());
        if (response.isSuccessful()) {
          listener.onSuccess();
        } else if (response.code() == 503) {
          listener.onDropped();
        } else {
          listener.onIgnore();
        }

        return response;
      } catch (IOException e) {
        listener.onIgnore();
        throw e;
      }
    } else {
      return new Response.Builder()
        .code(503)
        .protocol(Protocol.HTTP_1_1) // dummy
        .request(chain.request())
        .message("Client concurrency limit reached")
        .body(ResponseBody.create(null, new byte[0]))
        .build();
    }
  }
}
public class OkhttpClientRequestContext { // このクラス、参考元のコードはinterfaceだったが、classにして楽をした。

  private final Request request;

  public OkhttpClientRequestContext(Request request) {
    this.request = request;
  }

  Request request() {
    return request;
  }
}

余談

NetflixはConcurrency-Limitsとは別にResillience4jを入れようとしているらしいが このConcurrency-Limitsだけで良いのでは?と思ったんだけど Circuit Breakerとは性質が少し異なるのかな?ちょっとその辺が分からない。 即応性はCircuit Breakerのほうが高い気もするが。 流量制御はConcurrency Limitsで良いのかもしれない。 この辺詳しい人居たら教えてほしい。

Release Itの魅力を伝える

この記事では「Release It」という本を紹介する。 エンジニアリングに携わり、ソフトウェアの開発・運用をしている人に この「Release It」の魅力を伝えたい。

1年以上かけて作ったソフトウェアのリリースの日がやってきたあなたは、全機能が完成し、単体テスト結合テストも終え 安堵したことがあるだろうか。これで完了のはずだ。

本当にそうだろうか?

「そうじゃない」と思った人はもちろん、「これで完了」と思ったあなたに届けたい。 「本番用ソフトウェア」を作りたい、あなたに届ける本である。

この本との出会い

いろんな人に出会い、たまたま幸運なことに、この本を教えてもらう機会があった。 私は、序盤を読んだところ、非常に興奮し、そこそこ厚いこの本をすぐに読み終え 「こういったことがやりたい!」と感じた。

私が感銘を受けた、この本の序盤には、以下のような記述がされている(※)。 少し長いが、引用させてもらう。

※ 「第1章 イントロダクション 1.6 実践的な達人のアーキテクチャ」より

まったく違う2つの活動がアーキテクチャという言葉で一括りにされている。 アーキテクチャの片方では、プラットフォームを超えて可搬な高レベルの抽象化を指向し、電子や光子については言わずもがな、ハードウェアやネットワークなどの面倒な詳細にはできるだけかかわらない。 このアプローチを極限まで推し進めると「象牙の塔」に至る。

象牙の塔にはキューブリックの映画に出てきそうなクリーンルームがあり、そこには超然とした教祖がいて、どの壁にも四角と矢印が描かれている。 象牙の塔からさまざまな司令が発せられ、あくせく働いているコード作成者たちの頭上に降り注ぐ。 「EJBのコンテナ管理による永続性を使用せよ!」 「UIはすべてJSFを使用して構築せよ!」 「今あるすべて、かつてあったすべて、そしてこれからあるすべてのものをOracle内に格納せよ!」 歯ぎしりして「社内標準」に従ったコードを書きながら「別の技術なら同じことを10倍簡単にできるのに」と考えた人もいるだろう

あなたは象牙の塔のアーキテクトの犠牲者だ。 。チームのコード作成者たちの考えを聞こうともしないアーキテクトは、間違いなくユーザに対しても聞く耳を持とうとしない。 その結果はご存知のとおり。クラッシュしたおかげでシステムをしばらく使わずに済むようになったユーザの歓声だ。

本を読んだ後も今も私にとって、非常に耳が痛い記述になっている。 この本を読む以前の私は 表面的な開発手法やツールなどについて固執していたように思う。

そんな私だったが、この本の中で出てくる「サーキットブレイカー」などに興味を惹かれ システム開発をしていくうちに私は、なんの因果か「可用性」を預かるSREという立ち位置で働いている。 この本を手に取る機会を得たことに感謝したい。

この本に少し触れてみようと思う

Java エンジニアとして働いている私にとっては幸運なことに、この本で出てくるコードは Java がメインになる。 それも古い構成の Java の話だ ( EJB とかが出てくる)。 しかし、この本は Java を使っていない人にも読んでもらいたい。

この本はJavaだけには留まらず、マイクロサービスでシステム間連携をやっている人にとっては面白い。 実際にこの本の筆者が経験したケーススタディアンチパターンやそれに対する対応のパターンなどが書かれている。 事細かに状況が描かれており、読み物としても面白いものとなっている。

この本にある、いくつかの章についてタイトルを紹介したい。

目次を読んでいるだけでも、興味を惹かれてしまう本になっている。 この本にはこんなタイトルの章もある。

自分の顧客に踏みつけられる、という表現は非常に面白い。 他にも色々面白い表現が使われている。 手にとって中身を読む楽しみとして、取っておいてもらいたい。

少しでも中身が気になったら、是非この本を読んでみてほしい。 「本番用ソフトウェア」の開発・運用に向き合いたいあなたに きっと役に立つはずだ。

最後に

この記事ではJavaエンジニアである私が 「Release It! 本番用ソフトウェア製品の設計とデプロイのために」という本を紹介した。 この本を読んで、仕事への取り組み方が変わったように思う。 紹介してくださった方にはこの本を手に取る機会を得られたことに非常に感謝している。

これからも私は エンジニアとして「本番用ソフトウェア」の開発・運用に関わっていきたい。

私の稚拙な言葉で綴ったこの記事で、「Release It! 本番用ソフトウェア製品の設計とデプロイのために」の魅力は伝わっただろうか? 記事の終わりに次の言葉を引用したい(※)。

※ 「第1章 イントロダクション 1.6 実践的な達人のアーキテクチャ」より

あなたが達人アーキテクトであるなら、強力な弾丸として本書をささげよう。 あなたが象牙の塔のアーキテクトで、まだ読むのを止めていないのなら、本書をきっかけにして抽象化のレベルを何段か降り、ソフトウェアとハードウェアとユーザの交わる決定的に重要な部分に立ち戻ってほしい。 ついに「Release It!」と言う瞬間がきたとき、あなたと、ユーザと、あなたの会社は、象牙の塔のアーキテクトでいるよりずっと幸福になるはずだ。

あなたは、象牙の塔のアーキテクトだろうか?それとも達人アーキテクトだろうか? わたしは、「達人アーキテクト」を目指す一人として、エンジニリングに携わりたい。