マイクロサービス時代 のHTTPクライアントの作り方 / how we build our http client for microservice

マイクロサービス時代 のHTTPクライアントの作り方 / how we build our http client for microservice

Go Conference'19 Summer in Fukuokaでの登壇資料です
https://fukuoka.gocon.jp/ja/

53850955f15249a1a9dc49df6113e400?s=128

LINE Developers

July 13, 2019
Tweet

Transcript

  1. 3.
  2. 10.

    Engineering GO࢖͍ͬͯΔͷʁ Metric Agent Time Series Storage Metric API Server

    ͜Ε͔Β࡞Δ΋ͷ΋ ͲΜͲΜGO࠾༻ʂ CLI tool Metric Alert Engine
  3. 25.

    Engineering ϦϞʔτίʔϧ͕ࣦഊ͠ ͯ΋ճ෮Ͱ͖ΔΑ͏ʹͳ Δ ࣮૷ύλʔϯ - Retry - CircuitBreaker ճ෮ੑ

    ίʔϧઌ͕෼ࢄͰ͖ΔΑ ͏ʹͳΔ ࣮૷ύλʔϯ - Load Balancing - Service Discovery ߴՄ༻ੑ ࢲ͕ཉ͍͠ HTTPΫϥΠΞϯτ
  4. 26.

    Engineering • CircuitBreaker, RetryͳͲ͕ἧ͏ύοέʔδͳͲ΋ଘࡏ͢Δ͕ • Ұͭͷػೳ͔͍࣋ͬͯ͠ͳ͍ͷ͕ଟ͍ • ඪ४ͷnet/httpͷΠϯλʔϑΣʔεʹ૬ੑѱ͍ɺίʔυϕʔεͷม ߋ͕ଟ͘ͳΔ •

    BoilerPlate͕ଟ͍ • ύϑΥʔϚϯεྑ͘ͳ͍ • ֎෦dependencies͕ଟ͍ • ྫɿgojek/heimdall, go-kitܥͷϥΠϒϥΠͳͲ ͭ·ΓΧελϜNET/HTTP ΫϥΠΞϯτཉ͍͠
  5. 29.

    Engineering • ඪ४ͷnet/httpͷΠϯλʔϑΣʔεͰ͖Δ͚ͩै͏ • ࢖͏ଆʢϢʔβʣͷมߋΛ࠷খݶʹ͢Δ • net/httpͷϥούϥΠϒϥϦͱͯ͠ • ඞཁͳ΋ͷ͸શ෦ἧ͏ •

    ճ෮ੑɺߴՄ༻ੑͷػೳΛἧ͏ • ϝτϦΫεɺTracingͷػೳΛἧ͏ • ଥ౰ͳσϑΥϧτʢλΠϜΞ΢τͳͲʣ ઃܭࢥߟ
  6. 30.

    Engineering package xhttp type ClientConfig struct {…} func NewClient(config *ClientConfig)

    *Client {} func NewRequestX(method string, addr *AddressGroup, path string, body io.Reader) (*Request, error) func (c *Client) DoX(req *Request, option ...CallOption) (*Response, error) func (c *Client) PostX(addr *AddressGroup, path string, contentType string, body io.Reader, options ...CallOption) (resp *Response, err error) { func (c *Client) PostFormX(addr *AddressGroup, path string, data url.Values, options ...CallOption) (resp *Response, err error) func (c *Client) GetX(addr *AddressGroup, path string, options ...CallOption) (resp *Response, err error) { NET/HTTPͱಉ͡ΠϯλʔϑΣΠε
  7. 31.

    Engineering addrGroup := xhttp.MustNewAddressGroup( //AddressGroup͸͋ͱ΄Ͳ঺հ͠·͢ “dns: //txt:your-domain.com:8080”, xhttp.WithLoadBalancer(xhttp.RoundRobin), ) reqP,

    _ := xhttp.NewRequestX("POST", addrGroup, "/", nil) res, err = client.DoX(reqG) res, err = client.PostFormX(addrGroup, "/", url.Values{}) res, err = client.GetX(addrGroup, "/") ࢖͍ํͷΠϝʔδ
  8. 34.

    Engineering • ීஈ͸ Reverse Proxy (VIP)ͳͲར༻͕ଟ͍ • ৽͍͠αʔϏε௥ՃΛ͢Δͨͼʹ৽͍͠ Reverse Proxyಋೖ͢Δ

    ͷ͕໘౗ • Solution • Client Side Load Balancing • ΫϥΠΞϯτ͸ίʔϧઌͷαʔόάϧʔϓΛࣗ෼Ͱίϯτϩʔ ϧ͢Δ͜ͱ • ΫϥΠΞϯτ͸ίʔϧઌͷαʔόάϧʔϓɺͲΕΛݺͼग़͢ͷ ͔ͷϩʔυόϥϯγϯάϩδοΫΛ࣮૷͢Δ͜ͱ αʔόάϧʔϓ͕ඞཁ
  9. 35.

    Engineering type Address struct { Port string Host string }

    type AddressGroup struct { scheme Scheme name string resolver Resolver loadBalancer LoadBalancer cacheAddress []Address closeCh chan interface{} } AddressGroup ʢαʔόάϧʔϓͷந৅Խʣ
  10. 36.

    Engineering type Resolver interface { Scheme() Scheme Init(address string) error

    UpdateCh(context.Context) <-chan []Address Close() } type LoadBalancer interface { Pick() (Address, error) HandleStateChange(list []Address) error } Resolver + LoadBalancer
  11. 37.

    Engineering type DnsResolver struct { domain string updateCh chan []Address

    doneCh chan interface{} } func (d *DnsResolver) Scheme() Scheme { return Scheme("dns") } func (d *DnsResolver) Init(name string) error { d.resolve() } func (d *DnsResolver) resolve() { go func() { // resolve d.domain d.updateCh <- addresses time.Sleep(TTL) } } Example: DnsResolve
  12. 38.

    Engineering type roundRobinLB struct { list []Address next uint32 mu

    sync.RWMutex } func (rr *roundRobinLB) Pick() (Address, error) { l := len(rr.list) if l == 0 { return Address{}, fmt.Errorf("empty list of address") } v := atomic.AddUint32(&rr.next, 1) return rr.list[int(v)%len(rr.list)], nil } func (rr *roundRobinLB) HandleStateChange(list []Address) error { rr.mu.Lock() defer rr.mu.Unlock() rr.list = list return nil } Example: Round Robin LB
  13. 40.

    Engineering • Load Balancing is impossible • https://www.youtube.com/watch?v=kpvbOzHUakA • Round

    Robin ͸ Upstreamϗετͷঢ়ଶʢLatency, Φʔόϩʔυঢ় گʣͳͲΛҙࣝͰ͖ͣɺͣͬͱಉ͡ѱ͍ঢ়ଶͷϗετʹಉ͡ϦΫΤε τ਺Λ౤͛Δɹ • P2C ΞϧΰϦζϜ • Power of 2 Random Choice • Upstreamͷঢ়ଶΛ఻ୡඞཁ͋Δ • https://www.eecs.harvard.edu/~michaelm/postscripts/mythesis.pdf ༨ஊ ϩʔυόϥγϯάΞϧΰϦζϜ͸೉͍͠
  14. 44.

    Engineering • ׬શʹࢮ͵͡Όͳͯ͘ɺ(GCͳͲͷཧ༝ͰʣLatency͕௓Ͷ্͕Δ • ద੾ͳσϑΥϧτλΠϜΞ΢τ͕ඞཁ • ଈ࣌తͳϦΫΤετࣦഊ (500ΤϥʔͳͲʣ • ద੾ͳϦτϥΠ

    • ܧଓతͳϦΫΤετࣦഊ (ωοτϫʔΫࣦഊͳͲʣ • Ͱ͖Δ͚ͩૣ͍ஈ֊ͰϦΫΤετΛυϩοϓ͢Δ • CircuitBreakerύʔλϯɿFail Fast Error αʔό͕ࢮ͵ͱ͖Ͳ͏͠·͢ʁ
  15. 45.

    Engineering • net/http͸σϑΥϧτλΠϜΞ΢τ͋Γ·ͤΜʂ • ແݶʹϦΫΤετ଴ͭ • xhttpͰ͸σϑΥϧτͰ30sઃఆՄೳɺ֎͔Β౉͢ͷ΋Մೳ λΠϜΞ΢τʹ͍ͭͯ func NewClient(config

    *ClientConfig) *Client { client.client = &http.Client{ Timeout: config.RequestTimeout, Transport: newRoundTripper(config.RoundTripper, config.MetricRegistry), }… }
  16. 46.

    Engineering Dial TLS Handshake Request Resp Headers Resp Body Idle

    Client.Do http.Client.Timeout net.Dialer.Timeout http.Transport.TLSHandshake.Timeout http.Transport.ResponseHeader.Timeout http.Transport.IdleConn.Timeout https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/ ৔߹ʹΑΓࡉ͔͘ίϯτϩʔϧՄೳ
  17. 47.

    Engineering • Լखʹretry͢Δ͜ͱʹࣾ಺αʔϏεΛDDOS͢Δ͜ͱ • ඞͣ Backoff ( Exponential )͢Δ͜ͱ •

    Retry͸ϦΫΤετείʔϓͷOption (ClientείʔϓͰ͸͋Γ·ͤΜʣ RETRYʹ͍ͭͯ res, err = client.DoX(reqG, xhttp.WithRetry(&xhttp.RetryOptions{ MaxAttempts: 2, RetryOn: func(response *http.Response) bool { return response.StatusCode != 200 }, TimeoutPerAttempt: time.Second, }))
  18. 48.

    Engineering • ૣ͍ஈ֊Ͱυϩοϓ͢Δख๏ͷҰͭͱͯ͠͸ CircuitBreakerύλʔϯɹ • https://engineering.linecorp.com/ja/blog/circuit-breakers-for- distributed-services/ • ϗετͷঢ়ଶΛʮࣦഊ͢ΔϦΫΤετͷ਺ʯͰ൑அ •

    ʮࣦഊʯఆٛࣗମ͸Ϣʔβʹ೚ͤΔ • ঢ়ଶʹΑΓʮϦΫΤετՄೳʯͱʮϦΫΤετෆՄೳʯͰ෼͚Δ • ϦΫΤετෆՄೳͷ৔߹୯७ʹ FailFastErr ฦ͢ FAIL FASTରࡦʹ͍ͭͯ
  19. 49.

    Engineering • ׂͱύϥϝʔλ͕ଟ͍ͷͰɺΞϧΰϦζϜࣗମΛཧղ͢Δ্ઃఆ͢Δ ඞཁ͕͋Δ CIRCUIT BREAKER type CircuitBreakerConfig struct {

    noop bool FailureRateThreshold float64 MinimumRequestThreshold int64 TrialRequestInterval time.Duration CircuitOpenWindow time.Duration CounterSlidingWindow time.Duration CounterUpdateInterval time.Duration IsFailed FailedJudger KeyBuilder KeyBuilder } client := xhttp.NewClient(&xhttp.ClientConfig{ CircuitBreakerConfig: &xhttp.CircuitBreakerConfig{}, })
  20. 50.

    Engineering • > ϗετͷঢ়ଶΛʮࣦഊ͢ΔϦΫΤετͷ਺ʯͰ൑அ • Ͳ͏΍ͬͯૣ͘਺͑Δʁ ༨ஊɹHIGH PERFORMANCE CIRCUITBREAKERͷͨΊʹ var

    c int64 c ++ var c atomic.Int64 c.Add(1) • Atomic࢖ͬͯ΋contentionมΘΒͳ͍ʢಉ͡ม਺ʹࢀর͢Δ͜ͱʣ • https://github.com/linxGnu/go-adder • Java LongAdderͷΞΠσΟΞʢatomicΛsharding) • Ϛϧνϧʔνϯ؀ڥͰatomic.Int64ΑΓ2~4ഒૣ͍
  21. 52.

    Engineering • ObservabilityνʔϜͳͷͰͦͷॏཁੑΛ͘͢͝ཧղ͍ͯ͠Δ • ౴͑ͳ͍ͱ͍͚ͳ͍ͷ͸ • ϦΫΤετLatencyߴ͍Ͱ͔͢ʁ • Latencyߴ͍࣌͸ͳͥʁ OBSERVABILITYͷॏཁੑ

    • Latencyߴ͍͔Ͳ͏͔͸MetricsͰ౴͑Δ • Metrics Instrumentation ͸ prometheus client ࢖͏ • ͳͥʁΛ౴͑Δͷ͸ Metrics + TracingͰ౴͑Δ • Tracing Instrumentation ͸ zipkin-go
  22. 53.

    Engineering ROUNDTRIPPERར༻ͯ͠INSTRUMENT type ClientConfig struct { MetricRegistry *prometheus.Registry Tracer *zipkin.Tracer

    …… } func newRoundTripper(originRt http.RoundTripper, mr *prometheus.Registry, tc *zipkin.Tracer) *roundtripper { … .. } client.client = &http.Client{ Timeout: config.RequestTimeout, Transport: newRoundTripper(config.RoundTripper, config.MetricRegistry, config.Tracer), }
  23. 56.

    Engineering • Ͱ͖Ε͹ࣾ಺ɺ͋Δ͍͸νʔϜ಺ڞ௨ͷϥΠϒϥϦἧͬͨ΄͏͕ྑ͍ • http client, log package, metrics instrument…

    • ϚΠΫϩαʔϏε͸೉͍͠ɺͰ͖Ε͹ආ͚ͨ΄͏͕ྑ͍ • ͍ۙ͏ͪʹxhttpΛOSS͢Δ • go޷͖Ͱɺେن໛γεςϜΛ࡞Γ͍ͨਓ͸͝࿈བྷ଴͓ͪͯ͠Γ·͢