Ruby에서 Rack 미들웨어 란 무엇입니까? 나는 "미들웨어"의 의미에 대한 좋은 설명을 찾을 수 없었다.
Ruby에서 Rack 미들웨어 란 무엇입니까? 나는 "미들웨어"의 의미에 대한 좋은 설명을 찾을 수 없었다.
답변:
랙 미들웨어는 "요청 및 응답을 필터링하는 방법"이상 입니다. 이는 Rack을 사용하는 웹 서버에 대한 파이프 라인 디자인 패턴 의 구현입니다 .
요청을 처리하는 여러 단계를 깨끗하게 분리합니다. 잘 분리 된 모든 소프트웨어 제품의 주요 목표는 관심사 분리입니다.
예를 들어 랙 I을 사용하면 다음과 같은 별도의 파이프 라인 단계를 수행 할 수 있습니다.
인증 : 요청이 도착하면 사용자 로그온 세부 정보가 정확합니까? 이 OAuth, HTTP 기본 인증, 이름 / 암호를 어떻게 확인합니까?
권한 부여 : "사용자가이 특정 작업을 수행 할 권한이 있습니까?", 즉 역할 기반 보안.
캐싱 :이 요청을 이미 처리 했습니까? 캐시 된 결과를 반환 할 수 있습니까?
장식 : 다운 스트림 처리를 개선하기위한 요청을 어떻게 강화할 수 있습니까?
성능 및 사용 모니터링 : 요청 및 응답에서 어떤 통계를 얻을 수 있습니까?
실행 : 실제로 요청을 처리하고 응답을 제공합니다.
서로 다른 단계를 분리하고 선택적으로 포함 할 수 있다는 것은 체계적인 응용 프로그램 개발에 큰 도움이됩니다.
Rack Middleware를 중심으로 개발 된 훌륭한 에코 시스템도 있습니다. 위의 모든 단계를 수행하기 위해 사전 구축 된 랙 구성 요소를 찾을 수 있어야합니다. 미들웨어 목록은 Rack GitHub 위키를 참조하십시오 .
미들웨어는 일부 작업 실행을 지원하지만 직접 수행하지 않는 소프트웨어 구성 요소 / 라이브러리를 의미하는 무서운 용어입니다. 매우 일반적인 예는 로깅, 인증 및 기타 일반적인 수평 처리 구성 요소 입니다. 이들은 여러 응용 프로그램에서 모든 사람이 필요로하는 경향이 있지만 너무 많은 사람들이 자신을 구축하는 데 관심이 있거나 있어야합니다.
요청을 필터링하는 방법에 대한 의견은 아마도 RailsCast 에피소드 151 : 랙 미들웨어 화면 캐스트 에서 비롯된 것입니다 .
미들웨어는 랙에서 진화에 큰 소개가 랙 미들웨어를 랙에 소개 .
Wikipedia에 대한 미들웨어 소개가 있습니다 .
우선 랙은 정확히 두 가지입니다.
랙-웹 서버 인터페이스
랙의 기본 사항은 간단한 규칙입니다. 모든 랙 호환 웹 서버는 항상 사용자가 제공 한 객체에서 호출 메소드를 호출하고 해당 메소드의 결과를 제공합니다. Rack은이 호출 방법의 모양과 반환 할 내용을 정확하게 지정합니다. 랙입니다.
간단한 시도를 해보자. WEBrick을 랙 호환 웹 서버로 사용하지만 그 중 하나는 사용합니다. JSON 문자열을 반환하는 간단한 웹 응용 프로그램을 만들어 봅시다. 이를 위해 config.ru라는 파일을 만듭니다. 랙 gem의 명령 랙업에 의해 config.ru가 자동으로 호출되어 랙 호환 웹 서버에서 config.ru의 내용을 간단히 실행합니다. config.ru 파일에 다음을 추가하십시오 :
class JSONServer
def call(env)
[200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
end
end
map '/hello.json' do
run JSONServer.new
end
규약에 따라 서버에는 환경 해시를 허용하고 웹 서버가 제공 할 [상태, 헤더, 본문] 형식의 배열을 반환하는 call이라는 메소드가 있습니다. 간단히 랙업을 호출하여 사용해 봅시다. 기본 랙 호환 서버 (WEBrick 또는 Mongrel)가 시작되고 즉시 요청이 처리 될 때까지 기다립니다.
$ 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
url http://localhost:9292/hello.json
과 voila를 말리거나 방문하여 새 JSON 서버를 테스트 해 보겠습니다 .
$ curl http://localhost:9292/hello.json
{ message: "Hello!" }
효과가있다. 큰! 이것이 Rails 또는 Sinatra와 같은 모든 웹 프레임 워크의 기초입니다. 어느 시점에서 그들은 호출 메소드를 구현하고 모든 프레임 워크 코드를 통해 작업하며 마지막으로 전형적인 [상태, 헤더, 본문] 형식으로 응답을 반환합니다.
예를 들어 Ruby on Rails에서 랙 요청 ActionDispatch::Routing.Mapper
은 다음과 같은 클래스에 도달합니다.
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
따라서 기본적으로 Rails 검사는 경로가 일치하는 경우 env 해시에 따라 다릅니다. 그렇다면 응답을 계산하기 위해 env 해시를 응용 프로그램에 전달하고, 그렇지 않으면 즉시 404로 응답합니다. 따라서 랙 인터페이스 규칙을 준수하는 모든 웹 서버는 완전히 중단 된 Rails 응용 프로그램을 제공 할 수 있습니다.
미들웨어
랙은 또한 미들웨어 계층 작성을 지원합니다. 그들은 기본적으로 요청을 가로 채고, 무언가로 무언가를하고, 전달합니다. 다목적 작업에 매우 유용합니다.
요청이 걸리는 시간을 측정하는 로깅을 JSON 서버에 추가한다고 가정 해 봅시다. 정확히이 작업을 수행하는 미들웨어 로거를 작성할 수 있습니다.
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
생성되면 실제 랙 응용 프로그램의 사본을 저장합니다. 우리의 경우 이것은 JSONServer의 인스턴스입니다. Rack은 자동으로 미들웨어에서 호출 메소드를 호출 [status, headers, body]
하고 JSONServer가 리턴하는 것처럼 배열을 다시 기대 합니다.
따라서이 미들웨어에서는 시작점이 사용되고 JSONServer에 대한 실제 호출이로 수행 된 @app.call(env)
후 로거는 로깅 항목을 출력하고 마지막으로 응답을로 리턴합니다 [@status, @headers, @body]
.
우리의 작은 rackup.ru가이 미들웨어를 사용하게하려면, 다음과 같이 RackLogger 사용을 추가하십시오 :
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
서버와 voila를 다시 시작하면 모든 요청에 대한 로그가 출력됩니다. 랙을 사용하면 추가 된 순서대로 호출 된 여러 미들웨어를 추가 할 수 있습니다. 랙 응용 프로그램의 핵심을 변경하지 않고 기능을 추가 할 수있는 좋은 방법입니다.
랙-보석
랙은 우선 컨벤션이지만 훌륭한 기능을 제공하는 보석이기도합니다. 그중 하나는 이미 JSON 서버에 사용 된 랙업 명령입니다. 그러나 더 있습니다! 랙 젬은 정적 파일이나 전체 디렉토리를 제공하는 것과 같이 많은 사용 사례를위한 작은 응용 프로그램을 제공합니다. htmls / index.html에있는 매우 기본적인 HTML 파일과 같이 간단한 파일을 제공하는 방법을 살펴 보겠습니다.
<!DOCTYPE HTML>
<html>
<head>
<title>The Index</title>
</head>
<body>
<p>Index Page</p>
</body>
</html>
웹 사이트 루트에서이 파일을 제공하고 싶을 수도 있으므로 config.ru에 다음을 추가하십시오.
map '/' do
run Rack::File.new "htmls/index.html"
end
우리가 방문하면 http://localhost:9292
html 파일이 완벽하게 렌더링됩니다. 쉬운 일 이지요?
/ javascripts 아래에 일부 javascript 파일을 작성하고 config.ru에 다음을 추가하여 javascript 파일의 전체 디렉토리를 추가하십시오.
map '/javascripts' do
run Rack::Directory.new "javascripts"
end
서버를 다시 시작하고 방문 http://localhost:9292/javascript
하면 이제 어디서나 포함 할 수있는 모든 자바 스크립트 파일 목록이 표시됩니다.
랙을 충분히 이해하는 데 문제가있었습니다. 이 소형 Ruby 웹 서버를 직접 제작 한 후에 만 완전히 이해했습니다 . 내 블로그에서 랙에 대한 학습 내용을 이야기 형태로 공유했습니다. http://gauravchande.com/what-is-rack-in-ruby-rails
피드백은 환영 이상입니다.
config.ru
최소한의 실행 가능한 예
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)
실행 rackup
하고 방문하십시오 localhost:9292
. 출력은 다음과 같습니다.
main
Middleware
따라서 Middleware
랩핑하고 메인 앱을 호출하는 것이 분명합니다 . 따라서 요청을 사전 처리하고 어떤 방식 으로든 응답을 사후 처리 할 수 있습니다.
http://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack 에서 설명했듯이 Rails는 많은 기능을 위해 Rack 미들웨어를 사용하며 config.middleware.use
가족 방법으로 도 자신을 추가 할 수 있습니다 .
미들웨어에서 기능을 구현하면 장점은 Rails뿐만 아니라 모든 주요 Ruby 프레임 워크에서 모든 Rack 프레임 워크에서 재사용 할 수 있다는 것입니다.
랙 미들웨어는 응용 프로그램으로 들어오는 요청과 응답을 필터링하는 방법입니다. 미들웨어 구성 요소는 클라이언트와 서버 사이에 위치하여 인바운드 요청 및 아웃 바운드 응답을 처리하지만 웹 서버와 통신하는 데 사용할 수있는 인터페이스 이상입니다. 일반적으로 Ruby 클래스 인 모듈을 그룹화하고 순서를 지정하고 모듈 간의 종속성을 지정하는 데 사용됩니다. 랙 미들웨어 모듈은 다음을 수행해야합니다. – 스택에서 다음 애플리케이션을 매개 변수로 사용하는 생성자 – 환경 호출을 매개 변수로 사용하는 "호출"메소드에 응답합니다. 이 호출에서 반환되는 값은 상태 코드, 환경 해시 및 응답 본문의 배열입니다.
랙 미들웨어를 사용하여 몇 가지 문제를 해결했습니다.
두 경우 모두 꽤 우아한 수정을 제공했습니다.
Rack은 Ruby와 Ruby 프레임 워크를 지원하는 웹 서버간에 최소한의 인터페이스를 제공합니다.
Rack을 사용하여 Rack Application을 작성할 수 있습니다.
랙은 환경 해시 (CGI와 같은 헤더로 구성된 클라이언트의 HTTP 요청에 포함 된 해시)를 랙 응용 프로그램으로 전달하여이 해시에 포함 된 항목을 사용하여 원하는 작업을 수행 할 수 있습니다.
Rack을 사용하려면 #call
환경 해시를 매개 변수로 사용하여 메소드에 응답하는 객체 인 'app'를 제공해야합니다 (일반적으로로 정의 됨 env
). #call
정확히 세 개의 값으로 구성된 배열을 반환해야합니다.
each
).이러한 배열을 반환하는 랙 응용 프로그램을 작성할 수 있습니다.이 응용 프로그램은 응답 내에서 랙에 의해 클라이언트로 다시 전송 됩니다 (실제로 는 클래스 의 인스턴스 입니다 Rack::Response
[docs로 이동]).
gem install rack
config.ru
파일 만들기 -Rack은 이것을 찾아냅니다.Rack::Response
Response Body가 String :을 포함하는 배열 인 Response (인스턴스 ) 를 반환하는 작은 랙 응용 프로그램을 만듭니다 "Hello, World!"
.
명령을 사용하여 로컬 서버를 시작합니다 rackup
.
브라우저에서 관련 포트를 방문하면 "Hello, World!"가 표시됩니다. 뷰포트에서 렌더링됩니다.
#./message_app.rb
class MessageApp
def call(env)
[200, {}, ['Hello, World!']]
end
end
#./config.ru
require_relative './message_app'
run MessageApp.new
로 로컬 서버를 시작 rackup
하고 localhost : 9292를 방문 하면 'Hello, World!'가 표시됩니다. 렌더링.
이것은 포괄적 인 설명이 아니지만 본질적으로 클라이언트 (브라우저)가 로컬 서버를 통해 랙에 HTTP 요청을 보내고 랙이 인스턴스화 MessageApp
되고 실행 call
되어 환경 해시를 메소드의 매개 변수로 전달합니다 ( env
인수).
Rack은 반환 값 (어레이)을 가져 와서이를 사용하여 인스턴스를 생성하고이를 Rack::Response
클라이언트로 다시 보냅니다. 브라우저는 마법 을 사용 하여 'Hello, World!'를 인쇄합니다. 화면에.
또한 환경 해시가 어떻게 보이는지 보려면 puts env
아래에 넣으십시오 def call(env)
.
최소한, 여기서 작성한 것은 Rack 응용 프로그램입니다!
Little Rack 앱에서 env
해시 와 상호 작용할 수 있습니다 ( 환경 해시에 대한 자세한 내용 은 여기 참조 ).
사용자는 자신의 쿼리 문자열을 URL에 입력 할 수있는 기능을 구현할 것이므로 해당 문자열은 HTTP 요청에 존재하며 환경 해시의 키 / 값 쌍 중 하나의 값으로 캡슐화됩니다.
랙 앱은 환경 해시에서 해당 쿼리 문자열에 액세스하여 응답의 본문을 통해 클라이언트 (이 경우 브라우저)로 다시 보냅니다.
환경 해시의 랙 문서에서 : "QUERY_STRING :? 뒤에 오는 요청 URL 부분입니다 (있는 경우). 비어있을 수 있지만 항상 필요합니다!"
#./message_app.rb
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
이제 ( 쿼리 문자열)를 rackup
방문 하면 뷰포트에 'hello'가 렌더링됩니다.localhost:9292?hello
?hello
우리는 :
MessageSetter
,env
,MessageSetter
삽입합니다 'MESSAGE'
ENV 해시에 키를, 그 값의 존재는 'Hello, World!'
경우 env['QUERY_STRING']
비어 있습니다; env['QUERY_STRING']
그렇지 않다면@app.call(env)
- @app
다음 '스택'의 다음 응용되고 MessageApp
.먼저 '긴 손'버전 :
#./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
로부터 랙 :: 빌더 문서 우리는 그 참조 Rack::Builder
구현 반복적 구조 랙 애플리케이션에 작은 DSL을. 이것은 기본적으로 하나 이상의 미들웨어로 구성되는 '스택'과 디스패치 할 '맨 아래 레벨'애플리케이션을 빌드 할 수 있음을 의미합니다. 최하위 레벨 애플리케이션으로 진행되는 모든 요청은 먼저 미들웨어에 의해 처리됩니다.
#use
스택에서 사용할 미들웨어를 지정합니다. 미들웨어를 인수로 사용합니다.
랙 미들웨어는 다음을 충족해야합니다.
call
환경 해시를 매개 변수로 사용하는 메소드에 응답하십시오 .우리의 경우 'Middleware'는 MessageSetter
'constructor'는 MessageSetter의 initialize
방법이며 스택의 'next application'은 MessageApp
입니다.
따라서 여기 Rack::Builder
에서 후드의 기능 때문에 의 메소드 app
인수 는 입니다.MessageSetter
initialize
MessageApp
(이동하기 전에 위의 머리를 잡으십시오)
따라서 각 미들웨어는 기존 환경 해시를 체인의 다음 애플리케이션으로 '통과'합니다. 따라서 미들웨어 내에서 환경 해시를 변경하여 스택의 다음 애플리케이션으로 전달할 수 있습니다.
#run
#call
랙 응답 (의 인스턴스 Rack::Response
)에 응답하고 반환 하는 객체 인 인수를 사용합니다 .
사용 Rack::Builder
하면 미들웨어 체인을 구성 할 수 있으며 애플리케이션에 대한 모든 요청은 각 미들웨어에서 차례로 처리되기 전에 스택의 최종 부분 (이 경우에는 MessageApp
)에서 처리됩니다. 처리 요청의 다른 단계를 분리하기 때문에 매우 유용합니다. '문제의 분리'의 관점에서 볼 때 훨씬 깨끗하지 못했습니다!
다음과 같은 것을 처리하는 여러 미들웨어로 구성된 '요청 파이프 라인'을 구성 할 수 있습니다.
(이 스레드에 대한 다른 답변의 글 머리 기호 위)
전문적인 Sinatra 응용 프로그램에서이 사실을 종종 볼 수 있습니다. Sinatra는 Rack을 사용합니다! Sinatra IS 의 정의에 대해서는 여기 를 참조 하십시오 !
마지막으로, 우리 config.ru
는 짧은 스타일로 작성되어 정확히 동일한 기능을 생성 할 수 있습니다 (이것은 일반적으로 볼 수 있습니다).
require_relative './message_app'
require_relative './middleware/message_setter'
use MessageSetter
run MessageApp.new
그리고 무엇 MessageApp
을하고 있는지보다 명확하게 보여주기 위해 , 여기 에 필요한 3 개의 인수를 가진의 #call
새로운 인스턴스를 생성하고 있음을 명시 적으로 보여주는 'long-hand'버전이 Rack::Response
있습니다.
class MessageApp
def call(env)
Rack::Response.new([env['MESSAGE']], 200, {})
end
end
랙-웹 및 앱 서버 간 인터페이스
랙은 웹 서버가 응용 프로그램과 통신 할 수있는 인터페이스를 제공하는 Ruby 패키지입니다. 웹 서버와 앱 사이에 미들웨어 구성 요소를 쉽게 추가하여 요청 / 응답 작동 방식을 수정할 수 있습니다. 미들웨어 구성 요소는 클라이언트와 서버 사이에 위치하여 인바운드 요청 및 아웃 바운드 응답을 처리합니다.
평신도로서, 기본적으로 서버와 Rails 앱 (또는 다른 Ruby 웹 앱)이 서로 통신하는 방법에 대한 일련의 지침 일뿐 입니다.
Rack을 사용하려면 "app": 콜 메서드에 응답하고 환경 해시를 매개 변수로 사용하고 세 가지 요소가있는 Array를 반환하는 객체를 제공하십시오.
자세한 설명은 아래 링크를 참조하십시오.
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
레일에는 랙 파일로 config.ru가 있으며 rackup
명령을 사용 하여 모든 랙 파일을 실행할 수 있습니다 . 그리고 기본 포트는 9292
입니다. 이를 테스트하기 위해 간단히 rackup
rails 디렉토리에서 실행 하여 결과를 볼 수 있습니다. 실행할 포트를 지정할 수도 있습니다. 특정 포트에서 랙 파일을 실행하는 명령은
rackup -p PORT_NUMBER
Rack은 HTTP 요청 / 응답을 추상화하기위한 간단한 인터페이스를 제공하는 gem입니다. 랙은 웹 프레임 워크 (Rails, Sinatra 등)와 웹 서버 (유니콘, 퓨마) 사이에 어댑터로 사용됩니다. 위의 이미지에서 이것은 유니콘 서버가 레일에 대해 아는 것과 완전히 독립적이며 레일은 유니콘에 대해 알지 못합니다. 이것은 느슨한 커플 링 , 문제 분리의 좋은 예입니다 .
위의 이미지는 랙에서 진행되는이 레일스 컨퍼런스 토크입니다. https://youtu.be/3PnUV9QzB0g 더 깊이 이해하려면 시청하는 것이 좋습니다.