Computer >> Máy Tính >  >> Lập trình >> Ruby

Cách unicorn nói chuyện với nginx - Giới thiệu về ổ cắm unix trong Ruby

Các máy chủ ứng dụng Ruby thường được sử dụng cùng với một máy chủ web như nginx. Khi người dùng yêu cầu một trang từ ứng dụng Rails của bạn, nginx sẽ ủy quyền yêu cầu đó cho máy chủ ứng dụng. Nhưng chính xác thì điều đó hoạt động như thế nào? Nginx nói chuyện với kỳ lân như thế nào?

Một trong những lựa chọn hiệu quả nhất là sử dụng ổ cắm unix. Hãy xem chúng hoạt động như thế nào! Trong bài đăng này, chúng tôi sẽ bắt đầu với những kiến ​​thức cơ bản về socket và kết thúc bằng cách tạo máy chủ ứng dụng đơn giản của riêng chúng tôi được hỗ trợ bởi nginx.

Cách unicorn nói chuyện với nginx - Giới thiệu về ổ cắm unix trong Ruby Sockets cho phép các chương trình nói chuyện với nhau như thể chúng đang ghi hoặc đọc từ một tệp. Trong ví dụ này, Unicorn tạo socket và giám sát nó để tìm các kết nối. Sau đó, Nginx có thể kết nối với ổ cắm và nói chuyện với Unicorn.

Ổ cắm unix là gì?

Các ổ cắm Unix cho phép một chương trình nói chuyện với một chương trình khác theo cách tương tự như làm việc với các tệp. Chúng là một loại IPC hoặc giao tiếp giữa các quá trình.

Để có thể truy cập thông qua một ổ cắm, trước tiên chương trình của bạn tạo một ổ cắm và "lưu" nó vào đĩa, giống như một tệp. Nó giám sát ổ cắm cho các kết nối đến. Khi nhận được một, nó sử dụng các phương thức IO tiêu chuẩn để đọc và ghi dữ liệu.

Ruby cung cấp mọi thứ bạn cần để làm việc với các ổ cắm unix thông qua một vài lớp:

  • UNIX * Máy chủ * - Nó tạo ra ổ cắm, lưu nó vào đĩa và cho phép bạn theo dõi nó để tìm các kết nối mới.

  • UNIX * Ổ cắm * - Mở các ổ cắm hiện có cho IO.

LƯU Ý: Các loại ổ cắm khác tồn tại. Đáng chú ý nhất là các ổ cắm TCP. Nhưng bài đăng này chỉ đề cập đến các ổ cắm unix. Làm thế nào để bạn biết sự khác biệt? Các ổ cắm Unix có tên tệp.

Ổ cắm Đơn giản nhất

Chúng ta sẽ xem xét hai chương trình nhỏ.

Đầu tiên là "máy chủ". Nó chỉ tạo một phiên bản của UnixServer lớp, sau đó sử dụng server.accept để chờ kết nối. Khi nhận được kết nối, nó sẽ trao đổi một lời chào.

Cần lưu ý rằng cả acceptreadline các phương thức chặn việc thực thi chương trình cho đến khi chúng nhận được những gì chúng đang chờ đợi.

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

Vì vậy, chúng tôi có một máy chủ. Bây giờ, tất cả những gì chúng tôi cần là một khách hàng.

Trong ví dụ dưới đây, chúng tôi mở socket được tạo bởi máy chủ của chúng tôi. Sau đó, chúng tôi sử dụng các phương pháp IO thông thường để gửi và nhận lời chào.

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

Để chứng minh, trước tiên chúng ta cần chạy máy chủ. Sau đó, chúng tôi chạy khách hàng. Bạn có thể xem kết quả bên dưới:

Cách unicorn nói chuyện với nginx - Giới thiệu về ổ cắm unix trong Ruby Ví dụ về tương tác máy khách / máy chủ ổ cắm UNIX đơn giản. Khách hàng ở bên trái. Máy chủ ở bên phải.

Giao diện với nginx

Bây giờ chúng ta đã biết cách tạo một "máy chủ" unix socket, chúng ta có thể dễ dàng giao tiếp với nginx.

Không tin tôi? Hãy thực hiện một bằng chứng nhanh chóng về khái niệm. Tôi sẽ điều chỉnh mã ở trên để làm cho nó in ra mọi thứ mà nó nhận được từ socket.

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

Bây giờ nếu tôi định cấu hình nginx để chuyển tiếp các yêu cầu tới socket tại /tmp/socktest.sock Tôi có thể xem dữ liệu nginx đang gửi. (Đừng lo lắng, chúng ta sẽ thảo luận về cấu hình sau một phút nữa)

Khi tôi thực hiện một yêu cầu web, nginx sẽ gửi dữ liệu sau đến máy chủ nhỏ của tôi:

Cách unicorn nói chuyện với nginx - Giới thiệu về ổ cắm unix trong Ruby

Tuyệt đấy! Đó chỉ là một yêu cầu HTTP bình thường với một vài tiêu đề bổ sung được thêm vào. Bây giờ chúng tôi đã sẵn sàng để xây dựng một máy chủ ứng dụng thực sự. Nhưng trước tiên, hãy thảo luận về cấu hình nginx.

Cài đặt và định cấu hình Nginx

Nếu bạn chưa cài đặt nginx trên máy phát triển của mình, hãy dành một chút thời gian và thực hiện điều đó ngay bây giờ. Nó thực sự dễ dàng trên OSX thông qua homebrew:

brew install nginx

Bây giờ chúng ta cần cấu hình nginx để chuyển tiếp các yêu cầu trên localhost:2048 tới máy chủ ngược dòng thông qua một ổ cắm có tên /tmp/socktest.sock . Cái tên đó không có gì đặc biệt. Nó chỉ cần khớp với tên ổ cắm được sử dụng bởi máy chủ web của chúng tôi.

Bạn có thể lưu cấu hình này vào /tmp/nginx.conf và sau đó chạy nginx bằng lệnh nginx -c /tmp/nginx.conf để tải nó.

# 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;
    }

  }
}

Cấu hình này khiến nginx chạy như một ứng dụng đầu cuối bình thường, không giống như một daemon. Nó cũng ghi tất cả các bản ghi vào stdout. Khi bạn chạy nginx, nó sẽ trông giống như sau:

Cách unicorn nói chuyện với nginx - Giới thiệu về ổ cắm unix trong Ruby Nginx đang chạy ở chế độ không phải daemon.

Máy chủ ứng dụng tự làm

Bây giờ chúng ta đã thấy cách kết nối nginx với chương trình của mình, đó là một vấn đề khá đơn giản để xây dựng một máy chủ ứng dụng đơn giản. Khi nginx chuyển tiếp một yêu cầu tới socket của chúng tôi, đó là một yêu cầu HTTP tiêu chuẩn. Sau khi mày mò một chút, tôi đã có thể xác định rằng nếu socket trả về phản hồi HTTP hợp lệ, thì nó sẽ được hiển thị trong trình duyệt.

Ứng dụng dưới đây nhận bất kỳ yêu cầu nào và hiển thị dấu thời gian.

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

Bây giờ nếu tôi kích hoạt nginx cũng như tập lệnh của mình, tôi có thể truy cập localhost:2048. Yêu cầu được gửi đến ứng dụng của tôi. Và phản hồi được hiển thị bởi trình duyệt. Khá tuyệt!

Cách unicorn nói chuyện với nginx - Giới thiệu về ổ cắm unix trong Ruby Các yêu cầu HTTP được ghi vào STDOUT bởi máy chủ ứng dụng đơn giản của chúng tôi

Và đây là thành quả vinh quang của sự lao động của chúng ta. Hãy chứng kiến! Dấu thời gian!

Cách unicorn nói chuyện với nginx - Giới thiệu về ổ cắm unix trong Ruby Máy chủ trả về dấu thời gian được hiển thị trong trình duyệt