Slide 1

Slide 1 text

Widebullet - Proxying RESTful APIs with JSON-RPC in Go Tatsuhiko Kubo@cubicdaiya Go 1.6 Release Party 2016/02/17

Slide 2

Slide 2 text

@cubicdaiya / Tatsuhiko Kubo Principal Engineer, SRE @ Mercari, Inc. ngx_small_light, ngx_dynamic_upstream, nginx-build, slackboard,cachectl, gaurun, etc…

Slide 3

Slide 3 text

nginxຊΛࣥච͠·ͨ͠ʢ2016/01/16ൃചʣ

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

https://www.mercari.com/ Mercari - Your Friendly Mobile Marketplace

Slide 6

Slide 6 text

Agenda • Use cases Go @ • Widebullet - proxying RESTful APIs with JSON-RPC

Slide 7

Slide 7 text

Use cases for Go @

Slide 8

Slide 8 text

Use cases for Go @ • nginx-build - seamless nginx builder (github.com/cubicdaiya/nginx-build) • cachectl - page cache controller (github.com/cubicdaiya/cachectl) • Slackboard - proxying message to Slack (github.com/cubicdaiya/slackboard) • Gaurun - proxying push notification (github.com/mercari/gaurun) • fluent-agent-hydra - A Fluentd log agent (github.com/fujiwara/fuluent-agent-hydra)

Slide 9

Slide 9 text

Use cases for Go @ • zabbix-cli - CLI tool for Zabbix • CLI tools and plugins for Mackerel • Widebullet - proxying multi API requests • etc… (e.g. internal CLIs and daemons…)

Slide 10

Slide 10 text

Use cases for Go @ • zabbix-cli - CLI tool for Zabbix • CLI tools and plugins for Mackerel • Widebullet - proxying multi API requests • etc… (e.g. internal CLIs and daemons…)

Slide 11

Slide 11 text

Widebullet - Proxying RESTful APIs with JSON-RPC in Go

Slide 12

Slide 12 text

Overview of mercari API call - Before NFSDBSJ "1* Multimedia Corporate data center Traditional server Mobile Client Example: IAM Add-on Requester Workers HTTPS HTTP private network global network

Slide 13

Slide 13 text

Overview of mercari API call - Now NFSDBSJ "1* Multimedia Corporate data center Traditional server Mobile Client Example: IAM Add-on Requester Workers HTTPS HTTP HTTP TVCTZT private network global network

Slide 14

Slide 14 text

Overview of mercari API call - Now NFSDBSJ "1* Multimedia Corporate data center Traditional server Mobile Client Example: IAM Add-on Requester Workers HTTPS HTTP HTTP TVCTZT TVCTZT HTTP private network global network

Slide 15

Slide 15 text

Overview of mercari API call - Now NFSDBSJ "1* Multimedia Corporate data center Traditional server Mobile Client Example: IAM Add-on Requester Workers HTTPS HTTP HTTP TVCTZT TVCTZT HTTP TVCTZT HTTP private network global network

Slide 16

Slide 16 text

Overview of mercari API call - Now NFSDBSJ "1* Multimedia Corporate data center Traditional server Mobile Client Example: IAM Add-on Requester Workers HTTPS HTTP HTTP TVCTZT TVCTZT HTTP TVCTZT HTTP … private network global network

Slide 17

Slide 17 text

Performance reduction due to API mismatch • mercari API is not always constructed optimally for every situation • some subsystem must call multiple APIs at once • Call APIs concurrently for high performance • BTW, some subsystem is powered by PHPʂ

Slide 18

Slide 18 text

Call APIs concurrently in PHP • We tried with Guzzle ( internally curl_exec_multi) • succeed a bit optimization • But overhead is not negligible switched concurrent API call PHP wait API response consumed time graph

Slide 19

Slide 19 text

Next step • Try to call multiple APIs with single request • Nice to have anyway • JSON-RPC is reasonable choice • But mercari API does not provides JSON-RPC • I tried to write the proxy server in Go • It’s the Widebullet.

Slide 20

Slide 20 text

Widebullet • The proxy server between JSON-RPC and RESTful API server • Protocol • based JSON-RPC ( a little extended) • Configuration with TOML • Not published yetʂ

Slide 21

Slide 21 text

Overview NFSDBSJ "1* TVCTZT JSON-RPC XJEFCVMMFU

Slide 22

Slide 22 text

Start Widebullet $ wbt -c wbt.toml

Slide 23

Slide 23 text

Configuration with TOML Port = "29300" LogLevel = "error" Timeout = 5 MaxIdleConnsPerHost = 100 DisableCompression = false [[Endpoints]] Name = "ep-1" Ep = “ep-1:30001" ProxySetHeaders = [ ["Host", "ep1.example.com"], ] [[Endpoints]] Name = "ep-2" Ep = “ep-2:30001" ProxySetHeaders = [ ["Host", "ep2.example.com"], ]

Slide 24

Slide 24 text

JSON-RPC request example [ { “jsonrpc”: “2.0”, “ep”: “ep-1”, “method”: “/goods/get”, “params”: { “id”: 1234, “status”: “alive” } }, { “jsonrpc”: “2.0”, “ep”: “ep-2”, “method”: “/users/get”, “params”: { “id”: 5678 } } ]

Slide 25

Slide 25 text

Convert to RESTful API requests GET http://ep-1/goods/get?id=1234&status=alive HTTP/1.1 GET http://ep-2/users/get?id=5678 HTTP/1.1

Slide 26

Slide 26 text

Sending HTTP requests concurrently // error handling is eliminated for simplicity func jsonRpc2Http(reqs *[]jsonrpc.Request) ([]jsonrpc.Response) { wg := new(sync.WaitGroup) resps := make([]jsonrpc.Response, len(reqs) for i, reqj := range *reqs { wg.Add(1) go func(i int, reqj jsonrpc.Request) { defer wg.Done() reqh := buildHttpRequest(&reqj) resp, _ := HttpClient.Do(reqh) defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) resps[i] = buildJsonRpcResponse(string(body), reqj.ID) }(i, reqj) } wg.Wait() return resps }

Slide 27

Slide 27 text

Overview NFSDBSJ "1* TVCTZT JSON-RPC 8JEFCVMMFU REST [ { “jsonrpc”: “2.0”, “ep”: “ep-1”, “method”: “/goods/get”, “params”: { “id”: 1234, “status”: “alive” } }, { “jsonrpc”: “2.0”, “ep”: “ep-2”, “method”: “/users/get”, “params”: { “id”: 5678 } } ] GET http://ep-1/goods/get?id=1234&status=alive HTTP/1.1 GET http://ep-2/users/get?id=5678 HTTP/1.1

Slide 28

Slide 28 text

Result PHP wait API response from widebullet consumed time graph introduced Widebullet

Slide 29

Slide 29 text

We are hiring Go Engineer! https://www.mercari.com/jp/jobs/