LoginSignup
10
7

More than 5 years have passed since last update.

JSON-RPC over HTTPなサーバを作る

Last updated at Posted at 2016-03-20

JSON-RPC 2.0 over HTTPなRPCサーバをrubyで実装してみる。

RESTfullに定義できないアクションを持つAPIサーバを作る必要があってRPCサーバか、じゃJSON-RPCで作ってみるかと。どんなものかと週末手を動かしてみた。

JSON-RPC 2.0 の仕様はこちらで薄い仕様なのでさくっと読むべし。なお、JSON-RPC 2.0 にはトランスポート層の指定がないので、TCP、UDP、websocket、HTTPなど用途に応じて決めれる。今回はHTTP上で動作させることにした。

例として足し算するsum(10,20) => 30こういうRPCを作ってみる。

$ curl -v -s -S \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d  '{"jsonrpc": "2.0", "method": "sum", "params": [10, 20], "id": 1}' \
   http://localhost:8080  
> POST / HTTP/1.1
> Host: localhost:8080
> Accept: application/json
> Content-Type: application/json
> Content-Length: 64
> 
< HTTP/1.1 200 OK
< Date: Sun, 20 Mar 2016 13:39:21 GMT
< Connection: close
< Content-Length: 36
< X-Runtime: 0.004520
< 
{
  "jsonrpc": "2.0",
  "result": 30,
  "id": 1
}

方針

サーバ実装の方針を立てる

  • HTTP部分
    • Rack
    • WAF不要。つまりRails/Sinatra使わない
    • Webサーバ(Webric, unicorn, pumaなど好きなもの)
  • JSON-RPCサーバ
    • リクエスト、呼び出し関数へのディスパッチ処理、レスポンス)実装が必要
    • 自作する? しない?

HTTP層はRackに乗っかるとして、WAF(RailsやSinatraなど)が不要なのは、RPCサーバゆえエントリーポイント(パス)は1個、つまりルーティング不要、またレスポンスはjsonで返すだけでviewレンダリングがいらないので、WAFいらないやとなった。シンプルだ。

図示するとサーバのスタックとしては以下になる。

  +----------------------------+  ^
  |      RPC Handlers          |  |
  +----------------------------+  | rack app
  |  JSON-RPC implementation   |  |
  +----------------------------+  v
  |          Rack              |
  +----------------------------+
  |  Web Server(Unicorn or etc)|
  +----------------------------+
           ^   |
       req |   v resp    HTTP

          Client        

作る

JSON-RPCの仕様薄いから自作するかとか一瞬おもったけど、一旦落ち着いてRubyの実装を探したところjimsonというgemが自分が実装したいイメージと同じだったので、実装せず使わせてもらう。

jimsonには

  • JSON-RPC clientクラス
  • JSON-RPC serverクラス (中身はrack app)
  • JSON-RPC request, responseクラス
  • 各種エラークラス
  • handler用のベース

が同封されているので、RPC handler、serverクラスはjimsonがベースにすることになる。

handlerクラス

RPCの実装部分。簡単ですね。

# my_handler.rb

require 'jimson'

class MyHandler
  extend Jimson::Handler 

  def sum(a,b)
    a + b
  end
end

rackup

# config.ru
require 'jimson'
require_relative 'my_handler'

# middlewares that you want to use
use Rack::Reloader,0
use Rack::Runtime

run Jimson::Server.new(MyHandler.new)

こう書いて、$ bundle exec rackup とか $ bundle exec unicorn とかすればよろしい。

ネスト

ちなみにjimsonはfoo.hello, bar.baz.sayといったnapespaceつきのJSON-RPC methodも定義可能。

# config.ru

require 'jimson'
# require handlers 

router = Jimson::Router.new
router.draw do
  namespace 'foo', Foo   # Foo is expected to implement hello()
  namespace 'bar' do    
    namespace 'baz', Baz # Baz is expected to implement say()
  end
end

run Jimson::Server.new(router)

2016/4/13 update: なお、.区切でしかnamespace表現出来ない仕様だったのでAdd Router :ns_sep option for custom namespace #31でpull reqして任意のセパレータ使えるようお願い中。

ToDo

  • リクエストパラメタのバリデーション。外部から渡された値をハンドラにバリデーション無しで渡すのは危険なので、json schemaとか?
  • APIのversioning
10
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
7