ããã§ã¯è€éãªããšã¯äœããããŸããããããããããããããã«ãã¢ã¹ããŒã¢ãã¡ãŒã·ã§ã³ãïŒã¢ã¹ããŒãããµãŒãã¹ã䜿çšããŠïŒèšé²ããŸãããã åèã«ãªãããšãé¡ã£ãŠããŸãã

ã€ã³ã¿ãŒãã§ãŒã¹
ã€ã³ã¿ãŒãã§ã€ã¹ã¯ã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