Working with TCP Sockets 読書メモ 第7章 ノンブロッキングIO

目次


ノンブロッキングIO

  • ノンブロッキングIOは、コネクションの多重化によって実現できる

ノンブロッキングな読み込み

  • readを使うと、EOFを受け取るまでブロックする
  • readpartialを使えば今読めるデータだけを取得できるが、データが送られないとブロックする
  • 絶対に ブロックしないようにするには、read_nonblockを使用する

  • データが無い場合、Errno::EAGAINの例外が発生する
  • Errno::EAGAINが発生したらretryしている
    • プロダクションで推奨される実装ではないので注意
  • retryの実行を制御する例は以下

  • IO.selectを使うと、connectionから読み込みが可能になるまでブロックする
  • 読み込みが可能になった時点でretryしている
  • この実装では、読み込み可能までブロックするので、readを使うのと変わらない!

読み込みはいつブロックするのか?

  • read_nonblockメソッドはRubyの内部バッファをチェックして保留中のデータがないか確認する
    • select(2)を使って利用可能なデータを確認する
  • 利用可能なデータがある場合はデータが返り、利用可能なデータがない場合はブロックする

ノンブロッキングな書き込み

  • write_nonblockによってノンブロッキングな書き込みを実装できる

  • write_nonblockは、大量のデータ送信等でブロックするような状態に陥った場合、全てのデータを送信せずに送信済みのバイト数を返す
    • 残りのデータは改めて送信する必要がある
  • write_nonblockの挙動はwrite(2)と同じ
  • Rubyのwriteは何度もwrite(2)を呼んで全てのデータを書き込む
  • write_nonblockの制御にもIO.selectが利用できる

  • IO.selectの第2引数にソケット配列を渡すと、書き込み可能になるまでブロックする
  • write_nonblockとpayloadのスライスを繰り返して、全てのデータを送信している

書き込みはいつブロックするのか?

  • write(2)がブロックする可能性があるのは以下の2つの状況
  1. TCPコネクションの受信側に到達するのに時間がかかるとき
  2. 受信側が処理可能なデータ量の限界を超えているとき

ノンブロッキングなaccept

  • 通常のacceptは、キューにコネクションが1つもないとブロックする
  • accept_nonblockは、キューにコネクションがないとErrno::EAGAINを発生させる

ノンブロッキングなconnect

  • connect_nonblockの挙動は、他のxxx_nonblockとは異なる
  • xxx_nonblockは正常に処理を行うか、例外を投げるかのいずれか
  • connect_nonblockは、処理を行い、 かつ 、例外を投げることがある
  • リモートホストと即時に接続ができない場合、バックグラウンドで接続を試みつつErrno::EINPROGRESSを発生させる
  • 詳細なサンプルは次章

『The Pragmatic Programmer』:知識のポートフォリオ

The Pragmatic Programmer: From Journeyman to Master

『達人プログラマー』というタイトルで邦訳されている本の原書を読んでいます。邦訳は絶版で入手が難しいので…。

知識の増やし方について、気になることが書いてあったので、メモしておきます。

知識ポートフォリオ

技術的な知識は、経年劣化する。
プログラマーとして成功を収めるには、資産運用のような考え方で知識のポートフォリオを築き、メンテナンスする必要がある。

投資のポイント

  • 定期的に投資すべし。一度の投資は少しずつでもよい。
  • 多様性。様々なことを知っている方が、価値が高い。
  • リスクマネージメント。1つの技術にだけ投資することは避けるべき。
  • 安く買って高く売れ。これから重要になる技術に早い段階で投資すれば、大きな見返りがある。
  • レビューしてバランスを取れ。廃れた技術は捨てて新しい技術を身に着けよ。

目標

  • 毎年、最低1つは新しい言語を覚える。
  • (少なくとも)4半期に1冊は技術書を読む。
  • 技術書以外も読む。
  • 大学の授業を聴講する(※今だとオンラインのコースを受けるのがよさそう)。
  • 技術のユーザーグループに参加する(※いわゆる勉強会と捉えるのがよいかも)。
  • 異なる環境を試す。例えば、Windowsに慣れているならMacやLinuxも使ってみる。
  • 最新情報を取り入れる。例えば、雑誌やブログを購読する。
  • 他のプログラマーとつながる(※今だとSNSやブログ等を利用するとよさそう)。

新しい技術を覚える際は、それを仕事で使えるかとか、履歴書に書けるかといったことは気にしないでよい。新しい技術の習得によって、思考が広がり、新しいやり方が身につく。

スプラウトメソッドとラップメソッド

レガシーコード改善ガイド (Object Oriented SELECTION)

スプラウトメソッド(Sprout Method)

既存のテストがないメソッドに新しい振る舞いを加える際に、新しく追加する部分を完全に別の操作として扱うこと。
独立した1つの機能としてコードを追加する場合や、まだメソッドのテストを整備していない場合には、スプラウトメソッドの適用が推奨される。

長所

古いコードと新しいコードを明確に区別できる。新しいコードはテストで保護することができる。

短所

元のメソッドやクラスが改善されるわけではない。

スプラウトクラス(Sprout Class)

変更に必要な機能を別のクラスとして切り出し、そのクラスを元のクラスから利用すること。
オブジェクトの依存関係が複雑で、テストでオブジェクトを生成することが困難である場合に用いる。

長所

コードを直接書き換える方法よりも、確信を持って変更を進められる。

短所

仕組みが複雑になる。

ラップメソッド(Wrap Method)

元のメソッドと同じ名前のメソッドを新しく作り、古いコードに処理を委譲すること。

長所

既存のメソッドの長さが変わらないこと。
既存の機能から新しい機能を明確に独立させられること。

短所

追加する新しい機能のロジックを、既存機能のロジックと絡ませられないこと。
古いコードに別名をつける必要があり、不適切な名前をつけてしまいがちなこと。

ラップクラス(Wrap Class)

別のクラスをラップするオブジェクト群を生成し、それらを順に渡す(Decoratorパターン)。
ラップする側のクラスは、ラップ対象のクラスと同じインタフェースを持つ。

長所

既存のクラスに変更を加えずに、システムに新たな振る舞いを追加できること。

短所

仕組みが複雑になる。

『Java言語で学ぶデザインパターン入門』をPHPで実習する Bridge

増補改訂版Java言語で学ぶデザインパターン入門

本記事に掲載したサンプルコードは、https://github.com/ryo-utsunomiya/design_patternでも公開中です。

Bridgeとは

「機能」のクラス階層と「実装」のクラス階層を分離し、それらを独立に変更できるようにすることです。
これによって、「機能」を柔軟に交換できるようになります。

まず、「何かを表示する」という「機能」を持ったDisplayクラスを作成してみます。

このクラスは「何かを表示する」機能の具体的な実装であるDisplayImplに依存しています。DisplayImplは、実装を交換可能にするため抽象クラスにします。

次に、DisplayImplの具象クラスであるStringDisplayImplを実装してみます。

StringDisplayImplでは、文字列を装飾して表示します。StringDisplayImplを変更しなくても、DisplayImplのサブクラスを作れば、表示方法を変更することができます。

次に、表示する「機能」の方を変更してみます。

CountDisplayは、Displayを拡張し、同じ文言を複数回表示するmultiDisplayメソッドを追加しています。

最後に、これらのクラスの使用例です。

このように、機能と実装を分離することで、機能・実装の両方を柔軟に交換可能にするのがBridgeパターンの特長です。

『リファクタリング』のサンプルコード(テストコード付き)をPHPで実装した

新装版 リファクタリング―既存のコードを安全に改善する― (OBJECT TECHNOLOGY SERIES)

『リファクタリング』の第1章では、実際のコードのリファクタリングを体験することができます。本には詳細な手順が載っていますが、やはり手を動かして学びたい、という方も多いのではないでしょうか。

しかし、本書のサンプルコードには、「テストコード」という、リファクタリングを行う前提条件となるものが欠けています。そこで、テストコードを含んだものはないかなと探したところ、tobyweston/Refactoring-Chapter-1というリポジトリに、テストコード込みのものがありました。

ただ、テストコード込みでやるなら慣れ親しんだPHPの方がやりやすいよな、ということで、同様のもののPHP版を実装しました。ryo-utsunomiya/refactoring_php5で公開しています。

上のリポジトリの、masterおよびbefore_refactoringブランチが、リファクタリング前の状態です。ブランチをgit cloneするなりDownload ZIPするなりしてローカルに持ってきて使ってください。

phpunit.pharを同梱しているので、PHP実行環境さえあればテストコードを実行できます。PHPUnitのバージョンの関係で、PHP 5.6以上が必要です。また、テストのカバレッジを出したい場合は、XDebugというPHP拡張をインストールしてください。

リファクタリング前のテストコードのカバレッジは100%になっていますが、リファクタリングを進めていくと100%でなくなる場面が出てきます。その場合、独自にテストを追加していってください。

『リファクタリング』を参考にリファクタリングを行った結果はafter_refactoringブランチに含めています。リファクタリング後の実装はおおむねオリジナルに準拠していますが、集計処理にarray_sumを使う等、「PHPらしい」コードにもなっています。