Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

ユニコーンがnginxとどのように通信するか-RubyのUNIXソケットの紹介

Rubyアプリケーションサーバーは通常、nginxなどのWebサーバーと一緒に使用されます。ユーザーがRailsアプリからページをリクエストすると、nginxはそのリクエストをアプリケーションサーバーに委任します。しかし、それはどのように正確に機能しますか? nginxはユニコーンとどのように話しますか?

最も効率的なオプションの1つは、UNIXソケットを使用することです。それらがどのように機能するか見てみましょう!この投稿では、ソケットの基本から始めて、nginxによってプロキシされる独自のシンプルなアプリケーションサーバーを作成することで終わります。

ユニコーンがnginxとどのように通信するか-RubyのUNIXソケットの紹介 ソケットを使用すると、プログラムは、ファイルへの書き込みまたはファイルからの読み取りのように相互に通信できます。この例では、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

実例を示すために、最初にサーバーを実行する必要があります。次に、クライアントを実行します。以下の結果を見ることができます:

ユニコーンがnginxとどのように通信するか-RubyのUNIXソケットの紹介 単純な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は次のデータを私の小さなサーバーに送信します。

ユニコーンがnginxとどのように通信するか-RubyのUNIXソケットの紹介

かなりクール!これは、いくつかの追加ヘッダーが追加された通常の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とどのように通信するか-RubyのUNIXソケットの紹介 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に移動できます。リクエストは私のアプリに送信されます。そして、応答はブラウザによってレンダリングされます。かなりかっこいい!

ユニコーンがnginxとどのように通信するか-RubyのUNIXソケットの紹介 HTTPリクエストはシンプルなアプリサーバーによってSTDOUTに記録されます

そして、ここに私たちの労働の輝かしい実があります。見よ!タイムスタンプ!

ユニコーンがnginxとどのように通信するか-RubyのUNIXソケットの紹介 サーバーはブラウザに表示されるタイムスタンプを返します


  1. RubyでStructとOpenStructを使用する方法

    Rubyの構造体とは何ですか? 構造体は組み込みのRubyクラスであり、値オブジェクトを生成する新しいクラスを作成するために使用されます。値オブジェクトは、関連する属性を一緒に格納するために使用されます。 ここに例があります : Point 2つの座標(x &y 。 このデータはさまざまな方法で表すことができます。 いいね : 配列[10, 20] ハッシュ{ x: 10, y: 10 } オブジェクトPoint.new(10, 20) 複数のPointを使用する場合 、オブジェクトアプローチを使用することをお勧めします。 しかし… これら2つの値を一緒に格納するた

  2. Rubyネットワークプログラミング

    Rubyでカスタムネットワーククライアントとサーバーを作成しますか?または、それがどのように機能するかを理解しますか? 次に、ソケットを処理する必要があります。 このルビーネットワークプログラミングのツアーに参加してください 基本を学び、Rubyを使用して他のサーバーやクライアントと会話を始めましょう! では、ソケットとは何ですか ? ソケットは通信チャネルのエンドポイントであり、クライアントとサーバーの両方がソケットを使用して通信します。 動作方法は非常にシンプルです : 接続が確立されると、データをソケットに入れることができます。データはもう一方の端に送られ、そこで受信者はソケ