রুবি অ্যাপ্লিকেশন সার্ভারগুলি সাধারণত nginx এর মতো একটি ওয়েব সার্ভারের সাথে একসাথে ব্যবহৃত হয়। যখন ব্যবহারকারী আপনার Rails অ্যাপ থেকে একটি পৃষ্ঠার অনুরোধ করে, nginx অ্যাপ্লিকেশন সার্ভারে অনুরোধটি অর্পণ করে। কিন্তু ঠিক কিভাবে কাজ করে? কিভাবে nginx ইউনিকর্নের সাথে কথা বলে?
সবচেয়ে কার্যকরী বিকল্পগুলির মধ্যে একটি হল ইউনিক্স সকেট ব্যবহার করা। দেখা যাক কিভাবে তারা কাজ করে! এই পোস্টে আমরা সকেটের মৌলিক বিষয়গুলি দিয়ে শুরু করব এবং আমাদের নিজস্ব সাধারণ অ্যাপ্লিকেশন সার্ভার তৈরি করে শেষ করব যা nginx দ্বারা প্রক্সি করা হয়৷
সকেটগুলি প্রোগ্রামগুলিকে একে অপরের সাথে কথা বলতে দেয় যেন তারা একটি ফাইলে লিখছে বা পড়ছে। এই উদাহরণে, ইউনিকর্ন সকেট তৈরি করে এবং সংযোগের জন্য এটি নিরীক্ষণ করে। Nginx তারপর সকেটের সাথে সংযোগ করতে পারে এবং ইউনিকর্নের সাথে কথা বলতে পারে।
ইউনিক্স সকেট কি?
ইউনিক্স সকেটগুলি একটি প্রোগ্রামকে অন্যটির সাথে এমনভাবে কথা বলতে দেয় যা ফাইলগুলির সাথে কাজ করার মতো। এগুলি এক ধরনের আইপিসি, বা আন্তঃপ্রক্রিয়া যোগাযোগ।
একটি সকেটের মাধ্যমে অ্যাক্সেসযোগ্য হতে, আপনার প্রোগ্রামটি প্রথমে একটি সকেট তৈরি করে এবং এটিকে একটি ফাইলের মতো ডিস্কে "সংরক্ষণ" করে। এটি ইনকামিং সংযোগের জন্য সকেট নিরীক্ষণ করে। যখন এটি একটি পায়, এটি ডেটা পড়তে এবং লিখতে স্ট্যান্ডার্ড IO পদ্ধতি ব্যবহার করে।
রুবি কয়েকটি ক্লাসের মাধ্যমে ইউনিক্স সকেটের সাথে কাজ করার জন্য আপনার প্রয়োজনীয় সবকিছু সরবরাহ করে:
-
UNIX*সার্ভার *- এটি সকেট তৈরি করে, ডিস্কে সংরক্ষণ করে এবং নতুন সংযোগের জন্য আপনাকে এটি নিরীক্ষণ করতে দেয়।
-
UNIX*সকেট * - IO এর জন্য বিদ্যমান সকেট খুলুন।
দ্রষ্টব্য: অন্যান্য ধরণের সকেট বিদ্যমান। সবচেয়ে উল্লেখযোগ্যভাবে TCP সকেট। কিন্তু এই পোস্টটি শুধুমাত্র ইউনিক্স সকেট নিয়ে কাজ করে। আপনি কিভাবে পার্থক্য বলতে পারেন? ইউনিক্স সকেটের ফাইলের নাম আছে।
সরলতম সকেট
আমরা দুটি ছোট প্রোগ্রাম দেখতে যাচ্ছি।
প্রথমটি হল "সার্ভার।" এটি সহজভাবে 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 এর সাথে ইন্টারফেস করা
এখন যেহেতু আমরা জানি কিভাবে ইউনিক্স সকেট "সার্ভার" তৈরি করতে হয়, আমরা সহজেই 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 পাঠাচ্ছে। (চিন্তা করবেন না, আমরা এক মিনিটের মধ্যে কনফিগারেশন নিয়ে আলোচনা করব)
যখন আমি একটি ওয়েব অনুরোধ করি, nginx আমার ছোট সার্ভারে নিম্নলিখিত ডেটা পাঠায়:
বেশ দারুন! এটি কয়েকটি অতিরিক্ত শিরোনাম যোগ করা সহ একটি সাধারণ HTTP অনুরোধ। এখন আমরা একটি বাস্তব অ্যাপ সার্ভার তৈরি করতে প্রস্তুত। তবে প্রথমে nginx কনফিগারেশন নিয়ে আলোচনা করা যাক।
Nginx ইনস্টল এবং কনফিগার করা
আপনি যদি ইতিমধ্যে আপনার ডেভেলপমেন্ট মেশিনে nginx ইনস্টল না করে থাকেন, তাহলে এক সেকেন্ড সময় নিন এবং এখনই করুন। হোমব্রু এর মাধ্যমে ওএসএক্সে এটি সত্যিই সহজ:
brew install nginx
এখন আমাদের স্থানীয় হোস্ট:2048-এ অনুরোধ ফরওয়ার্ড করার জন্য nginx কনফিগার করতে হবে /tmp/socktest.sock
নামের একটি সকেটের মাধ্যমে আপস্ট্রিম সার্ভারে। . নামটা বিশেষ কিছু না। এটি শুধুমাত্র আমাদের ওয়েব সার্ভার দ্বারা ব্যবহৃত সকেট নামের সাথে মেলে।
আপনি এই কনফিগারেশনটি /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 ফায়ার করি, আমি লোকালহোস্টে যেতে পারি:2048। আমার অ্যাপে অনুরোধ পাঠানো হয়। এবং প্রতিক্রিয়া ব্রাউজার দ্বারা রেন্ডার করা হয়. বেশ সুন্দর!
HTTP অনুরোধগুলি আমাদের সাধারণ অ্যাপ সার্ভার দ্বারা STDOUT এ লগ করা হয়
এবং এখানে আমাদের শ্রমের গৌরবময় ফল। দেখো! একটি টাইমস্ট্যাম্প!
সার্ভার একটি টাইমস্ট্যাম্প প্রদান করে যা ব্রাউজারে প্রদর্শিত হয়