Slide 1

Slide 1 text

ϚΠΫϩαʔϏε࣌୅ ͷHTTPΫϥΠΞϯτͷ࡞Γํ @dxhuy Observability Team / LINE Corp

Slide 2

Slide 2 text

@dxhuy https://github.com/huydx

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Developer

Slide 5

Slide 5 text

• LINEͷObservabilityνʔϜʹ͍ͭͯ • ϚΠΫϩαʔϏεͷམͱ݀͠ • ಺෦ͰΧελϜͨ͠HTTPΫϥΠΞϯτ ͷઃܭͱ঺հ ࠓ೔ͷ࿩

Slide 6

Slide 6 text

Engineering LINE’S OBSERVABILITY TEAM

Slide 7

Slide 7 text

Engineering OBSERVABILITY IS NEW WAVE

Slide 8

Slide 8 text

Πϝʔδతʹ͸ ࣾ಺DATADOGΛ࡞͍ͬͯΔ

Slide 9

Slide 9 text

Engineering Log ج൫ Metric ج൫ ෼ࢄ τϨʔγ ϯάج൫ Zipkin΁ͷOSS ίϯτϦϏϡʔτ

Slide 10

Slide 10 text

Engineering GO࢖͍ͬͯΔͷʁ Metric Agent Time Series Storage Metric API Server ͜Ε͔Β࡞Δ΋ͷ΋ ͲΜͲΜGO࠾༻ʂ CLI tool Metric Alert Engine

Slide 11

Slide 11 text

> 1.000.000 metrics per sec εέʔϧνϟϨϯδ

Slide 12

Slide 12 text

> 2.000 read per sec εέʔϧνϟϨϯδ

Slide 13

Slide 13 text

Engineering Metrics਺૿Ճ Ϗδωεεέʔϧ

Slide 14

Slide 14 text

Engineering GOPHERੵۃ࠾༻தʂ ιϑτ΢ΣΞΤϯδχΞ (Software EngineerɺEngineering Efficiency)ʲLINEϓϥοτϑΥʔϜʳ

Slide 15

Slide 15 text

Engineering ࠓ೔ͷຊ୊΁

Slide 16

Slide 16 text

Engineering ϚΠΫϩαʔϏε ࢖͍ͬͯ·͔͢

Slide 17

Slide 17 text

Engineering ίϛϡχέʔγϣϯ(RPC) ͕େࣄ

Slide 18

Slide 18 text

Engineering twitch/Twirp GRPC Thrift REST Http RPCख๏͕༷ʑ ࠓޙ΋૿͑ͦ͏

Slide 19

Slide 19 text

Engineering GRPC, THRIFTͳͲ͸શͯͷ γεςϜΛม͑ͳ͍ͱߦ͚ͳ͍

Slide 20

Slide 20 text

Engineering ͢Ͱʹଘࡏ͍ͯ͠Δ APIαʔϏε͕ࠞࡏ net/http͕ఆ൪

Slide 21

Slide 21 text

Engineering NET/HTTP͸ͪΐͬͱ ෺଍Γͳ͍

Slide 22

Slide 22 text

Engineering ϚΠΫϩαʔϏε௨৴ͷ ΫϥΠΞϯτඞཁͳੑ࣭

Slide 23

Slide 23 text

Engineering αʔϏεͷελοΫ ωοτϫʔΫ஗Ԇ ϦΫΤετࣦഊ αʔϏεಥવؾઈ https://www.flickr.com/photos/chrish_99/8526019374/

Slide 24

Slide 24 text

Engineering https://www.flickr.com/photos/chrish_99/8526019374/

Slide 25

Slide 25 text

Engineering ϦϞʔτίʔϧ͕ࣦഊ͠ ͯ΋ճ෮Ͱ͖ΔΑ͏ʹͳ Δ ࣮૷ύλʔϯ - Retry - CircuitBreaker ճ෮ੑ ίʔϧઌ͕෼ࢄͰ͖ΔΑ ͏ʹͳΔ ࣮૷ύλʔϯ - Load Balancing - Service Discovery ߴՄ༻ੑ ࢲ͕ཉ͍͠ HTTPΫϥΠΞϯτ

Slide 26

Slide 26 text

Engineering ● CircuitBreaker, RetryͳͲ͕ἧ͏ύοέʔδͳͲ΋ଘࡏ͢Δ͕ ● Ұͭͷػೳ͔͍࣋ͬͯ͠ͳ͍ͷ͕ଟ͍ ● ඪ४ͷnet/httpͷΠϯλʔϑΣʔεʹ૬ੑѱ͍ɺίʔυϕʔεͷม ߋ͕ଟ͘ͳΔ ● BoilerPlate͕ଟ͍ ● ύϑΥʔϚϯεྑ͘ͳ͍ ● ֎෦dependencies͕ଟ͍ ● ྫɿgojek/heimdall, go-kitܥͷϥΠϒϥΠͳͲ ͭ·ΓΧελϜNET/HTTP ΫϥΠΞϯτཉ͍͠

Slide 27

Slide 27 text

Engineering ࣾ಺ͰΧελϜNET/HTTP ࡞Γ·ͨ͠

Slide 28

Slide 28 text

Engineering XHTTPύοέʔδ ઃܭͱ࢖͍ํͷ঺հ

Slide 29

Slide 29 text

Engineering ● ඪ४ͷnet/httpͷΠϯλʔϑΣʔεͰ͖Δ͚ͩै͏ ● ࢖͏ଆʢϢʔβʣͷมߋΛ࠷খݶʹ͢Δ ● net/httpͷϥούϥΠϒϥϦͱͯ͠ ● ඞཁͳ΋ͷ͸શ෦ἧ͏ ● ճ෮ੑɺߴՄ༻ੑͷػೳΛἧ͏ ● ϝτϦΫεɺTracingͷػೳΛἧ͏ ● ଥ౰ͳσϑΥϧτʢλΠϜΞ΢τͳͲʣ ઃܭࢥߟ

Slide 30

Slide 30 text

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ͱಉ͡ΠϯλʔϑΣΠε

Slide 31

Slide 31 text

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, "/") ࢖͍ํͷΠϝʔδ

Slide 32

Slide 32 text

Engineering ߴՄ༻ੑͷ࿩

Slide 33

Slide 33 text

Upstream 2 die xhttp client Upstream 1 αʔό͸͍ͭͰ΋ࢮ͵Մೳੑ΋ Upstream 3

Slide 34

Slide 34 text

Engineering ● ීஈ͸ Reverse Proxy (VIP)ͳͲར༻͕ଟ͍ ● ৽͍͠αʔϏε௥ՃΛ͢Δͨͼʹ৽͍͠ Reverse Proxyಋೖ͢Δ ͷ͕໘౗ ● Solution ● Client Side Load Balancing ● ΫϥΠΞϯτ͸ίʔϧઌͷαʔόάϧʔϓΛࣗ෼Ͱίϯτϩʔ ϧ͢Δ͜ͱ ● ΫϥΠΞϯτ͸ίʔϧઌͷαʔόάϧʔϓɺͲΕΛݺͼग़͢ͷ ͔ͷϩʔυόϥϯγϯάϩδοΫΛ࣮૷͢Δ͜ͱ αʔόάϧʔϓ͕ඞཁ

Slide 35

Slide 35 text

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 ʢαʔόάϧʔϓͷந৅Խʣ

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

Engineering ࢖͏ଆ͸BOILER PLATE͕΄ͱΜͲͳ͍ addrGroup := xhttp.MustNewAddressGroup( //AddressGroup͸͋ͱ΄Ͳ঺հ͠·͢ “dns: //txt:your-domain.com:8080”, xhttp.WithLoadBalancer(xhttp.RoundRobin), ) reqP, _ := xhttp.NewRequestX("POST", addrGroup, "/", nil) ϥΠϒϥϦଆͰ schemeύʔεͯ͠Resolver ΛΞαΠϯ͢Δ

Slide 40

Slide 40 text

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 ༨ஊ ϩʔυόϥγϯάΞϧΰϦζϜ͸೉͍͠

Slide 41

Slide 41 text

Engineering ճ෮ੑͷ࿩

Slide 42

Slide 42 text

Engineering Upstream 2 die xhttp client Upstream 1 αʔό͸͍ͭͰ΋ࢮ͵Մೳੑ΋ Upstream 3

Slide 43

Slide 43 text

Engineering Upstream 2 die xhttp client Upstream 1 die (શʣαʔό͸͍ͭͰ΋ࢮ͵Մೳੑ΋ Upstream 3 die

Slide 44

Slide 44 text

Engineering ● ׬શʹࢮ͵͡Όͳͯ͘ɺ(GCͳͲͷཧ༝ͰʣLatency͕௓Ͷ্͕Δ ● ద੾ͳσϑΥϧτλΠϜΞ΢τ͕ඞཁ ● ଈ࣌తͳϦΫΤετࣦഊ (500ΤϥʔͳͲʣ ● ద੾ͳϦτϥΠ ● ܧଓతͳϦΫΤετࣦഊ (ωοτϫʔΫࣦഊͳͲʣ ● Ͱ͖Δ͚ͩૣ͍ஈ֊ͰϦΫΤετΛυϩοϓ͢Δ ● CircuitBreakerύʔλϯɿFail Fast Error αʔό͕ࢮ͵ͱ͖Ͳ͏͠·͢ʁ

Slide 45

Slide 45 text

Engineering ● net/http͸σϑΥϧτλΠϜΞ΢τ͋Γ·ͤΜʂ ● ແݶʹϦΫΤετ଴ͭ ● xhttpͰ͸σϑΥϧτͰ30sઃఆՄೳɺ֎͔Β౉͢ͷ΋Մೳ λΠϜΞ΢τʹ͍ͭͯ func NewClient(config *ClientConfig) *Client { client.client = &http.Client{ Timeout: config.RequestTimeout, Transport: newRoundTripper(config.RoundTripper, config.MetricRegistry), }… }

Slide 46

Slide 46 text

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/ ৔߹ʹΑΓࡉ͔͘ίϯτϩʔϧՄೳ

Slide 47

Slide 47 text

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, }))

Slide 48

Slide 48 text

Engineering ● ૣ͍ஈ֊Ͱυϩοϓ͢Δख๏ͷҰͭͱͯ͠͸ CircuitBreakerύλʔϯɹ ● https://engineering.linecorp.com/ja/blog/circuit-breakers-for- distributed-services/ ● ϗετͷঢ়ଶΛʮࣦഊ͢ΔϦΫΤετͷ਺ʯͰ൑அ ● ʮࣦഊʯఆٛࣗମ͸Ϣʔβʹ೚ͤΔ ● ঢ়ଶʹΑΓʮϦΫΤετՄೳʯͱʮϦΫΤετෆՄೳʯͰ෼͚Δ ● ϦΫΤετෆՄೳͷ৔߹୯७ʹ FailFastErr ฦ͢ FAIL FASTରࡦʹ͍ͭͯ

Slide 49

Slide 49 text

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{}, })

Slide 50

Slide 50 text

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ഒૣ͍

Slide 51

Slide 51 text

Engineering METRICS/TRACINGͷ࿩

Slide 52

Slide 52 text

Engineering ● ObservabilityνʔϜͳͷͰͦͷॏཁੑΛ͘͢͝ཧղ͍ͯ͠Δ ● ౴͑ͳ͍ͱ͍͚ͳ͍ͷ͸ ● ϦΫΤετLatencyߴ͍Ͱ͔͢ʁ ● Latencyߴ͍࣌͸ͳͥʁ OBSERVABILITYͷॏཁੑ ● Latencyߴ͍͔Ͳ͏͔͸MetricsͰ౴͑Δ ● Metrics Instrumentation ͸ prometheus client ࢖͏ ● ͳͥʁΛ౴͑Δͷ͸ Metrics + TracingͰ౴͑Δ ● Tracing Instrumentation ͸ zipkin-go

Slide 53

Slide 53 text

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), }

Slide 54

Slide 54 text

Engineering ● ΧελϚΠζ͢ΔͨΊͷϑοΫ ● ϦΫΤετૹΔॲཧΛΧελϚΠζɿ RoundTripperΛ౉͢ ● ίωΫγϣϯΛ࡞ΔॲཧΛΧελϚΠζɿDialContextΛ౉͢ ༨ஊɹNET/HTTP͸୔ࢁ ϑοΫఏڙͯ͘͠ΕͯΔ ● ϝτϦΫεΛऔಘ͢ΔͨΊͷϑοΫ ● http/ClientTrace͸े෼ɹ ● https://blog.golang.org/http-tracing

Slide 55

Slide 55 text

Engineering ࠷ޙ

Slide 56

Slide 56 text

Engineering ● Ͱ͖Ε͹ࣾ಺ɺ͋Δ͍͸νʔϜ಺ڞ௨ͷϥΠϒϥϦἧͬͨ΄͏͕ྑ͍ ● http client, log package, metrics instrument… ● ϚΠΫϩαʔϏε͸೉͍͠ɺͰ͖Ε͹ආ͚ͨ΄͏͕ྑ͍ ● ͍ۙ͏ͪʹxhttpΛOSS͢Δ ● go޷͖Ͱɺେن໛γεςϜΛ࡞Γ͍ͨਓ͸͝࿈བྷ଴͓ͪͯ͠Γ·͢

Slide 57

Slide 57 text

Engineering ͝੩ௌ͋Γ͕ͱ͏͍͟͝·͢