---
title: Goでヤフーの分散オブジェクトストレージを作った話 Go Conference 2017 Spring
tags: 
author: [Yahoo!デベロッパーネットワーク](https://www.docswell.com/user/ydnjp)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/YJ6NNQKGJV.jpg?width=480
description: Go Conference 2017Springの発表資料です。 Yahoo! JAPANで開発しているGo製オブジェクトストレージ「Dragon」の紹介と、Dragonで利用している耐障害性向上のためのテクニックについて説明します。
published: March 27, 17
canonical: https://www.docswell.com/s/ydnjp/KY6N35-2017-03-27-122818
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/YJ6NNQKGJV.jpg)

Goでヤフーの分散オブジェクトストレージを作った話
Go Conference 2017 Spring
Yasuharu GOTO (@ono_matope)
2017/03/25
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 2

![Page Image](https://bcdn.docswell.com/page/GJ555RPXJ4.jpg)

About me
名前: Yasuharu GOTO
Twitter: @ono_matope
Github: @matope
所属: ヤフー株式会社 データプラットフォーム開発本部
Go歴：3年
コントリビューション：
Expect:100-Continueのクライアント実装 (Go1.6)
2
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 3

![Page Image](https://bcdn.docswell.com/page/LE3NNP34E5.jpg)

Agenda
•
•
3
Goでヤフーの基盤ストレージ Dragon を作った話
Goでの耐障害性向上テクニック
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 4

![Page Image](https://bcdn.docswell.com/page/8EDWWL457G.jpg)

Dragon
0 1 7 Yahoo
Yaho o Japan
Jap anCorporation.
Co rp o ratio n.AllAll
Rig hts
Reserved .
Co
p yrig ht ©
Copyright
Rights
Reserved.
© 22017


# Page. 5

![Page Image](https://bcdn.docswell.com/page/V7P992MDJ8.jpg)

Dragon
5
•
ヤフーで開発している分散オブジェクトストレージ
•
•
•
デザインゴール：高速、高スケーラビリティ、高可用性、低コスト
Go言語
S3 互換 API
•
2016年1月 リリース
(14ヶ月の本番稼働実績)
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 6

![Page Image](https://bcdn.docswell.com/page/2JVMMD9GJQ.jpg)

Why we built a new Object Storage?
•
Octagon(2011-)
•
•
•
既存のOSS？
•
•
•
Riak CS : 一部で導入するも、性能がサービス要件を満たさず
OpenStack Swift : スケーラビリティに不安
パブリッククラウド？
•
6
最初の内製オブジェクトストレージ
諸々の技術的課題から、代替を検討
•
遅い・不安定・運用しづらい・レガシー・etc...
自社DCと比べてコスト面で不利
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 7

![Page Image](https://bcdn.docswell.com/page/5EGGGY4DEL.jpg)

Why we built a new Object Storage?
じゃあ作ろう
2014年 実装スタート
7
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 8

![Page Image](https://bcdn.docswell.com/page/4JQXXMGXEP.jpg)

利用規模
クラスタ数: 2
格納オブジェクト数: 100億
格納データ量: 9PB
サービス利用多数(右)
その他社内システム多数
Presto (in experiment)
8
Co p yrig ht © 2 0 1 7
Yahoo!オークション (画像)
Yahoo!ニュース・トピックス/個人 (画像)
Yahoo!ディスプレイアドネットワーク (画像/動画)
Yahoo!ブログ (画像)
Yahoo!スマホきせかえ (画像)
Yahoo!トラベル (画像)
Yahoo!不動産 (画像)
Yahoo!知恵袋 (画像)
Yahoo!飲食店予約 (画像)
Yahoo!みんなの政治 (画像)
Yahoo!ゲーム (コンテンツ)
Yahoo!ブックストア (コンテンツ)
Yahoo!ボックス (データ)
ネタりか (記事画像)
etc...
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 9

![Page Image](https://bcdn.docswell.com/page/K7466V52E1.jpg)

Performance (with Riak CS/参考値)
GET Object 10KB Throughput
PUT Object 10KB Throughput
3500
1000
900
800
2500
2000
Riak CS
1500
Dragon
1000
Requests / sec
Requests / sec
3000
700
600
500
Dragon
300
200
500
100
0
0
1
9
Riak CS
400
5
10
50
100
# of Threads
200
400
1
5
•
Dragon: API*1, Storage*1,Cassandra*3
•
Riak CS: haproxy*1, stanchion*1, Riak (KV+CS)*3
•
CassandraとStanchion以外はすべて同一構成のHWを使用。
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .
10
50
100
# of Threads
200
400


# Page. 10

![Page Image](https://bcdn.docswell.com/page/LJ1VV3WKEG.jpg)

Architecture
HTTP (S3 API)
API Nodes
Meta DB
Metadata
Blob
Storage Cluster
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
...
10
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.


# Page. 11

![Page Image](https://bcdn.docswell.com/page/GJWLLP6PE2.jpg)

Architecture
HTTP (S3 API)
API Nodes
Meta DB
Metadata
Blob
Storage Cluster
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
...
11
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.


# Page. 12

![Page Image](https://bcdn.docswell.com/page/4EZMMKY6J3.jpg)

Upload
HTTP PUT
API Nodes
格納位置を含む
オブジェクトメタデータ
Meta DB
HTTP PUT
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
...
12
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.


# Page. 13

![Page Image](https://bcdn.docswell.com/page/Y76NNQDL7V.jpg)

Download
HTTP GET
API Nodes
格納位置を含む
オブジェクトメタデータ
Meta DB
HTTP GET
Storage Cluster
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
Storage Node
...
13
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.


# Page. 14

![Page Image](https://bcdn.docswell.com/page/G7555R3M74.jpg)

Architecture
• シンプル is ベスト
• ブラックボックスを減らす
• メタDBとしてCassandraを利用
• 十分な可用性とスケーラビリティ
• 他にもいろいろな工夫が
• 今日は省略
14
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 15

![Page Image](https://bcdn.docswell.com/page/9J2NNDZRER.jpg)

Go Failure Tlerance Tips:
1. Circuit Breaker
2. Timeout for Streaming
0 1 7 Yahoo
Yaho o Japan
Jap anCorporation.
Co rp o ratio n.AllAll
Rig hts
Reserved .
Co
p yrig ht ©
Copyright
Rights
Reserved.
© 22017


# Page. 16

![Page Image](https://bcdn.docswell.com/page/DEYPP2R5EM.jpg)

Go Tips 1: Circuit Breaker
0 1 7 Yahoo
Yaho o Japan
Jap anCorporation.
Co rp o ratio n.AllAll
Rig hts
Reserved .
Co
p yrig ht ©
Copyright
Rights
Reserved.
© 22017


# Page. 17

![Page Image](https://bcdn.docswell.com/page/VJN558D478.jpg)

Circuit Breaker
Storage Nodeが障害で停止・ネットワーク断の場合
• ストレージへのリクエストがコネクションタイムアウトの間ブロック
• 他ノードにフェイルオーバーするまでのレイテンシがユーザーリクエストに影響
• 落ちているノードへのリクエストは避けたい
数秒間ブロック
数秒間ブロック
API Node
17
😢
Storage Nodes
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 18

![Page Image](https://bcdn.docswell.com/page/YE9YYKG4J3.jpg)

Circuit Breaker
Remote Server
Circuit Breaker
Circuit Breakerパターン
ある処理のエラー頻度が閾値をこえたら、
しばらくは処理を省略(Circuit Open)して
即座にエラーを返すパターン
Success
Error!(1)
タイムアウト待ちを省略してエラーを返せる
Error!(2)
Error!(3)
Circuit Open!
Trip
Trip
18
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 19

![Page Image](https://bcdn.docswell.com/page/GE866RVV7D.jpg)

Circuit Breaker
http://github.com/rubyist/circuitbreaker
19
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 20

![Page Image](https://bcdn.docswell.com/page/LEL99K5RJR.jpg)

Circuit Breaker
Circuit
Breakers
Dragonでは、Circuit Breakerを
HTTPクライアントのDialContextに適用
Storage Nodes
node1
node2
• 接続先アドレスのDialをCBで管理
• n回連続でDialに失敗したNodeは
Circuit Openし、一定期間Dialしない
• 即座にフォールバック可能
node3
node4
node5
API Node
20
Co p yrig ht © 2 0 1 7
client := http.Client{
Transport: &amp;http.Transport{
DialContext:
(&amp;CircuitDialer{}).DialContext,
},
}
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 21

![Page Image](https://bcdn.docswell.com/page/4JMZZ23PEW.jpg)

Circuit Breaker
type CircuitDialer struct {
mu
sync.Mutex
dialer
net.Dialer
breakers map[string]*circuit.Breaker
}
func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
d.mu.Lock()
if d.breakers == nil {
d.breakers = map[string]*circuit.Breaker{}
}
if _, ok := d.breakers[addr]; !ok {
d.breakers[addr] = circuit.NewConsecutiveBreaker(4)
}
cb := d.breakers[addr]
d.mu.Unlock()
err = cb.CallContext(ctx, func() error {
conn, err = d.dialer.DialContext(ctx, network, addr)
return err
}, 0)
return conn, err
}
21
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 22

![Page Image](https://bcdn.docswell.com/page/PJR66MQY79.jpg)

Circuit Breaker
type CircuitDialer struct {
mu
sync.Mutex
dialer
net.Dialer
breakers map[string]*circuit.Breaker
}
接続先ごとにCircuitBreakerを用意
func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
d.mu.Lock()
if d.breakers == nil {
d.breakers = map[string]*circuit.Breaker{}
}
if _, ok := d.breakers[addr]; !ok {
d.breakers[addr] = circuit.NewConsecutiveBreaker(4)
}
cb := d.breakers[addr]
d.mu.Unlock()
err = cb.CallContext(ctx, func() error {
conn, err = d.dialer.DialContext(ctx, network, addr)
return err
}, 0)
return conn, err
}
22
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 23

![Page Image](https://bcdn.docswell.com/page/PEXMMV547X.jpg)

Circuit Breaker
type CircuitDialer struct {
mu
sync.Mutex
dialer
net.Dialer
breakers map[string]*circuit.Breaker
}
接続先ごとにCircuitBreakerを用意
func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
d.mu.Lock()
if d.breakers == nil {
d.breakers = map[string]*circuit.Breaker{}
}
接続先のCircuitBreaker
if _, ok := d.breakers[addr]; !ok {
がなければ作成
d.breakers[addr] = circuit.NewConsecutiveBreaker(4)
}
cb := d.breakers[addr]
d.mu.Unlock()
err = cb.CallContext(ctx, func() error {
conn, err = d.dialer.DialContext(ctx, network, addr)
return err
}, 0)
return conn, err
}
23
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 24

![Page Image](https://bcdn.docswell.com/page/3EKGGQ6PJD.jpg)

Circuit Breaker
type CircuitDialer struct {
mu
sync.Mutex
dialer
net.Dialer
breakers map[string]*circuit.Breaker
}
接続先ごとにCircuitBreakerを用意
func (d *CircuitDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) {
d.mu.Lock()
if d.breakers == nil {
d.breakers = map[string]*circuit.Breaker{}
}
接続先のCircuitBreaker
if _, ok := d.breakers[addr]; !ok {
がなければ作成
d.breakers[addr] = circuit.NewConsecutiveBreaker(4)
}
cb := d.breakers[addr]
d.mu.Unlock()
err = cb.CallContext(ctx, func() error {
conn, err = d.dialer.DialContext(ctx, network, addr)
return err
}, 0)
return conn, err
}
24
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .
DialContextにCircuitBreakerを適用


# Page. 25

![Page Image](https://bcdn.docswell.com/page/L73NNP6475.jpg)

Circuit Breaker
Circuit Breakerパターンにより、ノード障害時のリクエストレイテンシを保護
Circuit
Dialer
😄
25
API Node
Storage Nodes
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 26

![Page Image](https://bcdn.docswell.com/page/87DWWLV5JG.jpg)

Go Tips 2: I/O Timeout for Streaming
0 1 7 Yahoo
Yaho o Japan
Jap anCorporation.
Co rp o ratio n.AllAll
Rig hts
Reserved .
Co
p yrig ht ©
Copyright
Rights
Reserved.
© 22017


# Page. 27

![Page Image](https://bcdn.docswell.com/page/VJP992VDE8.jpg)

Download
単純化したダウンロードハンドラ実装
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
resp, err := s.HTTPClient.Get(blobURL)
if err != nil {
return err
}
defer resp.Body.Close()
}
27
_, err = io.Copy(w, resp.Body)
return err
Co p yrig ht © 2 0 1 7
バックエンドストレージに
HTTP GETリクエストを発行
ストレージからのレスポンスボディを
io.CopyでResponseWriterに転送
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 28

![Page Image](https://bcdn.docswell.com/page/2EVMMDZGEQ.jpg)

Download
•
io.Copy(dest io.Writer, src io.Reader)
•
src (io.Reader) を dest (io.Writer) にコピーする関数
•
内部では32KBバッファ bufを確保し、
src.Read(buf), dest.Write(buf)を繰り返し呼ぶ
io.Copy
ResponseWriter
dest.Write()
src.Read()
API Node
28
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .
GET Response.Body


# Page. 29

![Page Image](https://bcdn.docswell.com/page/57GGGYMD7L.jpg)

Case1: Storage Blocking on Download
1.
もしストレージノードにNW障害やHW障害が起こると、
Response.Bodyが流れてこなくなる
2.
io.Copy()内のsrc.Read()が無限にブロックする
3.
ダウンロード転送が止まる！
io.Copy
ResponseWriter
dest.Write()
src.Read()
API Node
😢
29
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .
GET Response.Body


# Page. 30

![Page Image](https://bcdn.docswell.com/page/4EQXXMRX7P.jpg)

Case2: Client Blocking on Download
1.
逆に、何らかの問題で、クライアントがレスポンスのダウンロードを止めると、
ResponseWriter.Write() が無限にブロックする
2.
io.Copy()が進まず、ダウンロード転送が止まる
3.
リソースリーク！
io.Copy
ResponseWriter
dest.Write()
src.Read()
GET Response.Body
API Node
😢
30
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 31

![Page Image](https://bcdn.docswell.com/page/KJ466V8271.jpg)

Upload
アップロードもio.Copyを使っている。
src: クライアントRequest.Body
dest: 3ノードへのPUTリクエストのBody(MultiWriterとPipeを経由)
io.Copy
Request.Body
src.Read()
dest.Write()
Multi
Writer
Writer – Pipe - Reader
PUT Request.Body
Writer – Pipe - Reader
PUT Request.Body
Writer – Pipe - Reader
PUT Request.Body
API Node
31
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 32

![Page Image](https://bcdn.docswell.com/page/LE1VV32K7G.jpg)

Case3: Storage Blocking on Upload
1.
ストレージノードに障害が起こると、リクエストBodyが送れなくなる
io.Copy()内のsrc.Read()が無限にブロックする
2.
アップロード転送が止まる！
io.Copy
Request.Body
😢
32
src.Read()
dest.Write()
Multi
Writer
Writer – Pipe - Reader
PUT Request.Body
Writer – Pipe - Reader
PUT Request.Body
Writer – Pipe - Reader
PUT Request.Body
API Node
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 33

![Page Image](https://bcdn.docswell.com/page/GEWLLPRP72.jpg)

Timeout for Stream
•
まとめると…
•
ストレージ、クライアントどちらかでデータ転送が止まると、
Read()またはWrite()が無限にブロックする
ダウンロード、アップロードのストリームが止まったままになる
リソースリークが起こる
•
•
33
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 34

![Page Image](https://bcdn.docswell.com/page/47ZMMKR6E3.jpg)

Timeout for Stream
•
34
なんとかしてI/Oのブロックを検知して、
タイムアウトエラーとしてハンドリングしたい
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 35

![Page Image](https://bcdn.docswell.com/page/YJ6NNQRLJV.jpg)

Timeout for Stream
•
net.Conn.SetDeadline() ?
•
net.ConnのRead(),Write()に時間制限を指定する機能
•
http.ServeHTTPはクライアントのnet.Connにアクセスできない
•
http.TimeoutHandler ?
•
データサイズやユーザーの通信帯域がバラバラなので
固定のタイムアウト値が設定できない
•
•
The complete guide to Go net/http timeouts
•
35
サイズ：1Byte〜5GB、帯域：100Kbps 〜 10Gbps
https://blog.cloudflare.com/the-complete-guide-to-golang-net-http-timeouts/
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 36

![Page Image](https://bcdn.docswell.com/page/GJ555R8MJ4.jpg)

Our approach
•
タイムアウト機能付きの io.Reader, io.Writerを実装して、
すべてのストリーム経路に仕掛ける
Timeout
Writer
Request
.Body
Timeout
Writer
Request
.Body
Timeout
Writer
Request
.Body
Writer – Pipe - Reader
Request.Body
Timeout
Reader
io.Copy
Multi
Writer
On Upload
Writer – Pipe - Reader
Writer – Pipe - Reader
ResponseWriter
Timeout
Writer
GET Response.Body
io.Copy
Timeout
Reader
On Download
API Node
36
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 37

![Page Image](https://bcdn.docswell.com/page/9E2NNDKR7R.jpg)

TimeoutWriter/Reader
• Write()がTimeout時間で完了しなかったら関数 Fn が実行されるWriteラッパー
• シンプル！
• TimeoutReaderも同様に定義
type TimeoutWriter struct {
W
io.Writer
Timeout time.Duration
Fn
func()
}
func (w *TimeoutWriter) Write(p []byte) (int, error) {
timer := time.AfterFunc(w.Timeout, w.Fn)
defer timer.Stop()
return w.W.Write(p)
}
37
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 38

![Page Image](https://bcdn.docswell.com/page/D7YPP2N57M.jpg)

Timeout for Streaming
適用前
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
blob, _ := s.GetBlobStream(ctx, object, byteRange)
n, err := io.Copy(w,blob)
return err
}
38
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 39

![Page Image](https://bcdn.docswell.com/page/VEN558X4J8.jpg)

Timeout for Streaming
適用後
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
blob, _ := s.GetBlobStream(ctx, object, byteRange)
timeoutCh := make(chan struct{}, 1)
resultCh := make(chan resultAndError, 1)
go func() {
tw := TimeoutWriter{
W: w,
Timeout: 10 * time.Second,
Fn: func() { timeoutCh &lt;- struct{}{} },
}
n, err := io.Copy(tw,blob)
resultCh &lt;- resultAndError{n:n, err:err}
}()
select {
case &lt;-timeoutCh:
return RequestTimeoutError
case result := &lt;-resultCh:
return result.err
}
}
39
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .
Copyを別Goroutineに


# Page. 40

![Page Image](https://bcdn.docswell.com/page/Y79YYK84E3.jpg)

Timeout for Streaming
適用後
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
blob, _ := s.GetBlobStream(ctx, object, byteRange)
timeoutCh := make(chan struct{}, 1)
resultCh := make(chan resultAndError, 1)
go func() {
tw := TimeoutWriter{
W: w,
Timeout: 10 * time.Second,
Fn: func() { timeoutCh &lt;- struct{}{} },
}
n, err := io.Copy(tw,blob)
resultCh &lt;- resultAndError{n:n, err:err}
}()
select {
case &lt;-timeoutCh:
return RequestTimeoutError
case result := &lt;-resultCh:
return result.err
}
}
40
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .
ResponseWriterを
TimeoutWriterでラップ。
io.Copyのdestをtwに


# Page. 41

![Page Image](https://bcdn.docswell.com/page/G7866R8VJD.jpg)

Timeout for Streaming
適用後
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
blob, _ := s.GetBlobStream(ctx, object, byteRange)
timeoutCh := make(chan struct{}, 1)
resultCh := make(chan resultAndError, 1)
go func() {
tw := TimeoutWriter{
W: w,
Timeout: 10 * time.Second,
Fn: func() { timeoutCh &lt;- struct{}{} },
}
n, err := io.Copy(tw,blob)
resultCh &lt;- resultAndError{n:n, err:err}
}()
select {
case &lt;-timeoutCh:
return RequestTimeoutError
case result := &lt;-resultCh:
return result.err
}
}
41
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .
Writeが10秒間ブロックしたら
timeoutChに送信
ServeHTTPを抜けると、
wはClose()してCopyはエラーに


# Page. 42

![Page Image](https://bcdn.docswell.com/page/L7L99KRRER.jpg)

Timeout for Streaming
適用後
func (s *Server) Download(w http.ResponseWriter, r *http.Request) error {
// ...
blob, _ := s.GetBlobStream(ctx, object, byteRange)
timeoutCh := make(chan struct{}, 1)
resultCh := make(chan resultAndError, 1)
go func() {
tw := TimeoutWriter{
W: w,
Timeout: 10 * time.Second,
Fn: func() { timeoutCh &lt;- struct{}{} },
}
n, err := io.Copy(tw,blob)
resultCh &lt;- resultAndError{n:n, err:err}
}()
select {
case &lt;-timeoutCh:
return RequestTimeoutError
case result := &lt;-resultCh:
return result.err
}
}
42
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .
io.Copyが終了したら
resultChに送信


# Page. 43

![Page Image](https://bcdn.docswell.com/page/4EMZZ2RP7W.jpg)

Performance Impact?
43
•
io.Read(), io.Write()のタイムアウトをシンプルな実装でハンドルできた😄
•
でも遅いんでしょう?
•
すべてのRead()/Write()にタイマーを仕掛けるなんて…
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 44

![Page Image](https://bcdn.docswell.com/page/PER66MRYJ9.jpg)

Performance Impact?
性能インパクトは僅少
• 100KB ダウンロード スループット: -2% 〜 0%
• 100KB アップロード スループット: +3% 〜 -5%
GET Object 100KB Throughput
PUT Object 100KB Throughput
12000
4000
3500
8000
6000
No Timeout
4000
Timeout
2000
3000
2500
2000
No Timeout
1500
Timeout
1000
500
0
0
20
44
Requests / sec
Requests / sec
10000
50
100
200
# of Threads
400
800
Co p yrig ht © 2 0 1 7
20
50
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .
100
200
# of Threads
400
800


# Page. 45

![Page Image](https://bcdn.docswell.com/page/P7XMMVR4JX.jpg)

Go Failure Tlerance Tips
大規模な分散システムに要求される耐障害性をシンプルに実装
• CircuitBreaker
• Timeout Read/Write
45
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


# Page. 46

![Page Image](https://bcdn.docswell.com/page/37KGGQRPED.jpg)

Conclusion
0 1 7 Yahoo
Yaho o Japan
Jap anCorporation.
Co rp o ratio n.AllAll
Rig hts
Reserved .
Co
p yrig ht ©
Copyright
Rights
Reserved.
© 22017


# Page. 47

![Page Image](https://bcdn.docswell.com/page/LJ3NNP84J5.jpg)

Conclusion
• ヤフーではGoで大規模な分散オブジェクト
ストレージDragonを開発・運用中です
• サービス基盤のモダン化を進行中
• We’re Hiring
• Thank you!
47
Co p yrig ht © 2 0 1 7
Yaho o Jap an Co rp o ratio n. All Rig hts Reserved .


