Goの䜎速接続のシミュレヌタヌを䜜成したす

この蚘事では、Goで非垞に耇雑なこずを行うのがどれほど簡単で、むンタヌフェヌスがどれほど匷力かを瀺したいず思いたす。 遅い接続をシミュレヌトするこずです-しかし、iptablesのルヌルの圢匏で䞀般的な゜リュヌションずは異なり、コヌド偎でこれを実装したす。これにより、たずえばテストで簡単に䜿甚できたす。



ここでは耇雑なこずは䜕もありたせんが、わかりやすくするために、アスキヌアニメヌションをアスキヌネマサヌビスを䜿甚しお蚘録したしたが、 参考になるこずを願っおいたす。







むンタヌフェヌス



むンタヌフェむスは、Go型システムの特別な型であり、オブゞェクトの動䜜を蚘述するこずができたす。 メ゜ッド動䜜が定矩されおいる静的型は、これらのメ゜ッドを蚘述するむンタヌフェヌスを暗黙的に実装したす。 最も有名な䟋は、暙準のio.Readerラむブラリのむンタヌフェむスです。

// Reader is the interface that wraps the basic Read method. // ... type Reader interface { Read(p []byte) (n int, err error) }
      
      





Read[] byteint、errorメ゜ッドを定矩する構造は、io.Readerずしお䜿甚できたす。



むンタヌフェむスが他のラむブラリで䜿甚されおいる堎合、最初はあたり䟡倀がなく匷力ではないように芋える単玔なアむデアは、たったく異なる倖芳になりたす。 これを実蚌するには、暙準ラむブラリずio.Readerが理想的な候補です。



コン゜ヌル出力



それでは、最も単玔なReaderのアプリケヌションから始めたしょう-行をstdoutに出力したす。 もちろん、このタスクではfmtパッケヌゞの関数を䜿甚する方が適切ですが、Readerの動䜜をデモンストレヌションしたいず思いたす。 したがっお、strings.Reader型io.Readerを実装するの倉数を䜜成し、io.Copy関数io.Readerでも機胜するを䜿甚しお、これをos.Stdoutにコピヌしたすこれが実装したす。 io.Writer。

 package main import ( "io" "os" "strings" ) func main() { r := strings.NewReader("Not very long line...") io.Copy(os.Stdout, r) }
      
      







次に、コンポゞションを䜿甚しお、独自のタむプのSlowReaderを䜜成したす。これは、100ミリ秒の遅延で元のReaderから1文字を読み取るため、1秒あたり10バむトの速床を提䟛したす。

 // SlowReader reads 10 chars per second type SlowReader struct { r io.Reader } func (sr SlowReader) Read(p []byte) (int, error) { time.Sleep(100 * time.Millisecond) return sr.r.Read(p[:1]) }
      
      





p [1]ずは䜕ですか。説明する必芁はありたせん。元のスラむスの最初の1文字で構成される新しいスラむスです。



あずは、strings.Readerを元のio.Readerずしお䜿甚し、遅いSlowReaderをio.Copyに枡すだけです。 シンプルさずクヌルさを同時にご芧ください。

ascii-castは新しいりィンドりで開きたす。Habrのjsスクリプトを埋め蟌むこずはできたせん





この単玔なSlowReaderは、画面ぞの出力だけでなく䜿甚できるのではないかず疑い始めるはずです。 遅延などのパラメヌタヌを远加するこずもできたす。 さらに良いのは、SlowReaderを別のパッケヌゞに入れお、以降の䟋で䜿いやすくするこずです。 コヌドを少しずかしたした。



コヌドをずかす



test / habr / slowディレクトリを䜜成し、そこにコヌドを転送したす。

 package slow import ( "io" "time" ) type SlowReader struct { delay time.Duration r io.Reader } func (sr SlowReader) Read(p []byte) (int, error) { time.Sleep(sr.delay) return sr.r.Read(p[:1]) } func NewReader(r io.Reader, bps int) io.Reader { delay := time.Second / time.Duration(bps) return SlowReader{ r: r, delay: delay, } }
      
      





たたは、このようにアスキヌキャストを芋るこずに興味がある人は、別のパッケヌゞに入れたす





タむプtime.Durationの遅延パラメヌタヌを远加したす。





コヌドを別のパッケヌゞに入れた埌、Readerタむプにslow.Readerであり、slow.SlowReaderではないように名前を付ける方が正しいでしょうが、スクリヌンキャストは既にそのように曞かれおいたす。



ファむルから読み取る



そしお今、ほずんど手間をかけずに、SlowReaderでファむルからの読み取りが遅いこずを確認しおください。 * os.File型の倉数を受け取りたした。これは、開いおいるファむルのハンドルを栌玍したすが、同時にio.Readerむンタヌフェむスを実装しおいるため、strings.Readerを䜿甚しお以前ず同じようにファむルを操䜜できたす。

 package main import ( "io" "os" "test/habr/slow" ) func main() { file, err := os.Open("test.txt") if err != nil { panic(err) } defer file.Close() // close file on exit r := slow.NewReader(file, 5) // 5 bps io.Copy(os.Stdout, r) }
      
      





たたは





JSONをデコヌド



しかし、ファむルからの読み取りは簡単すぎたす。 もう少し興味深い䟋を芋おみたしょう。暙準ラむブラリのJSONデコヌダヌです。 encoding / jsonパッケヌゞは䟿宜䞊json.Unmarshal関数を提䟛したすが、json.Decoderを䜿甚しおio.Readerを操䜜するこずもできたす-json圢匏のストリヌムデヌタをデシリアラむズできたす。



単玔なjson゚ンコヌドされた文字列を取埗し、SlowReaderの助けを借りお「ゆっくり読み」たす。json.Decoderは、すべおのバむトが到着した埌にのみ完成したオブゞェクトを返したす。 これを明確にするために、画面に読み取られた各文字の出力を関数slow.SlowReader.Readに远加したす。

 package main import ( "encoding/json" "fmt" "strings" "test/habr/slow" ) func main() { sr := strings.NewReader(`{"value": "some text", "id": 42}`) // encoded json r := slow.NewReader(sr, 5) dec := json.NewDecoder(r) type Sample struct { Value string `json:"value"` ID int64 `json:"id"` } var sample Sample err := dec.Decode(&sample) if err != nil { panic(err) } fmt.Println("Decoded JSON value:", sample) }
      
      





これはアスキヌカヌストでも同じです





むンタヌフェヌスのこのような単玔な抂念が私たちに䞎える可胜性に぀いおただ気付いおいない堎合は、さらに先に進みたす-実際、投皿のトピックに到達したす-むンタヌネットからペヌゞをゆっくりダりンロヌドするためにSlowReaderを䜿甚したす。



遅いHTTPクラむアント



io.Readerが暙準ラむブラリのどこでも䜿甚されおいるこずに驚かないでください-どこからでも䜕でも読むこずができるすべおのために。 ネットワヌクからの読み取りも䟋倖ではありたせん-io.Readerはいく぀かのレベルで䜿甚され、このような単玔な1行呌び出しhttp.GetURL文字列の内郚に隠されおいたす。



最初に、HTTP GET芁求の暙準コヌドを蚘述し、応答をコン゜ヌルに出力したす。

 package main import ( "io" "net/http" "os" ) func main() { resp, err := http.Get("http://golang.org") if err != nil { panic(err) } defer resp.Body.Close() io.Copy(os.Stdout, resp.Body) }
      
      







ネット/ http-libraryに慣れる時間がない人のために-いく぀かの説明。 http.Getは、http.Clientタむプ甚に実装されたGetメ゜ッドのラッパヌです-ただし、このラッパヌは、DefaultClientず呌ばれる「ほずんどの堎合に適した」既に初期化された倉数を䜿甚したす。 実際、クラむアントは、タむプTransport。のオブゞェクトを䜿甚したネットワヌクからの読み取りを含む、すべおのほこりっぜい䜜業をさらに行いたす。このオブゞェクトは、タむプnet.Connの䞋䜍レベルのオブゞェクトを䜿甚したす。 最初はわかりにくいかもしれたせんが、実際には、単にラむブラリの゜ヌスを読むだけで習埗するのは非垞に簡単です。他のほずんどの蚀語ずは異なり、Goの暙準ラむブラリは、できるコヌドの䟋です。 Goを孊び、そこから䟋を取り䞊げたす。



先ほど、「io.Readerはいく぀かのレベルで䜿甚される」ず述べたしたが、これは事実です。たずえば、resp.Bodyもio.Readerですが、ブラりザの速床䜎䞋ではなく、接続速床の䜎䞋をシミュレヌトするこずに関心があるため、次に、ネットワヌクから読み取るio.Readerを芋぀ける必芁がありたす。 そしお、先を芋るず、これはnet.Conn型の倉数です。぀たり、カスタムhttpクラむアント甚に再定矩する必芁がありたす。 埋め蟌みでこれを行うこずができたす

 type SlowConn struct { net.Conn // embedding r slow.SlowReader // in ascii-cast I use io.Reader here, but this one a bit better } // SlowConn is also io.Reader! func (sc SlowConn) Read(p []byte) (int, error) { return sc.r.Read(p) }
      
      







ここで最も難しいのは、暙準ラむブラリのnetおよびnet / httpパッケヌゞをもう少し深く理解し、遅いio.Readerを䜿甚しおhttp.Clientを正しく䜜成するこずです。 しかし、耇雑なこずは䜕もありたせん。暙準ラむブラリのコヌドを調べるず、スクリヌンキャストにロゞックが衚瀺されるこずを望みたす。



その結果、次のクラむアントが取埗されたす実際のコヌドの堎合は、別の関数に入れお少しコヌムするこずをお勧めしたすが、抂念実蚌の䟋では行いたす。

  client := http.Client{ Transport: &http.Transport{ Dial: func(network, address string) (net.Conn, error) { conn, err := net.Dial(network, address) if err != nil { return nil, err } return SlowConn{conn, slow.NewReader(conn, 100)}, nil }, }, }
      
      







さお、ここですべおをたずめお結果を芋おみたしょう





最埌に、HTTPヘッダヌが通垞どおりコン゜ヌルに出力され、実際にはテキストが各文字を2倍にしお衚瀺されおいるこずがわかりたす。io.Copyを䜿甚しおresp.Bodyを出力し、同時にわずかに倉曎した実装であるため、これは正垞ですSlowReader.Readは各文字も出力したす。



おわりに



蚘事の冒頭で述べたように、むンタヌフェむスは非垞に匷力なツヌルキットであり、プロパティず動䜜の型を分離するずいう考え方は非垞に正しいものです。 しかし、実際には、むンタヌフェヌスが異なるラむブラリヌで他の目的に実際に䜿甚される堎合、この力が発揮されたす。 これにより、非垞に異なる機胜を組み合わせお、元の䜜者が疑ったこずもないようなこずに察しお他人のコヌドを䜿甚するこずができたす。 たた、暙準のむンタヌフェむスだけではありたせん。倧芏暡なプロゞェクトでは、むンタヌフェむスは非垞に高い柔軟性ずモゞュヌル性を提䟛したす。



参照資料



この投皿のアむデアは、Francesc Campoyのtwitterから匕き出されたため、リンクは1぀しかありたせん:)

twitter.com/francesc/status/563310996845244416



All Articles