クックパッドがどのようにMicroservicesしてきたか/How Cookpad shifts to Microservices

F356bb27a5329c131855abadfd309b7e?s=47 adorechic
October 27, 2016

クックパッドがどのようにMicroservicesしてきたか/How Cookpad shifts to Microservices

F356bb27a5329c131855abadfd309b7e?s=128

adorechic

October 27, 2016
Tweet

Transcript

  1. 2.

    

  2. 9.

    

  3. 13.

    w ᴈ໌ظ w ୯ҰαʔϏεɺϞσϧڞ༗ɺ༻్͝ͱͷΠϯϑϥ w αʔϏε֦େظ w ΞϓϦέʔγϣϯංେԽ΁ͷରԠͱݶք w ୤.POPMJUIJDظ

    w ౷Ұతͳ"1*ɺڞ௨ج൫ɺαʔϏε෼ׂ w .JDSPTFSWJDFTԽظ w αʔϏε෼ׂͷฐ֐Λղফ͢Δɺ෼ׂίετͷ௿Լ 
  4. 38.

    3&45GVMIZQFSNFEJB"1* GET /v1/users/123 { "id": 123, "name": "ύυඒ", "kitchen": {

    "id": 15, "created": "2016-03-10T10:59:24+09:00", } }, "_links": { "self": { "href": "/v1/users/123" }, "recipes": { "href": "/v1/users/123/recipes" }, } } Ϧιʔεؒͷؔ࿈Λ ϦϯΫͱͯ͠දݱͰ͖Δ 
  5. 39.

    (BSBHF w HJUIVCDPNDPPLQBEHBSBHF class Employee < ActiveRecord::Base include Garage::Representer belongs_to

    :division has_many :projects property :id property :title property :division, selectable: true collection :projects, selectable: true link(:division) { division_path(division) } link(:projects) { employee_projects_path(self) } def self.build_permissions(perms, other, target) perms.permits! :read end end Ϧιʔεͱͯ͠ͷ ଐੑ΍ϦϯΫΛఆٛ ೝՄ৚݅΋ఆٛͰ͖Δ 
  6. 40.

    (BSBHF$MJFOU w HJUIVCDPNDPPLQBEHBSBHF@DMJFOU # GET https://garage.example.com/v1/me user = client.get("/me") user.id

    user.name # GET https://garage.example.com/v1/recipes recipes = client.get("/recipes") recipes.total_count recipes[0].id recipes[0].name ϦιʔεΛจࣈྻͰࢦఆ "1*ͷ࣮૷͕૿͑ͯ΋ ΫϥΠΞϯτ͸ͦͷ··࢖͑Δ ͲͷαʔϏεΛ࢖͏ͱ͖΋ ࢖͍ํ͸ಉ͡ ֤ΤϯυϙΠϯτͷ࢓༷͚ͩ Θ͔Ε͹Α͍ 
  7. 41.

    "VUPEPD w HJUIVCDPNSLBNVSBBVUPEPD # spec/requests/users_spec.rb describe "Users" do describe "GET

    /v1/me", autodoc: true do it "returns a current_resource_owner" do get "/v1/me", params: {}, headers: headers expect(response).to have_http_status(:success) expect(response.body).to be_json_as( id: resource_owner.id, name: resource_owner.name ) end end end ςετ͔ΒυΩϡϝϯτΛ ࣗಈੜ੒ 
  8. 46.

    FUDFOWFUDWBVMU w HJUIVCDPNTPSBIFUDFOW w FUDEͰઃఆ஋Λ؅ཧ w HJUIVCDPNTPSBIFUDWBVMU w FUDEͷ஋Λ҉߸Խ w

    ΋ͱ΋ͱFUDEʹ"$-͕ແ͔ͬͨͨΊ࡞ΒΕͨ w ࢦఆͷ伴Λ͍࣋ͬͯΔΠϯελϯεͰ͔͠෮߸Ͱ͖ͳ͍ 
  9. 55.

    "1*࢓༷ͷᴥᴪʹΑΔࣄނ w ഁյతͳ࢓༷มߋʹΑΔࣄނ w ଟ͘ͷ৔߹͸ҙਤͨ͠΋ͷͰ͸ͳ͘ൃੜ w ҙਤ͍ͯ͠Δ৔߹΋Ͳ͔͜Βར༻͞Ε͍ͯΔͷ͔೺Ѳͮ͠Β͍ w ςετ͸ʁ w

    ࣗಈςετͰ͸ଞαʔϏεͷ"1*͸ελϒ͍ͯ͠Δ w "1*ϓϩόΠμଆ͕࢓༷มߋͯ͠΋ελϒ͸ߋ৽͞Εͳ͍ͷͰݕ஌Ͱ͖ͳ͍ 
  10. 60.

    1BDU w HJUIVCDPNSFBMFTUBUFDPNBVQBDU w ΫϥΠΞϯτଆ w $*ͰQBDUϑΝΠϧΛੜ੒ w "1*ϓϩόΠμଆ w

    $*ͰQBDUϑΝΠϧΛWFSJGZ IUUQTHJUIVCDPNSFBMFTUBUFDPNBVQBDUIPXEPFTJUXPSL 
  11. 61.

    1BDU $POTVNFS describe 'get_all' do let(:recipe_a) { { id: Pact.like(1),

    name: Pact.like('Curry') } } let(:recipe_b) { { id: Pact.like(2), name: Pact.like('Salada') } } before do provider_app.given('there are 2 recipes'). upon_receiving('a request for recipes'). with(method: :get, path: '/v1/recipes'). will_respond_with( status: 200, headers: { 'Content-Type' => Pact.term( generate: 'application/json', matcher: %r{application/json} ), }, body: [recipe_a, recipe_b] ) end it 'returns recipes' do recipes = described_class.get_all expect(recipes.size).to eq(2) expect(recipes.first.name).to eq('Curry') end end ςετ͸௨ৗͱಉ͡ 1BDUͰελϒ 
  12. 62.

    1BDUpMF { "consumer": { "name": "ConsumerApp" }, "provider": { "name":

    "ProviderApp" }, "interactions": [ { "description": "a request for recipes", "provider_state": "there are 2 recipes", "request": { "method": "get", "path": "/v1/recipes" }, "response": { "status": 200, "headers": { "Content-Type": { "json_class": "Pact::Term", "data": { "generate": "application/json", "matcher": { "json_class": "Regexp", "o": 0, "s": "application/json" } ϦΫΤετͱ Ϩεϙϯεͷ ಺༰ΛKTPOͰग़ྗ 
  13. 63.

    1BDU 1SPWJEFS w "1*ଆ w γφϦΦʹԊͬͨηοτΞοϓίʔυ͚ͩΛ༻ҙ Pact.provider_states_for 'ConsumerApp' do provider_state

    "there are 2 recipes" do set_up do %w[Curry Salada].each {|name| Recipe.create!(name: name) } end end end 
  14. 66.

    &YQFEJUPS w HJUIVCDPNDPPLQBEFYQFEJUPS EXPEDITOR_SERVICE = Expeditor::Service.new( non_break_count: 20, threshold: 0.2,

    period: 10, sleep: 5, ) def notification_counts Expeditor::Command.new(service: EXPEDITOR_SERVICE, timeout: 3) { response = client.get("/notifications/counts", params, options) response.notification_count }.set_fallback {|e| Raven.capture_exception(e) 0 }.start(current_thread: true).get end $JSDVJU#SFBLFSͷᮢ஋Λઃఆ Τϥʔ࣌͸ϩάΛه࿥ͯ͠ σϑΥϧτ஋Λฦ͢