ユニコーンがnginxとどのように通信するか-RubyのUNIXソケットの紹介
Rubyアプリケーションサーバーは通常、nginxなどのWebサーバーと一緒に使用されます。ユーザーがRailsアプリからページをリクエストすると、nginxはそのリクエストをアプリケーションサーバーに委任します。しかし、それはどのように正確に機能しますか? nginxはユニコーンとどのように話しますか?
最も効率的なオプションの1つは、UNIXソケットを使用することです。それらがどのように機能するか見てみましょう!この投稿では、ソケットの基本から始めて、nginxによってプロキシされる独自のシンプルなアプリケーションサーバーを作成することで終わります。
ソケットを使用すると、プログラムは、ファイルへの書き込みまたはファイルからの読み取りのように相互に通信できます。この例では、Unicornがソケットを作成し、接続を監視します。その後、Nginxはソケットに接続して、Unicornと通信できます。
UNIXソケットとは何ですか?
Unixソケットを使用すると、あるプログラムが別のプログラムと、ファイルの操作に似た方法で通信できるようになります。これらは一種のIPC、つまりプロセス間通信です。
ソケットを介してアクセスできるように、プログラムは最初にソケットを作成し、ファイルと同じようにディスクに「保存」します。ソケットの着信接続を監視します。受信すると、標準のIOメソッドを使用してデータの読み取りと書き込みを行います。
Rubyは、いくつかのクラスを介してUNIXソケットでの作業に必要なすべてを提供します。
-
UNIX*サーバー *-ソケットを作成してディスクに保存し、新しい接続を監視できるようにします。
-
UNIX*ソケット *-IO用の既存のソケットを開きます。
注: 他の種類のソケットが存在します。最も顕著なのはTCPソケットです。しかし、この投稿はUNIXソケットのみを扱っています。違いはどうやってわかりますか? Unixソケットにはファイル名があります。
2つの小さなプログラムを見ていきます。
1つ目は「サーバー」です。 UnixServer
のインスタンスを作成するだけです クラス、次にserver.accept
を使用します 接続を待ちます。接続を受信すると、挨拶を交換します。
両方のaccept
が およびreadline
メソッドは、待機しているものを受け取るまでプログラムの実行をブロックします。
require "socket"
server = UNIXServer.new('/tmp/simple.sock')
puts "==== Waiting for connection"
socket = server.accept
puts "==== Got Request:"
puts socket.readline
puts "==== Sending Response"
socket.write("I read you loud and clear, good buddy!")
socket.close
これでサーバーができました。今必要なのはクライアントだけです。
以下の例では、サーバーによって作成されたソケットを開きます。次に、通常のIOメソッドを使用して挨拶を送受信します。
require "socket"
socket = UNIXSocket.new('/tmp/simple.sock')
puts "==== Sending"
socket.write("Hello server, can you hear me?\n")
puts "==== Getting Response"
puts socket.readline
socket.close
実例を示すために、最初にサーバーを実行する必要があります。次に、クライアントを実行します。以下の結果を見ることができます:
単純なUNIXソケットクライアント/サーバーの相互作用の例。クライアントは左側にあります。サーバーは右側にあります。
nginxとのインターフェース
UNIXソケット「サーバー」を作成する方法がわかったので、nginxと簡単にインターフェースできます。
私を信じないの?簡単な概念実証を行いましょう。上記のコードを適応させて、ソケットから受け取ったすべてのものを印刷するようにします。
require "socket"
# Create the socket and "save it" to the file system
server = UNIXServer.new('/tmp/socktest.sock')
# Wait until for a connection (by nginx)
socket = server.accept
# Read everything from the socket
while line = socket.readline
puts line.inspect
end
socket.close
ここで、リクエストを/tmp/socktest.sock
のソケットに転送するようにnginxを構成するとします。 nginxが送信しているデータを確認できます。 (心配しないでください。構成についてはすぐに説明します)
Webリクエストを行うと、nginxは次のデータを私の小さなサーバーに送信します。
かなりクール!これは、いくつかの追加ヘッダーが追加された通常のHTTPリクエストです。これで、実際のアプリサーバーを構築する準備が整いました。ただし、最初に、nginxの構成について説明しましょう。
Nginxのインストールと構成
開発マシンにnginxをまだインストールしていない場合は、少し時間を取って今すぐインストールしてください。 OSXでは自作でとても簡単です:
brew install nginx
次に、localhost:2048のリクエストを/tmp/socktest.sock
という名前のソケットを介してアップストリームサーバーに転送するようにnginxを構成する必要があります。 。その名前は特別なものではありません。 Webサーバーで使用されているソケット名と一致する必要があります。
この構成を/tmp/nginx.conf
に保存できます 次に、コマンドnginx -c /tmp/nginx.conf
を使用してnginxを実行します ロードします。
# Run nginx as a normal console program, not as a daemon
daemon off;
# Log errors to stdout
error_log /dev/stdout info;
events {} # Boilerplate
http {
# Print the access log to stdout
access_log /dev/stdout;
# Tell nginx that there's an external server called @app living at our socket
upstream app {
server unix:/tmp/socktest.sock fail_timeout=0;
}
server {
# Accept connections on localhost:2048
listen 2048;
server_name localhost;
# Application root
root /tmp;
# If a path doesn't exist on disk, forward the request to @app
try_files $uri/index.html $uri @app;
# Set some configuration options on requests forwarded to @app
location @app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass https://app;
}
}
}
この構成により、nginxはデーモンではなく、通常のターミナルアプリのように実行されます。また、すべてのログをstdoutに書き込みます。 nginxを実行すると、次のようになります。
Nginxは非デーモンモードで実行されています。
DIYアプリケーションサーバー
nginxをプログラムに接続する方法を確認したので、単純なアプリケーションサーバーを構築するのは非常に簡単です。 nginxがリクエストをソケットに転送する場合、それは標準のHTTPリクエストです。少しいじくり回した後、ソケットが有効なHTTP応答を返す場合、それがブラウザに表示されることを確認できました。
以下のアプリケーションは、リクエストを受け取り、タイムスタンプを表示します。
require "socket"
# Connection creates the socket and accepts new connections
class Connection
attr_accessor :path
def initialize(path:)
@path = path
File.unlink(path) if File.exists?(path)
end
def server
@server ||= UNIXServer.new(@path)
end
def on_request
socket = server.accept
yield(socket)
socket.close
end
end
# AppServer logs incoming requests and renders a view in response
class AppServer
attr_reader :connection
attr_reader :view
def initialize(connection:, view:)
@connection = connection
@view = view
end
def run
while true
connection.on_request do |socket|
while (line = socket.readline) != "\r\n"
puts line
end
socket.write(view.render)
end
end
end
end
# TimeView simply provides the HTTP response
class TimeView
def render
%[HTTP/1.1 200 OK
The current timestamp is: #{ Time.now.to_i }
]
end
end
AppServer.new(connection: Connection.new(path: '/tmp/socktest.sock'), view: TimeView.new).run
これで、スクリプトだけでなくnginxも起動すると、localhost:2048に移動できます。リクエストは私のアプリに送信されます。そして、応答はブラウザによってレンダリングされます。かなりかっこいい!
HTTPリクエストはシンプルなアプリサーバーによってSTDOUTに記録されます
そして、ここに私たちの労働の輝かしい実があります。見よ!タイムスタンプ!
サーバーはブラウザに表示されるタイムスタンプを返します
-
RubyでStructとOpenStructを使用する方法
Rubyの構造体とは何ですか? 構造体は組み込みのRubyクラスであり、値オブジェクトを生成する新しいクラスを作成するために使用されます。値オブジェクトは、関連する属性を一緒に格納するために使用されます。 ここに例があります : Point 2つの座標(x &y 。 このデータはさまざまな方法で表すことができます。 いいね : 配列[10, 20] ハッシュ{ x: 10, y: 10 } オブジェクトPoint.new(10, 20) 複数のPointを使用する場合 、オブジェクトアプローチを使用することをお勧めします。 しかし… これら2つの値を一緒に格納するた
-
Rubyネットワークプログラミング
Rubyでカスタムネットワーククライアントとサーバーを作成しますか?または、それがどのように機能するかを理解しますか? 次に、ソケットを処理する必要があります。 このルビーネットワークプログラミングのツアーに参加してください 基本を学び、Rubyを使用して他のサーバーやクライアントと会話を始めましょう! では、ソケットとは何ですか ? ソケットは通信チャネルのエンドポイントであり、クライアントとサーバーの両方がソケットを使用して通信します。 動作方法は非常にシンプルです : 接続が確立されると、データをソケットに入れることができます。データはもう一方の端に送られ、そこで受信者はソケ