10
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

リクルートテクノロジーズアドカレ14日目です!先日mod_mruby/ngx_mrubyのアドカレで記事を書かせていただきましたが,その勢いでngx_mrubyを使って実験的な試みをしてみました.

ProxyでバックエンドAPIを結合する

overview

今年の前半ぐらいに盛り上がった話題で周回遅れ感があり若干の気恥ずかしさがあるのですが,サーバサイドでAPI実装が既に完了してしまっている/改修不能な状態で,クライアントアプリが要求するAPIの仕様がドラスティックに変わるというずれがあるケースで,クライアントサイドのエンジニアがサーバサイド側でAPIをある程度整形できる緩衝地帯的な仕組みがあるは面白いなと,kawasimaさんのdarzananetflixの記事や同僚の話を聞いて考えておりました.SOA,SOAPやWDSLのことが思い浮かばれますが,最近覚えたproxyのリクエスト制御の仕組みを使って同じようにOracle Service busのようなものが作れるんじゃないか思いつきでちょっと作ってみました.

構成の概要

nginx/ngx_mrubyでレスポンス制御を行いますが,制御のロジックをRedisに格納して,それをngx_mrubyのハンドラで取得してevalするという大胆な仕組みで実現しています.redisへのロジック格納を外部から楽に行えるようにするため,管理用のwebアプリも付けています.

ハンドラの制御より抜粋

r = Nginx::Request.new
#redisから取得したロジックをeval
orchestrate = lambda{eval(score)}
r.content_type = "application/json"
Nginx.rputs orchestrate.call

デモ

プロトタイプを公開していて,一応デモを動かすことができます.一式構成できるDockerfileを用意したので,コンテナを立ち上げればすぐ動きます.

git clone https://github.com/prevs-io/Karajan.git && cd Karajan
docker build -t karajan .
docker run -it --rm -p 49080:80 -p 49081:8080 -p 49082:3000 --name karajan karajan /data/scripts/start.sh

デモは次のようなケースを想定して作っています.

  • クライアント側アプリはある画面の描画のために http://0.0.0.0:8080/api/contents and http://0.0.0.0:8080/api/badgeの2つのAPIからデータを取る必要がある.
  • しかしアプリ側はできることなら1回のhttpリクエストだけで必要な情報を取得したいので,2つのリクエストを1つのリクエストにまとめたい.

このケースにしたがって,2つのリクエストをまとめるロジックをngx_mrubyのフックスクリプトとして書くと次のようになります.

api_endp_1 ||= WebAPI.new "http://0.0.0.0:8080"
api_endp_2 ||= WebAPI.new "http://0.0.0.0:8080"

contents = JSON.parse(api_endp_1.get('/api/contents').body)
badges = JSON.parse(api_endp_2.get('/api/badges').body)

JSON.generate(contents.merge(badges))

上記のロジックをngx_mrubyから使うために,管理アプリ経由でRedisに格納します.POSTリクエストでリクエストを受けるURIとbase64エンコードしたロジックの組を登録します.

#!/usr/bin/env ruby
require 'httparty'
require 'json'
require 'base64'

path = File.dirname(__FILE__)
score=File.open(File.join(path, 'score.rb')).readlines.join('')
url = 'http://0.0.0.0:49082/write_score'
#{<context_path> => <client logic base64 encoded>}
body = {'/' =>  Base64.encode64(score)}.to_json
# write logic
puts HTTParty.post(url, :body=>body)

ロジックの格納が完了したら,動作を確認してみます./orchestrate以下にロジックが登録されます.

➜  karajan git:(master) ✗ curl  http://0.0.0.0:49080/api/badges
{"badges":[{"badge1":"badge1"},{"badge2":"badge2"},{"badge3":"badge3"},{"badge4":"badge4"},{"badge5":"badge5"},{"badge6":"badge6"}]}

➜  karajan git:(master) ✗ curl  http://0.0.0.0:49080/api/contents
{"contents":[{"item1":"description1"},{"item2":"description2"},{"item3":"description3"}]}

#2つのリクエストが1つにマージされた
➜  karajan git:(master) ✗ curl  http://0.0.0.0:49080/orchestrate/
{"contents":[{"item1":"description1"},{"item2":"description2"},{"item3":"description3"}],"badges":[{"badge1":"badge1"},{"badge2":"badge2"},{"badge3":"badge3"},{"badge4":"badge4"},{"badge5":"badge5"},{"badge6":"badge6"}]}

まとめ

前段の話に加え,AWS Lambdaの発表などもあってああいう仕組みを作ってみたいなぁと思っていたんですが,Lambdaもそうであるようにロジックはクライアント側が触るものなのでやっぱりjsにした方がいいのかなと思いつつ,そうなるとngx_mrubyでなくnodeで実装したほうがよいな...と迷いましたが気づいたら作り終わってました.Orchestration Layerに絡む問題を把握しきれていないこともあり,proxyレイヤでorchestrationすることに対して現状作ったコンセプトでメリットを出すことができませんでしたが,関心のある分野なので今後もウォッチしていきたいと思っています.

10
10
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
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?