Golangã®Contextå ¥é
Contextã¨ã¯ä½ãï¼
Golangã«ã¯ context
ã¨ããæ¨æºããã±ã¼ã¸ãããã¾ãï¼ä»¥åã¯å®é¨ããã±ã¼ã¸ golang.org/x/net/context
ã§ãããGolang1.7ããæ¨æºæ¡ç¨ããã¾ããï¼ãããã¯ãã³ã¼ã«ã°ã©ãã®ä¸æµãã¾ã¨ãã¦ãã£ã³ã»ã«ãããããããªã¯ã¨ã¹ãã¹ã³ã¼ããªå¤ãã³ã¼ã«ã°ã©ãã®ä¸æµã«ä¼æãããããã¨ããå ´åã«ä½¿ç¨ãã¾ãã
Webã¢ããªã±ã¼ã·ã§ã³ãä½ãå ´åã§æ³å®ãã¦ã¿ã¾ãããããªã¯ã¨ã¹ããæ¥ãã¨HTTPãã³ãã©ã¼ãä½ãå¤ãåãåºãã¦é¢æ°ã«æ¸¡ãããã®é¢æ°ãæ´ã«å¥ã®é¢æ°ãå¼ã³ãDBããæ
å ±ãåå¾ãããã¨è«¸ã
ã®å¦çãçµã¦ã¦ã¼ã¶ã¼ã¸ã¬ã¹ãã³ã¹ãè¿ãã¨ãã¾ãããã®æä½ããã®ãã©ãã«ããã£ã¦DBã®ã¬ã¹ãã³ã¹ã極端ã«é
ããªãã¨ã¦ã¼ã¶ã¼ã¯ãã¤ã¾ã§ãå¾
ã¤ãã¨ã«ãªã£ã¦ãã¾ãã®ã§ã¿ã¤ã ã¢ã¦ããããããã§ããï¼ãããçã£å½ã«å®è£
ããã¨channelããã¡ãã¡ã«æã¡åãããããªãã¨ããã¾ããããã©ããªçç±ã§ã¨ã©ã¼ã¨ãªã£ãã®ããã¾ãå¥éä¼ãããããªããã°ãªããªãç大å¤ç
©ãããã®ã§ããããã£ããã®ãã²ã¨ã¾ã¨ãã«æ±ããããã«ããã®ã context
ããã±ã¼ã¸ã§ãã
ãã£ã³ã»ã«ä»¥å¤ã«ã1åã®ãªã¯ã¨ã¹ãã§ãã¡ãã¡ã«æã¡åããããªæ§è³ªã®å¤ãä¸ç·ã«éã¶ãã¨ãã§ãã¾ãï¼ä¾ãã°ã¦ã¼ã¶ã¼IDããã¼ã¯ã³çï¼ãé¢æ°ã®å¼æ°ã§ userID string
ã®ããã«æã¡åãã¦ãããã®ã§ããããã¯ããããä½åº¦ãããã®ã¯ç
©éã«ãªãã®ã§ãã£ã³ã»ã«ã¨åæ§ã«ã²ã¨ã¾ã¨ãã«ãã¦æ±ãæ¹ã便å©ã§ãã
ãã£ã³ã»ã«ã®ä½¿ãæ¹
æåãã£ã³ã»ã«
ctx-with-cancel.go
package main import ( "context" "errors" "log" "sync" "time" ) func main() { ctx, cancel := context.WithCancel(context.Background()) // ãã£ã³ã»ã«æ©è½ä»ãContextãçºè¡ defer cancel() var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() if err := heavyProcess(ctx, 5); err != nil { // 5ç§å¾ã«ã¨ã©ã¼ãèµ·ãã cancel() // ã¨ã©ã¼ãçºçããããã£ã³ã»ã«ãè¡ã log.Printf("Failed to heavy process(1): %s\n", err.Error()) } }() wg.Add(1) go func() { defer wg.Done() if err := heavyProcess(ctx, 10); err != nil { // 10ç§å¾ã«ã¨ã©ã¼èµ·ãã cancel() log.Printf("Failed to heavy process(2): %s\n", err.Error()) } }() wg.Wait() } func heavyProcess(ctx context.Context, waitSec int) error { select { case <-ctx.Done(): return ctx.Err() case <-time.After(time.Duration(waitSec) * time.Second): // æå®ãããæéãçµéãããã¨ã©ã¼ãçºçããã return errors.New("some error") } }
ä¸è¨ã®ã³ã¼ãã§è©¦ãã¦ã¿ã¾ããããã¾ããã£ã³ã»ã«æ©è½ä»ãã®Contextãçºè¡ãããã ctx, cancel := context.WithCancel(context.Background())
ãè¡ãã¾ãã context.Background()
ã¯å¤ããã£ã³ã»ã«æ©è½ãç¡ã空ã®Contextãçºè¡ããã®ã§ãããã«å¯¾ããã£ã³ã»ã«æ©è½ãä»ä¸ãããã¨ãããããªæå³åãã§ããã¡ã½ãããè¦ãã¨ãããéãContextã¯ã¤ãã¥ã¼ã¿ãã«ãªã®ã§ä¸æµåãã«å¤æ´ãå ããå ´åã¯å¿
ãã©ããããå¿
è¦ãããã¾ãã
ããã¦é¢æ° heavyProcess
ã¯ä½ããã®éããå¦çã模ãããã®ã§ç¬¬ä¸å¼æ°ã« context.Context
ãã第äºå¼æ°ã§å¦çã«ãããæéãæå®ãã¾ãããã®heavyProcessã¯ç¬¬äºå¼æ°ã§æå®ãããæéãçµéãã㨠errors.New("some error")
ãè¿å´ããããå¿
ã失æãã¾ãããããgoroutineã§2ã¤èµ·åãã1ã¤ç®ã¯5ç§å¾ã2ã¤ç®ã¯10ç§å¾ã«errorãè¿å´ããã®ã§é常ã§ããã°å®è¡éå§ãã5ç§å¾ã¨10ç§å¾ã«ã¨ã©ã¼ã®ãã°ãåºåãããã¯ãã§ãããããä»åã¯ã¨ã©ã¼ãçºçããã cancel()
ãçºåããããã«ãªã£ã¦ããã®ã§5ç§å¾ã«2ã¤ã®ãã°ãåºã¾ãã
Contextãããã£ã³ã»ã«ã®çºåãåãåãã«ã¯ Done()
ã¡ã½ããã使ç¨ãã¾ãããã®ã¡ã½ããã¯ç©ºstructåã®åä¿¡å°ç¨channelãè¿å´ããã®ã§ããã¨æ¬æ¥ã®å¦çå®äºãselectã§å¾
ã¡åããã°ãã£ã³ã»ã«å¦çãè¡ããã¨ãã§ãã¾ãï¼ãã£ã³ã»ã«ãçºåããã¨ãã®channelãcloseãããï¼ã
Err()
ã¡ã½ããã¯Contextããã£ã³ã»ã«ãããçç±ãå«ã¾ããerrorãè¿å´ããã®ã§ Done()
ã§ã·ã°ãã«ãåãåã£ãå¾ã¯ãããè¿å´ããã®ãåºæ¬ã«ãªãã¾ãã
ã§ã¯å®è¡ãã¦ã¿ã¾ãããããã°ã®æå»ã¨ã¡ãã»ã¼ã¸ã«æ³¨ç®ããã¨æ³å®éã1ã¤ç®ã®ã¨ã©ã¼ãèµ·ããæç¹ã§ã¾ã å¦çä¸ã§ãã2ã¤ç®ã®heavyProcessããã£ã³ã»ã«ããã¦ãããã¨ããããã¾ãã
$ go run main.go 2019/05/03 16:14:41 Failed to heavy process(1): some error 2019/05/03 16:14:41 Failed to heavy process(2): context canceled
ã¿ã¤ã ã¢ã¦ã
ctx-with-timeout.go
package main import ( "context" "errors" "log" "sync" "time" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) // 3ç§ã¿ã¤ã ã¢ã¦ãä»ãContextãçºè¡ defer cancel() var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() if err := heavyProcess(ctx, 5); err != nil { // 5ç§å¾ã«ã¨ã©ã¼ãèµ·ãã cancel() log.Printf("Failed to heavy process(1): %s\n", err.Error()) } }() wg.Add(1) go func() { defer wg.Done() if err := heavyProcess(ctx, 10); err != nil { // 10ç§å¾ã«ã¨ã©ã¼èµ·ãã cancel() log.Printf("Failed to heavy process(2): %s\n", err.Error()) } }() wg.Wait() } func heavyProcess(ctx context.Context, waitSec int) error { select { case <-ctx.Done(): return ctx.Err() case <-time.After(time.Duration(waitSec) * time.Second): // æå®ãããæéãçµéãããã¨ã©ã¼ãçºçããã return errors.New("some error") } }
åºæ¬çãªèãæ¹ã¯æåãã£ã³ã»ã«ã¨åãã§ãããæå®æéãçµéããã¨èªåçã«ãã£ã³ã»ã«ã·ã°ãã«ãçºè¡ããã¾ããå
ç¨ã¯ context.WithCancel
ã§çºè¡ãã¦ãã¾ãããä»åº¦ã¯ context.WithTimeout
ã使ãï¼æ¥æãã¼ã¹ã«ãããå ´å㯠context.WithDeadline
ã使ãã¨æ¥½ï¼ã第äºå¼æ°ã«3ç§ã®å¶éæéãå ãã¾ããããã以å¤ã¯åãå¦çãªã®ã§ãä»åº¦ã¯3ç§å¾ã«ä¸¡æ¹ã®heavyProcessãã¿ã¤ã ã¢ã¦ãããã¯ãã§ãã
ãããå®è¡ãã¦ã¿ãã¨3ç§ã§ã¿ã¤ã ã¢ã¦ãã«ãªã両æ¹ã®goroutineãçµäºãã¦ãããã¨ã確èªã§ãã¾ãã
$ go run main.go 2019/05/03 16:40:51 Failed to heavy process(2): context deadline exceeded 2019/05/03 16:40:51 Failed to heavy process(1): context deadline exceeded
Valueã®ä½¿ãæ¹
ctx-with-value.go
package main import ( "context" "fmt" ) type ctxKey string const ( ctxUserID ctxKey = "UserID" ) func main() { requestHandler("testID") } func requestHandler(userID string) { ctx := context.WithValue(context.Background(), ctxUserID, userID) printUserID(ctx) } func printUserID(ctx context.Context) { userID, ok := ctx.Value(ctxUserID).(string) if !ok { panic("ãªããå¤") } fmt.Printf("UserID: %s\n", userID) }
Contextã«å¤ãæããããå ´å㯠context.WithValue(ctx, key, value)
ã®å½¢ã§è¡ãã¾ããæ¸ãæ¹ã¯ããå¤åçããããã¾ããããåºæ¬çãªèãæ¹ã¯Mapã¨åããKey/Valueæ¹å¼ã§ããä½ããã¼ã¯ç¬èªåãå®ç¾©ããããæå®ãã¦ãã¾ãï¼è©³ç´°ãªçç±ã¯æ¬¡ã®è¦åºãã§è§£èª¬ï¼ãã¾ãContextã®Value㯠interface{}
åã§å¤ãããã¨ããããããåãåãæã¯ã¢ãµã¼ã·ã§ã³ã§æå®ã®åã«ããå¿
è¦ãããã¾ãã
Valueã®ãã¼ã«ããªããã£ãåã使ãã®ã¯éæ¨å¥¨
Contextã¯ä¸æµã«åãã¦æ¬¡ã ã¨åãç¶ããã¦ãããã®ä¸ã¤ã¤ãã¥ã¼ã¿ãã«ãªã®ã§ããã誰ããåããã¼åã§å¤ãä¸æ¸ããã¦ãã¾ã£ãå ´åãã¤ã®éã«ãå¤ãããæ¿ããé¢åãªãã°ãçããã¨ã«ãªãã¾ãããªã®ã§ãã¼åã«ã¯stringçã®ããªããã£ãåã§ã¯ãªãèªããã±ã¼ã¸ã«ããã¹ã³ã¼ããç¡ãç¬èªåãå®ç¾©ãã¦æ±ããã¨ãæ¨å¥¨ããã¦ãã¾ãã
Valueã«ä½ãå«ããä½ãå«ããªãã®ã
Contextããã±ã¼ã¸ã®å ¬å¼ããã¥ã¡ã³ãã«ã¯ä»¥ä¸ã®ããã«è¨è¼ããã¦ãã¾ã *1ã
Use context Values only for request-scoped data that transits processes and APIs, not for passing optional parameters to functions.
æ訳ãã㨠Contextã®Valueã¯ããã»ã¹ã»APIãã¾ããããªã¯ã¨ã¹ãã¹ã³ã¼ããªãã¼ã¿ã§ä½¿ããé¢æ°ã¸ã®ãªãã·ã§ã³ãã©ã¡ã¼ã¿ã¼ã®åã渡ãã¨ãã¦ä½¿ã£ã¦ã¯ãããªãã
ã¨ãã£ãã¨ããã§ããããï¼ç§ãªãã«Contextã¸å«ãã¦ãããã®ã®æ§è³ªã解éãã¦ã¿ã¾ããã
- ãªã¯ã¨ã¹ãã¹ã³ã¼ãä¸ã¤ã¤ãã¥ã¼ã¿ãã«ãªå¤
- ããã»ã¹ãAPIãã¾ããã§ä½¿ããããã®
- ããã§ãªããã®ãã³ã¼ã«ã°ã©ãã®æµãã§åãç¶ãããããã®ç®±ã«å«ããæå³ã¯ç¡ã
- å¦çã®éä¸ã§å¤ãå¤åããªããã®
- åºæ¬çã«ãªã¯ã¨ã¹ãã¹ã³ã¼ããªå¤ã¯å¤ããæ¥ããã®ãªã®ã§ãå¤åãããã®ã¯ããã«åè´ããªãã¯ãã
- ããã»ã¹ãAPIãã¾ããã§ä½¿ããããã®
- ãã¸ãã¯ãå«ã¾ãªããã®ããã¸ãã¯ã«å½±é¿ããªããã®
- Contextã¯ã³ã¼ã«ã°ã©ãã«æ²¿ã£ã¦ãã¼ã¿ãåã渡ãããã®ãã®ã§ããã¸ãã¯ã«å½±é¿ãä¸ãããã®ã¯å¼æ°ã§ããã¨ããã¹ã
- ããã¸ãã¯ã«å½±é¿ãã¨ããã®ã¯å¦çã®æµããã¾ãã£ã¨å¤åããé¡ã®ãã®ã極端ãªä¾ã¨ãã¦ã¯ãã¦ã¼ã¶ã¼ããã©ã¼ã ã§é¸æããå 容ãContextã«å«ãå¾æ®µã§å¦çãå¤ãããããªãã¨ã¯NGã
ã¾ãValueãåããããå㯠interface{}
ã§ããããããæªç¨ãã¦ä½ã§ãæ¾ãè¾¼ããéæ³ã®ç®±ã«ãã¦ãã¾ãã¨å¯èªæ§ãèããä¸ãããããã°ã»ã¡ã³ããã³ã¹æã«å¤§å¤è¦å´ãããã¨ã«ãªãã§ããããContextã¨å¼æ°ã§æ¥ã¦ããã®ããææ§ã§ã¯ããããã®è²¬åãéè¤ãã¦ãã¾ãæ··ä¹±ãæãã¾ãã
ãã®ä»ãç´æäº
- Contextã¯ç¬¬ä¸å¼æ°ã§åã渡ããããã¨
- Contextã®å¤æ°åã¯
ctx
ãããã¯c
ãä¸è¬ç - ä¸æµããæ¥ãContextãä½ãã¾ã ããããªãå ´åã¯æ«å®ã¨ãã¦
context.TODO
ãçºè¡ãã
ã¾ã¨ã
- Contextã¯ãªã¯ã¨ã¹ãã§çºçããé¢é£å¦çãã¾ã¨ãã¦ãã£ã³ã»ã«ã§ããæ©è½ãæä¾ãã
- æéçºåã»ä»»æã®ã¿ã¤ãã³ã°ä¸¡æ¹ã«å¯¾å¿
- Contextã¯ãªã¯ã¨ã¹ãã§å
±éãã¦ä½¿ç¨ãããã¼ã¿ãã¾ã¨ãã¦éæ¬ã§ããæ©è½ãæä¾ãã
- ä½ãä½ã§ãå ¥ãéæ³ã®ç®±ã«ãã¦ã¯ãããªã