edge
Daugiau informacijos rubyonrails.org: Daugiau apie Ruby on Rails

Gijimo ir kodo vykdymas „Rails“

Po šio vadovo perskaitymo žinosite:

1 Automatinis lygiagretumas

„Rails“ automatiškai leidžia atlikti įvairias operacijas vienu metu.

Naudojant gijinį interneto serverį, pvz., numatytąjį „Puma“, kelios HTTP užklausos bus aptarnaujamos tuo pačiu metu, kiekvienai užklausai suteikiant jos pačios valdiklio egzempliorių.

Gijinės „Active Job“ adapteriai, įskaitant įdiegtąjį „Async“, taip pat vykdys kelias užduotis vienu metu. Taip pat taip valdomi ir „Action Cable“ kanalai.

Visi šie mechanizmai apima kelias gijas, kurios kiekviena tvarko darbą tam tikram objekto egzemplioriui (valdiklis, užduotis, kanalas), tuo pačiu dalindamos bendrą procesų erdvę (tokią kaip klasės ir jų konfigūracijos, globalūs kintamieji). Jei jūsų kodas nepakeičia šių bendrų dalykų, jis daugiausia gali ignoruoti kitas gijas.

Šio vadovo likusi dalis aprašo mechanizmus, kuriuos „Rails“ naudoja, kad tai būtų „daugiausia ignoruojama“, ir kaip juos gali naudoti plėtiniai ir programos su specialiais poreikiais.

2 Vykdytojas

„Rails“ vykdytojas atskiria programos kodą nuo pagrindo kodo: kiekvieną kartą, kai pagrindas iškviečia jūsų parašytą kodą jūsų programoje, jis bus apgaubtas vykdytoju.

Vykdytojas susideda iš dviejų atgalinių iškvietimų: to_run ir to_complete. „Run“ atgalinis iškvietimas yra iškviečiamas prieš programos kodą, o „Complete“ atgalinis iškvietimas yra iškviečiamas po jo.

2.1 Numatytieji atgaliniai iškvietimai

Numatytoje „Rails“ programoje vykdytojo atgaliniai iškvietimai naudojami šiems tikslams:

  • stebėti, kurios gijos yra saugiose padėtyse autokrovimui ir perkrovimui
  • įjungti ir išjungti „Active Record“ užklausų kešą
  • grąžinti gautus „Active Record“ ryšius į baseiną
  • apriboti vidinio kešo gyvavimo laiką

Iki „Rails“ 5.0 šie dalykai buvo tvarkomi atskirais „Rack“ tarpiniais sluoksniais (tokiais kaip „ActiveRecord::ConnectionAdapters::ConnectionManagement“) arba tiesiog apgaubiant kodą metodais, pvz., „ActiveRecord::Base.connection_pool.with_connection“. Vykdytojas juos pakeičia vienu abstrakčesniu sąsaja.

2.2 Programos kodo apgaubimas

Jei rašote biblioteką ar komponentą, kuris iškvies programos kodą, turėtumėte jį apgaubti vykdytojo iškvietimu:

Rails.application.executor.wrap do
  # čia iškviečiamas programos kodas
end

PATARIMAS: Jei nuolat iškviečiate programos kodą iš ilgai veikiančio proceso, galite jį apgaubti naudodami Perkrovėją vietoj to.

Kiekviena gija turėtų būti apgaubta prieš vykdant programos kodą, todėl jei jūsų programa rankiniu būdu perduoda darbą kitoms gijoms, pvz., naudojant Thread.new arba „Concurrent Ruby“ funkcijas, kurios naudoja gijų baseinus, turėtumėte nedelsdami apgaubti bloką:

Thread.new do
  Rails.application.executor.wrap do
    # jūsų kodas čia
  end
end

PASTABA: „Concurrent Ruby“ naudoja „ThreadPoolExecutor“, kurį kartais konfigūruoja su „executor“ parinktimi. Nepaisant pavadinimo, tai nesusiję.

Vykdytojas yra saugiai reentrantas; jei jis jau aktyvus dabartinėje gijoje, „wrap“ nevykdo jokios veiksmų.

Jei nepraktiška apgaubti programos kodo bloku (pvz., „Rack“ API tai apsunkina), galite naudoti run! / complete! porą:

Thread.new do
  execution_context = Rails.application.executor.run!
  # jūsų kodas čia
ensure
  execution_context.complete! if execution_context
end

2.3 Lygiagretumas

Vykdytojas įterpia dabartinę giją į „vykdomą“ būseną Krovimo sąsaja. Ši operacija laikinai blokuos, jei kita gija šiuo metu autokrauna konstantą arba iškrauna/perkrauna programą.

3 Perkrovėjas

Kaip ir Vykdytojas, Perkrovėjas taip pat apgaubia programos kodą. Jei Vykdytojas dar neaktyvus dabartinėje gijoje, Perkrovėjas jį iškvies už jus, todėl jums tereikia iškviesti tik vieną. Tai taip pat užtikrina, kad viskas, ką daro Perkrovėjas, įskaitant visus jo atgalinių iškvietimų iškvietimus, vyksta apgaubtoje Vykdytojo viduje.

Rails.application.reloader.wrap do
  # čia iškviečiamas programos kodas
end

Perkrovėjas tinkamas tik ten, kur ilgai veikiantis pagrindo lygmens procesas nuolat iškviečia programos kodą, pvz., interneto serveriui ar užduočių eilei. „Rails“ automatiškai apgaubia interneto užklausas ir „Active Job“ darbininkus, todėl retai kada reikės iškviesti Perkrovėją patiems. Visada apsvarstykite, ar Vykdytojas yra tinkamesnis jūsų naudojimo atvejui.

3.1 Atgaliniai iškvietimai

Prieš įeinant į apgaubtą bloką, Perkrovėjas patikrins, ar reikia perkrauti veikiančią programą - pavyzdžiui, jei modelio šaltinio failas buvo pakeistas. Jei nustatoma, kad perkrovimas yra būtinas, jis palauks, kol tai bus saugu, ir tada tai padarys, prieš tęsdamas. Kai programa sukonfigūruota visada perkrauti, nepaisant to, ar aptinkami pakeitimai, perkrovimas vykdomas bloko pabaigoje. Reloader taip pat teikia to_run ir to_complete atgalinio kvietimo funkcijas; jos yra iškviestos tais pačiais taškais kaip ir Executor, bet tik tada, kai dabartinis vykdymas inicijavo programos perkrovimą. Kai nėra laikoma, kad perkrovimas yra būtinas, Reloader iškviečia apgaubto bloko be kitų atgalinių kvietimų.

3.2 Klasės iškrovimas

Svarbiausia perkrovimo proceso dalis yra Klasės iškrovimas, kai visos automatiškai įkeltos klasės yra pašalinamos, kad būtų galima jas vėl įkelti. Tai įvyks tuojau prieš Run arba Complete atgalinį kvietimą, priklausomai nuo reload_classes_only_on_change nustatymo.

Dažnai reikia atlikti papildomus perkrovimo veiksmus arba tiesiog po Klasės iškrovimo, todėl Reloader taip pat teikia before_class_unload ir after_class_unload atgalinius kvietimus.

3.3 Lygiagretumas

Tik ilgai veikiantys "viršutinio lygio" procesai turėtų kviečti Reloader, nes jei jis nustato, kad perkrovimas yra būtinas, jis blokuos, kol visi kiti gijos baigs visus Executor kvietimus.

Jei tai įvyktų "vaiko" gijoje, kurioje laukia tėvas viduje Executor, tai sukeltų neišvengiamą užstrigimą: perkrovimas turi įvykti prieš vykdant vaiko giją, bet jis negali būti saugiai atliktas, kol tėvo gija yra viduryje vykdymo. Vaiko gijos turėtų naudoti vietoj to Executor.

4 Karkaso elgsena

Rails karkaso komponentai taip pat naudoja šiuos įrankius, kad valdytų savo lygiagretumo poreikius.

ActionDispatch::Executor ir ActionDispatch::Reloader yra Rack vidurinės programinės įrangos, kurios apgaubia užklausas su pateiktu Executor arba Reloader atitinkamai. Jos automatiškai įtrauktos į numatytąją programos eilutę. Reloader užtikrins, kad kiekviena atvykusi HTTP užklausa būtų aptarnaujama su naujausia įkelta programos kopija, jei įvyko kokios nors kodų pakeitimai.

Active Job taip pat apgaubia savo darbo vykdymą su Reloader, įkeliant naujausią kodą, kad būtų galima vykdyti kiekvieną darbą, kai jis išeina iš eilės.

Action Cable vietoj to naudoja Executor: nes kabelio ryšys yra susietas su konkretaus klasės egzemplioriumi, neįmanoma perkrauti kiekvienos atvykstančios WebSocket žinutės. Tačiau apgaubiamas tik žinutės tvarkytojas; ilgai veikiantis kabelio ryšys neleidžia perkrovai, kurią sukelia nauja atvykstanti užklausa ar darbas. Vietoj to, Action Cable naudoja Reloader before_class_unload atgalinį kvietimą, kad atjungtų visus savo ryšius. Kai klientas automatiškai prisijungs iš naujo, jis kalbės su kodo nauja versija.

Aukščiau pateikti yra karkaso įėjimo taškai, todėl jie atsakingi už tai, kad jų atitinkamos gijos būtų apsaugotos ir nuspręstų, ar perkrovimas yra būtinas. Kiti komponentai turi naudoti Executor tik tada, kai jie paleidžia papildomas gijas.

4.1 Konfigūracija

Reloader tikrina failų pakeitimus tik tada, kai config.enable_reloading yra true, taip pat config.reload_classes_only_on_change. Tai yra numatytieji nustatymai development aplinkoje.

Kai config.enable_reloading yra false (numatytasis production), Reloader yra tik perduodamas į Executor.

Executor visada turi svarbų darbą, pvz., duomenų bazės prisijungimų valdymą. Kai config.enable_reloading yra false ir config.eager_load yra true (numatytieji production nustatymai), nebus vykdomas perkrovimas, todėl jam nereikia įkrovos užrakto. Su numatytosiomis development aplinkos nustatymais Executor naudos įkrovos užraktą, kad užtikrintų, jog konstantos būtų įkeltos tik tada, kai tai yra saugu.

5 Įkrovos užraktas

Įkrovos užraktas leidžia įjungti automatinį įkėlimą ir perkrovimą daugiausiai gijų aplinkoje.

Kai viena gija atlieka automatinį įkėlimą, įvertindama klasės apibrėžimą iš tinkamo failo, svarbu, kad kitos gijos neatitiktų dalinai apibrėžtos konstantos nuorodos.

Panašiai, saugu atlikti iškrovimą/perkrovimą tik tada, kai jokia programos kodas nevykdomas: po perkrovimo, pavyzdžiui, User konstanta gali rodyti į kitą klasę. Be šios taisyklės, blogai laiku atliktas perkrovimas reikštų, kad User.new.class == User, arba net User == User, gali būti netiesa.

Abu šie apribojimai yra sprendžiami naudojant įkrovos užraktą. Jis seka, kurios gijos šiuo metu vykdo programos kodą, įkelia klasę arba iškelia automatiškai įkeltas konstantas.

Tik viena gija gali įkelti arba iškelti vienu metu, ir norint tai padaryti, ji turi palaukti, kol kitos gijos nevykdo programos kodo. Jei gija laukia atlikti įkėlimą, tai neleidžia kitoms gijoms įkelti (iš tikrųjų jos bendradarbiaus ir kiekviena atliks savo eilėje esantį įkėlimą, prieš visi kartu vėl pradėdami vykdyti).

5.1 permit_concurrent_loads

Executor automatiškai įgyvendina running užraktą per visą bloką, ir automatinis įkėlimas žino, kada perjungti į load užraktą ir po to vėl perjungti į running. Kitos blokavimo operacijos, atliekamos viduje Executor bloko (į kurį įeina visos programos kodas), gali nereikalingai išlaikyti running užrakto. Jei kitas gijas susiduria su konstanta, kurią reikia automatiškai įkelti, tai gali sukelti užstrigimą.

Pavyzdžiui, jei User dar nėra įkeltas, tai sukels užstrigimą:

Rails.application.executor.wrap do
  th = Thread.new do
    Rails.application.executor.wrap do
      User # vidinė gija laukia čia; ji negali įkelti
           # User, kol vyksta kita gija
    end
  end

  th.join # išorinė gija laukia čia, laikydamas 'running' užraktą
end

Norint išvengti šio užstrigimo, išorinė gija gali naudoti permit_concurrent_loads metodą. Iškvietus šį metodą, gija garantuoja, kad ji nebus nuorodinėjama į jokį galimai automatiškai įkeltą kintamąjį šaltiniame bloke. Saugiausias būdas įvykdyti šią pažadą yra jį įdėti kuo arčiau blokuojančio kvietimo:

Rails.application.executor.wrap do
  th = Thread.new do
    Rails.application.executor.wrap do
      User # vidinė gija gali gauti 'load' užraktą,
           # įkelti User ir tęsti darbą
    end
  end

  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
    th.join # išorinė gija laukia čia, bet neturi užrakto
  end
end

Kitas pavyzdys, naudojant Concurrent Ruby:

Rails.application.executor.wrap do
  futures = 3.times.collect do |i|
    Concurrent::Promises.future do
      Rails.application.executor.wrap do
        # atlikti darbą čia
      end
    end
  end

  values = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
    futures.collect(&:value)
  end
end

5.2 ActionDispatch::DebugLocks

Jei jūsų programa užstrigo ir manote, kad įsijungė Load Interlock, laikinai galite pridėti ActionDispatch::DebugLocks middleware prie config/application.rb:

config.middleware.insert_before Rack::Sendfile,
                                  ActionDispatch::DebugLocks

Jei tuomet paleidžiate programą iš naujo ir vėl sukeliama užstrigimo būsena, /rails/locks rodo santrauką visų gijų, kurios žinomos interlock, kurios užraktą jos laiko ar laukia ir jų dabartinį atgalinį taką.

Paprastai užstrigimas bus sukeltas interlock konfliktuojant su kitu išoriniu užraktu ar blokuojančiu I/O kvietimu. Radus tai, galite jį apgaubti permit_concurrent_loads.

Atsiliepimai

Jūs esate skatinami padėti pagerinti šio vadovo kokybę.

Prašome prisidėti, jei pastebite rašybos klaidų ar faktinių klaidų. Norėdami pradėti, galite perskaityti mūsų dokumentacijos prisidėjimo skyrių.

Taip pat gali būti nepilnos informacijos arba informacijos, kuri nėra atnaujinta. Prašome pridėti bet kokią trūkstamą dokumentaciją pagrindiniam. Patikrinkite Edge vadovus pirmiausia, ar problemas jau išspręsta arba ne pagrindinėje šakoje. Patikrinkite Ruby on Rails vadovų gaires dėl stiliaus ir konvencijų.

Jei dėl kokios nors priežasties pastebite kažką, ką reikia ištaisyti, bet negalite patys tai pataisyti, prašome pranešti apie problemą.

Ir galiausiai, bet ne mažiau svarbu, bet koks diskusijos dėl Ruby on Rails dokumentacijos yra labai laukiamos oficialiame Ruby on Rails forume.