Co to jest oprogramowanie pośredniczące w szafie w Ruby? Nie znalazłem dobrego wytłumaczenia tego, co rozumieją przez „oprogramowanie pośrednie”.
Co to jest oprogramowanie pośredniczące w szafie w Ruby? Nie znalazłem dobrego wytłumaczenia tego, co rozumieją przez „oprogramowanie pośrednie”.
Odpowiedzi:
Oprogramowanie pośredniczące w szafie to coś więcej niż „sposób filtrowania żądania i odpowiedzi” - to implementacja wzorca projektowania potoków dla serwerów WWW korzystających z szafy .
Bardzo dokładnie oddziela poszczególne etapy przetwarzania żądania - oddzielenie problemów jest kluczowym celem wszystkich dobrze zaprojektowanych produktów oprogramowania.
Na przykład w szafie Rack mogę mieć oddzielne etapy rurociągu:
Uwierzytelnianie : czy po otrzymaniu żądania dane logowania użytkowników są prawidłowe? Jak zweryfikować to OAuth, podstawowe uwierzytelnianie HTTP, nazwę / hasło?
Autoryzacja : „czy użytkownik jest upoważniony do wykonania tego konkretnego zadania?”, Tj. Bezpieczeństwo oparte na rolach.
Buforowanie : czy przetworzyłem już to żądanie, czy mogę zwrócić wynik z pamięci podręcznej?
Dekoracja : jak mogę ulepszyć żądanie, aby ulepszyć przetwarzanie końcowe?
Monitorowanie wydajności i użytkowania : jakie statystyki mogę uzyskać z zapytania i odpowiedzi?
Wykonanie : faktycznie obsłużyć żądanie i podać odpowiedź.
Możliwość oddzielenia różnych etapów (i opcjonalnie ich uwzględnienia) jest wielką pomocą w tworzeniu dobrze zorganizowanych aplikacji.
Istnieje również świetny ekosystem rozwijający się wokół oprogramowania pośredniczącego w szafach - powinieneś być w stanie znaleźć gotowe komponenty do montażu w szafie, aby wykonać wszystkie powyższe kroki i więcej. Zobacz wiki GitHub Rack, aby uzyskać listę oprogramowania pośredniego .
Oprogramowanie pośrednie to straszny termin, który odnosi się do dowolnego komponentu / biblioteki oprogramowania, który pomaga, ale nie jest bezpośrednio zaangażowany w wykonanie jakiegoś zadania. Bardzo częstymi przykładami są rejestrowanie, uwierzytelnianie i inne typowe komponenty przetwarzania poziomego . Są to rzeczy, których wszyscy potrzebują w wielu aplikacjach, ale niewiele osób jest zainteresowanych (lub powinno być) budowaniem siebie.
Komentarz na temat tego, że jest to sposób na filtrowanie żądań, prawdopodobnie pochodzi z odcinka RailsCast 151: Raster Middleware .
Oprogramowanie pośrednie Rack ewoluowało z Rack, a wprowadzenie do oprogramowania pośredniego Rack jest bardzo dobre .
Jest intro dla oprogramowania pośredniego Wikipedia tutaj .
Po pierwsze, Rack to dokładnie dwie rzeczy:
Rack - interfejs serwera WWW
Podstawy stojaka to prosta konwencja. Każdy serwer sieciowy zgodny ze stelażem zawsze wywoła metodę wywołania na podanym mu obiekcie i poda wynik tej metody. Rack określa dokładnie, jak powinna wyglądać ta metoda wywołania i co ma zwrócić. To jest stojak.
Wypróbujmy to prosto. Użyję WEBrick jako serwera sieciowego zgodnego ze stelażem, ale wystarczy jeden z nich. Stwórzmy prostą aplikację internetową, która zwraca ciąg JSON. W tym celu utworzymy plik o nazwie config.ru. Plik config.ru zostanie automatycznie wywołany przez polecenie backup gem szafy, które po prostu uruchomi zawartość pliku config.ru na serwerze WWW zgodnym z szafą. Dodajmy więc do pliku config.ru:
class JSONServer
def call(env)
[200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
end
end
map '/hello.json' do
run JSONServer.new
end
Jak określa konwencja, nasz serwer ma metodę o nazwie call, która akceptuje skrót środowiskowy i zwraca tablicę o formie [status, nagłówki, treść], która ma być obsługiwana przez serwer WWW. Wypróbujmy to, po prostu wywołując rackup. Domyślny serwer zgodny ze stelażem, być może WEBrick lub Mongrel uruchomi się i natychmiast zaczeka na żądania.
$ rackup
[2012-02-19 22:39:26] INFO WEBrick 1.3.1
[2012-02-19 22:39:26] INFO ruby 1.9.3 (2012-01-17) [x86_64-darwin11.2.0]
[2012-02-19 22:39:26] INFO WEBrick::HTTPServer#start: pid=16121 port=9292
Przetestujmy nasz nowy serwer JSON poprzez zwijanie lub odwiedzanie adresu URL http://localhost:9292/hello.json
i voila:
$ curl http://localhost:9292/hello.json
{ message: "Hello!" }
To działa. Wspaniały! To podstawa każdego frameworka internetowego, czy to Railsów, czy Sinatry. W pewnym momencie implementują metodę wywołania, pracują przez cały kod frameworka, a na koniec zwracają odpowiedź w typowej formie [status, nagłówki, treść].
Na przykład w Ruby on Rails żądania stelaża trafiają do ActionDispatch::Routing.Mapper
klasy, która wygląda następująco:
module ActionDispatch
module Routing
class Mapper
...
def initialize(app, constraints, request)
@app, @constraints, @request = app, constraints, request
end
def matches?(env)
req = @request.new(env)
...
return true
end
def call(env)
matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
end
...
end
end
Więc w zasadzie Railsy sprawdzają, zależnie od skrótu env, jeśli jakaś trasa jest zgodna. Jeśli tak, przekazuje skrót env do aplikacji w celu obliczenia odpowiedzi, w przeciwnym razie natychmiast odpowiada 404. Tak więc każdy serwer sieciowy, który jest zgodny z konwencją interfejsu stelażowego, może obsłużyć w pełni wydaną aplikację Railsową.
Middleware
Rack obsługuje również tworzenie warstw oprogramowania pośredniego. Zasadniczo przechwytują zapytanie, coś z nim robią i przekazują dalej. Jest to bardzo przydatne do wszechstronnych zadań.
Powiedzmy, że chcemy dodać rejestrowanie do naszego serwera JSON, który mierzy również czas trwania żądania. Możemy po prostu utworzyć rejestrator oprogramowania pośredniego, który robi dokładnie to:
class RackLogger
def initialize(app)
@app = app
end
def call(env)
@start = Time.now
@status, @headers, @body = @app.call(env)
@duration = ((Time.now - @start).to_f * 1000).round(2)
puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
[@status, @headers, @body]
end
end
Po utworzeniu zapisuje sobie kopię faktycznej aplikacji do montażu w szafie. W naszym przypadku jest to przykład naszego serwera JSONServer. Rack automatycznie wywołuje metodę wywołania w oprogramowaniu pośrednim i oczekuje z powrotem [status, headers, body]
tablicy, tak jak zwraca nasz JSONServer.
Tak więc w tym oprogramowaniu pośrednim jest brany punkt początkowy, następnie wykonuje się rzeczywiste wywołanie JSONServer @app.call(env)
, a następnie program rejestrujący wysyła wpis rejestrowania i ostatecznie zwraca odpowiedź jako [@status, @headers, @body]
.
Aby nasz mały rackup.ru używał tego oprogramowania pośredniego, dodaj do niego RackLogger w następujący sposób:
class JSONServer
def call(env)
[200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
end
end
class RackLogger
def initialize(app)
@app = app
end
def call(env)
@start = Time.now
@status, @headers, @body = @app.call(env)
@duration = ((Time.now - @start).to_f * 1000).round(2)
puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
[@status, @headers, @body]
end
end
use RackLogger
map '/hello.json' do
run JSONServer.new
end
Zrestartuj serwer i voila, wypisuje log na każde żądanie. Rack pozwala dodawać wiele programów pośrednich, które są wywoływane w kolejności ich dodawania. To po prostu świetny sposób na dodanie funkcjonalności bez zmiany rdzenia aplikacji w szafie.
Rack - klejnot
Chociaż stojak - przede wszystkim - jest konwencją, jest również klejnotem, który zapewnia doskonałą funkcjonalność. Jeden z nich, którego już używaliśmy dla naszego serwera JSON, polecenie rackup. Ale jest więcej! Klejnot do szafy zapewnia niewiele aplikacji do wielu zastosowań, takich jak serwowanie plików statycznych lub nawet całych katalogów. Zobaczmy, jak udostępniamy prosty plik, na przykład bardzo prosty plik HTML znajdujący się w htmls / index.html:
<!DOCTYPE HTML>
<html>
<head>
<title>The Index</title>
</head>
<body>
<p>Index Page</p>
</body>
</html>
Być może chcemy udostępnić ten plik z katalogu głównego witryny, dlatego dodajmy do naszego pliku config.ru:
map '/' do
run Rack::File.new "htmls/index.html"
end
Jeśli odwiedzimy http://localhost:9292
, zobaczymy, że nasz plik HTML jest doskonale renderowany. To było łatwe, prawda?
Dodajmy cały katalog plików javascript, tworząc niektóre pliki javascript w / javascripts i dodając następujące elementy do config.ru:
map '/javascripts' do
run Rack::Directory.new "javascripts"
end
Uruchom ponownie serwer i odwiedź, http://localhost:9292/javascript
a zobaczysz listę wszystkich plików javascript, które możesz teraz dołączyć bezpośrednio z dowolnego miejsca.
Przez dłuższy czas miałem problem ze zrozumieniem samego Racka. Zrozumiałem to dopiero po samodzielnym stworzeniu tego miniaturowego serwera Ruby . Na blogu podzieliłem się swoją wiedzą na temat Rack (w formie opowiadania): http://gauravchande.com/what-is-rack-in-ruby-rails
Informacje zwrotne są mile widziane.
config.ru
minimalny możliwy do uruchomienia przykład
app = Proc.new do |env|
[
200,
{
'Content-Type' => 'text/plain'
},
["main\n"]
]
end
class Middleware
def initialize(app)
@app = app
end
def call(env)
@status, @headers, @body = @app.call(env)
[@status, @headers, @body << "Middleware\n"]
end
end
use(Middleware)
run(app)
Uruchom rackup
i odwiedź localhost:9292
. Dane wyjściowe to:
main
Middleware
Jest więc jasne, że Middleware
otacza i wywołuje główną aplikację. Dlatego jest w stanie wstępnie przetworzyć żądanie i przetworzyć odpowiedź w dowolny sposób.
Jak wyjaśniono na stronie : http://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack , Rails korzysta z oprogramowania pośredniego Rack dla wielu swoich funkcji, a także możesz dodać swoje własne za pomocą config.middleware.use
metod rodzinnych.
Zaletą implementacji funkcjonalności w oprogramowaniu pośrednim jest to, że można go ponownie użyć w dowolnym środowisku Rack, a więc we wszystkich głównych Ruby, a nie tylko w Railsach.
Oprogramowanie pośredniczące w szafie jest sposobem na odfiltrowanie żądania i odpowiedzi przychodzących do aplikacji. Komponent oprogramowania pośredniego znajduje się między klientem a serwerem, przetwarzając żądania przychodzące i odpowiedzi wychodzące, ale to coś więcej niż interfejs, za pomocą którego można rozmawiać z serwerem WWW. Służy do grupowania i porządkowania modułów, które zwykle są klasami Ruby, i określania zależności między nimi. Moduł oprogramowania pośredniczącego w szafie musi tylko: - mieć konstruktora, który przyjmuje następną aplikację na stosie jako parametr - reagować na metodę „wywołania”, która jako parametr przyjmuje skrót środowiska. Zwracana wartość z tego wywołania to tablica: kodu statusu, skrótu środowiska i treści odpowiedzi.
Użyłem oprogramowania pośredniego Rack, aby rozwiązać kilka problemów:
Dało to dość eleganckie poprawki w obu przypadkach.
Rack zapewnia minimalny interfejs pomiędzy serwerami obsługującymi Ruby i frameworki Ruby.
Za pomocą Rack możesz napisać aplikację Rack.
Rack przekaże skrót środowiska (skrót, zawarty w żądaniu HTTP klienta, składającym się z nagłówków podobnych do CGI) do aplikacji w stojaku, która może korzystać z rzeczy zawartych w tym skrócie, aby robić, co chce.
Aby użyć Rack, musisz dostarczyć „aplikację” - obiekt, który reaguje na #call
metodę za pomocą funkcji skrótu środowiska jako parametru (zazwyczaj zdefiniowanej jako env
). #call
musi zwrócić tablicę dokładnie trzech wartości:
each
).Możesz napisać aplikację Rack, która zwróci taką tablicę - zostanie ona odesłana do klienta przez Rack, w odpowiedzi (faktycznie będzie to instancja klasy Rack::Response
[kliknij, aby przejść do dokumentów]).
gem install rack
config.ru
plik - Rack wie, że tego szuka.Będziemy tworzyć niewielką aplikację regałowe, które zwraca odpowiedź (instancję Rack::Response
) Kto ciała Response jest tablicą, która zawiera ciąg: "Hello, World!"
.
Uruchomimy lokalny serwer za pomocą polecenia rackup
.
Odwiedzając odpowiedni port w naszej przeglądarce, zobaczymy „Witaj, świecie!” renderowane w rzutni.
#./message_app.rb
class MessageApp
def call(env)
[200, {}, ['Hello, World!']]
end
end
#./config.ru
require_relative './message_app'
run MessageApp.new
Uruchom serwer lokalny rackup
i odwiedź localhost: 9292 , powinieneś zobaczyć „Witaj, świecie!” renderowane.
Nie jest to wyczerpujące wyjaśnienie, ale zasadniczo to, co się tutaj dzieje, polega na tym, że klient (przeglądarka) wysyła żądanie Rack do Rack, za pośrednictwem lokalnego serwera, a Rack tworzy instancję MessageApp
i uruchamia się call
, przekazując funkcję Hash środowiska jako parametr do metody ( env
argument).
Rack pobiera wartość zwracaną (tablicę) i używa jej do utworzenia instancji Rack::Response
i przesyła ją z powrotem do klienta. Przeglądarka używa magii do drukowania „Witaj, świecie!” do ekranu.
Nawiasem mówiąc, jeśli chcesz zobaczyć, jak wygląda skrót środowiska, po prostu umieść go puts env
pod spodem def call(env)
.
Choć jest minimalna, napisałeś tutaj o aplikacji Rack!
W naszej małej aplikacji Rack możemy wchodzić w interakcje z env
skrótem (zobacz tutaj więcej na temat skrótu środowiska).
Zaimplementujemy możliwość wprowadzania przez użytkownika własnego ciągu zapytania do adresu URL, dlatego ciąg ten będzie obecny w żądaniu HTTP, enkapsulowany jako wartość w jednej z par klucz / wartość skrótu środowiska.
Nasza aplikacja Rack uzyska dostęp do ciągu zapytania z mieszania środowiska i odeśle go z powrotem do klienta (w tym przypadku naszej przeglądarki) za pośrednictwem treści w odpowiedzi.
Z dokumentacji Rack na temat skrótu środowiska: „QUERY_STRING: Część adresu URL żądania, która następuje po znaku?, Jeśli istnieje. Może być pusta, ale zawsze wymagana!”
#./message_app.rb
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
Teraz rackup
i odwiedź localhost:9292?hello
( ?hello
jest ciąg zapytania) i powinieneś zobaczyć „witaj” renderowane w okienku ekranu.
Będziemy:
MessageSetter
,env
,MessageSetter
wstawi 'MESSAGE'
klucz do skrótu env, jego wartością jest 'Hello, World!'
if , jeśli env['QUERY_STRING']
jest pusty; env['QUERY_STRING']
Jeśli nie,@app.call(env)
- @app
będąc obok aplikacji w „stack”: MessageApp
.Po pierwsze, wersja „długiej ręki”:
#./middleware/message_setter.rb
class MessageSetter
def initialize(app)
@app = app
end
def call(env)
if env['QUERY_STRING'].empty?
env['MESSAGE'] = 'Hello, World!'
else
env['MESSAGE'] = env['QUERY_STRING']
end
@app.call(env)
end
end
#./message_app.rb (same as before)
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
#config.ru
require_relative './message_app'
require_relative './middleware/message_setter'
app = Rack::Builder.new do
use MessageSetter
run MessageApp.new
end
run app
Z dokumentacji Rack :: Builder widzimy, że Rack::Builder
implementuje małą DSL do iteracyjnego konstruowania aplikacji Rack. Zasadniczo oznacza to, że możesz zbudować „stos” składający się z co najmniej jednego oprogramowania pośredniego i aplikacji „najniższego poziomu” do wysłania. Wszystkie żądania przechodzące do aplikacji na najniższym poziomie zostaną najpierw przetworzone przez oprogramowanie pośrednie.
#use
określa oprogramowanie pośrednie, które ma być używane na stosie. Jako argument przyjmuje oprogramowanie pośrednie.
Oprogramowanie pośredniczące w szafie musi:
call
metodę, która przyjmuje jako parametr parametr skrótu środowiska.W naszym przypadku „Middleware” to MessageSetter
„konstruktor” to initialize
metoda MessageSetter , „następna aplikacja” na stosie MessageApp
.
Więc tutaj, ze względu na to, co Rack::Builder
robi pod maską app
argument MessageSetter
„s initialize
metody jest MessageApp
.
(obejrzyj się wyżej, zanim przejdziesz dalej)
Dlatego każdy element oprogramowania pośredniego zasadniczo „przekazuje” istniejący skrót środowiska do następnej aplikacji w łańcuchu - więc masz możliwość mutowania tego skrótu środowiska w oprogramowaniu pośrednim przed przekazaniem go do następnej aplikacji na stosie.
#run
pobiera argument, który jest obiektem, który odpowiada #call
i zwraca odpowiedź Rack (instancja Rack::Response
).
Korzystając z niego Rack::Builder
, możesz konstruować łańcuchy oprogramowania pośredniego, a każde żądanie aplikacji zostanie przetworzone przez każde oprogramowanie pośrednie, zanim ostatecznie zostanie przetworzone przez ostatni element stosu (w naszym przypadku MessageApp
). Jest to niezwykle przydatne, ponieważ oddziela różne etapy przetwarzania żądań. Jeśli chodzi o „rozdzielenie obaw”, nie może być dużo czystsze!
Możesz zbudować „potok zapytań” składający się z kilku Middlewares, które zajmują się takimi rzeczami jak:
(powyżej punktorów z innej odpowiedzi w tym wątku)
Często zobaczysz to w profesjonalnych aplikacjach Sinatra. Sinatra używa Rack! Zobacz tutaj do zdefiniowania co Sinatra JEST !
Na koniec config.ru
możemy napisać w skrócie, oferując dokładnie taką samą funkcjonalność (i to zwykle zobaczysz):
require_relative './message_app'
require_relative './middleware/message_setter'
use MessageSetter
run MessageApp.new
Aby dokładniej pokazać, co MessageApp
się dzieje, oto jego wersja „długiej ręki”, która wyraźnie pokazuje, że #call
tworzy nową instancję Rack::Response
, z wymaganymi trzema argumentami.
class MessageApp
def call(env)
Rack::Response.new([env['MESSAGE']], 200, {})
end
end
Rack - Interfejs b / w Web & App Server
Rack to pakiet Ruby, który zapewnia interfejs serwera WWW do komunikacji z aplikacją. Łatwo jest dodać składniki oprogramowania pośredniego między serwerem WWW a aplikacją, aby zmodyfikować sposób działania żądania / odpowiedzi. Komponent oprogramowania pośredniego znajduje się między klientem a serwerem, przetwarzając żądania przychodzące i odpowiedzi wychodzące.
Krótko mówiąc, jest to po prostu zestaw wskazówek, jak serwer i aplikacja Rails (lub jakakolwiek inna aplikacja internetowa Ruby) powinny ze sobą rozmawiać .
Aby użyć Rack, podaj „aplikację”: obiekt, który reaguje na metodę wywołania, przyjmując skrót środowiska jako parametr i zwracając tablicę z trzema elementami:
Aby uzyskać więcej wyjaśnień, możesz skorzystać z poniższych łączy.
1. https://rack.github.io/
2. https://redpanthers.co/rack-middleware/
3. https://blog.engineyard.com/2015/understanding-rack-apps-and-middleware
4. https://guides.rubyonrails.org/rails_on_rack.html#resources
W szynach mamy config.ru jako plik szafy, możesz uruchomić dowolny plik szafy za pomocą rackup
polecenia. Domyślny port to 9292
. Aby to przetestować, wystarczy uruchomić rackup
w katalogu Rails i zobaczyć wynik. Możesz także przypisać port, na którym chcesz go uruchomić. Polecenie uruchomienia pliku szafy na dowolnym określonym porcie to
rackup -p PORT_NUMBER
Rack to klejnot, który zapewnia prosty interfejs do abstrakcyjnego żądania / odpowiedzi HTTP. Rack znajduje się między szkieletami sieci (Rails, Sinatra itp.) A serwerami WWW (jednorożec, puma) jako adapter. Z powyższego obrazu utrzymuje to całkowicie niezależność serwera jednorożca od wiedzy o szynach, a szyny nie wiedzą o jednorożcu. To dobry przykład luźnego sprzężenia , oddzielenia problemów .
Powyższe zdjęcie pochodzi z tej konferencji na temat szyn na stojaku https://youtu.be/3PnUV9QzB0g. Polecam obejrzeć to dla głębszego zrozumienia.