مرتب شدہ تقسیم شدہ نظام کی ترتیب

میں آپ کو تقسیم شدہ نظام کی ترتیب کے ساتھ کام کرنے کا ایک دلچسپ طریقہ کار بتانا چاہتا ہوں۔ محفوظ اقسام کا استعمال کرتے ہوئے ترتیب کو براہ راست مرتب شدہ زبان (Scala) میں دکھایا جاتا ہے۔ یہ پوسٹ ایسی ترتیب کی ایک مثال فراہم کرتی ہے اور مجموعی ترقیاتی عمل میں مرتب کردہ ترتیب کو لاگو کرنے کے مختلف پہلوؤں پر بحث کرتی ہے۔

مرتب شدہ تقسیم شدہ نظام کی ترتیب

(انگریزی)

تعارف

ایک قابل اعتماد تقسیم شدہ نظام کی تعمیر کا مطلب یہ ہے کہ تمام نوڈس درست کنفیگریشن کا استعمال کرتے ہیں، دوسرے نوڈس کے ساتھ ہم آہنگ۔ DevOps ٹیکنالوجیز (terraform، ansible یا اس جیسی کوئی چیز) عام طور پر کنفیگریشن فائلوں کو خود بخود بنانے کے لیے استعمال کی جاتی ہیں (اکثر ہر نوڈ کے لیے مخصوص)۔ ہم یہ بھی یقینی بنانا چاہیں گے کہ تمام مواصلاتی نوڈس ایک جیسے پروٹوکول (بشمول ایک ہی ورژن) استعمال کر رہے ہیں۔ بصورت دیگر، ہمارے تقسیم شدہ نظام میں عدم مطابقت پیدا ہو جائے گی۔ JVM کی دنیا میں، اس ضرورت کا ایک نتیجہ یہ ہے کہ لائبریری کا ایک ہی ورژن جس میں پروٹوکول پیغامات ہوں ہر جگہ استعمال کیا جانا چاہیے۔

تقسیم شدہ نظام کی جانچ کے بارے میں کیا خیال ہے؟ بلاشبہ، ہم فرض کرتے ہیں کہ انضمام کی جانچ پر جانے سے پہلے تمام اجزاء کے یونٹ ٹیسٹ ہوتے ہیں۔ (ہمارے لیے ٹیسٹ کے نتائج کو رن ٹائم کے لیے نکالنے کے لیے، ہمیں ٹیسٹنگ کے مرحلے اور رن ٹائم پر لائبریریوں کا ایک جیسا سیٹ بھی فراہم کرنا چاہیے۔)

انضمام ٹیسٹ کے ساتھ کام کرتے وقت، تمام نوڈس پر ہر جگہ ایک ہی کلاس پاتھ کو استعمال کرنا اکثر آسان ہوتا ہے۔ ہمیں بس یہ یقینی بنانا ہے کہ رن ٹائم پر ایک ہی کلاس پاتھ کا استعمال کیا جائے۔ (جبکہ مختلف نوڈس کو مختلف کلاس پاتھ کے ساتھ چلانا مکمل طور پر ممکن ہے، لیکن اس سے مجموعی ترتیب میں پیچیدگی اور تعیناتی اور انضمام کے ٹیسٹ میں مشکلات کا اضافہ ہوتا ہے۔) اس پوسٹ کے مقاصد کے لیے، ہم یہ فرض کر رہے ہیں کہ تمام نوڈس ایک ہی کلاس پاتھ کا استعمال کریں گے۔

ترتیب ایپلی کیشن کے ساتھ تیار ہوتی ہے۔ ہم پروگرام کے ارتقاء کے مختلف مراحل کی شناخت کے لیے ورژن استعمال کرتے ہیں۔ کنفیگریشنز کے مختلف ورژنز کی نشاندہی کرنا بھی منطقی معلوم ہوتا ہے۔ اور کنفیگریشن کو خود ورژن کنٹرول سسٹم میں رکھیں۔ اگر پیداوار میں صرف ایک ہی ترتیب ہے، تو ہم صرف ورژن نمبر استعمال کر سکتے ہیں۔ اگر ہم کئی پیداواری مثالیں استعمال کرتے ہیں، تو ہمیں کئی کی ضرورت ہوگی۔
کنفیگریشن برانچز اور ورژن کے علاوہ ایک اضافی لیبل (مثال کے طور پر برانچ کا نام)۔ اس طرح ہم واضح طور پر درست کنفیگریشن کی شناخت کر سکتے ہیں۔ ہر کنفیگریشن شناخت کنندہ منفرد طور پر تقسیم شدہ نوڈس، پورٹس، بیرونی وسائل، اور لائبریری ورژن کے مخصوص مجموعہ سے مطابقت رکھتا ہے۔ اس پوسٹ کے مقاصد کے لیے ہم فرض کریں گے کہ صرف ایک شاخ ہے اور ہم ایک نقطے (1.2.3) سے الگ کیے گئے تین نمبروں کا استعمال کرتے ہوئے معمول کے مطابق ترتیب کی شناخت کر سکتے ہیں۔

جدید ماحول میں، کنفیگریشن فائلیں شاذ و نادر ہی دستی طور پر بنائی جاتی ہیں۔ زیادہ کثرت سے وہ تعیناتی کے دوران پیدا ہوتے ہیں اور اب انہیں چھوا نہیں جاتا ہے (تاکہ کچھ نہ توڑو)۔ ایک فطری سوال پیدا ہوتا ہے: ہم اب بھی ترتیب کو ذخیرہ کرنے کے لیے ٹیکسٹ فارمیٹ کیوں استعمال کرتے ہیں؟ ایسا لگتا ہے کہ ایک قابل عمل متبادل کنفیگریشن کے لیے باقاعدہ کوڈ استعمال کرنے اور کمپائل ٹائم چیک سے فائدہ اٹھانے کی صلاحیت ہے۔

اس پوسٹ میں ہم مرتب شدہ نمونے کے اندر ایک ترتیب کی نمائندگی کرنے کے خیال کو تلاش کریں گے۔

مرتب کردہ ترتیب

یہ سیکشن جامد مرتب کردہ ترتیب کی مثال فراہم کرتا ہے۔ دو آسان خدمات لاگو کی جاتی ہیں - ایکو سروس اور ایکو سروس کلائنٹ۔ ان دو خدمات کی بنیاد پر، سسٹم کے دو اختیارات جمع کیے گئے ہیں۔ ایک آپشن میں، دونوں سروسز ایک ہی نوڈ پر واقع ہیں، دوسرے آپشن میں - مختلف نوڈس پر۔

عام طور پر تقسیم شدہ نظام میں کئی نوڈس ہوتے ہیں۔ آپ کسی قسم کی اقدار کا استعمال کرتے ہوئے نوڈس کی شناخت کر سکتے ہیں۔ NodeId:

sealed trait NodeId
case object Backend extends NodeId
case object Frontend extends NodeId

یا

case class NodeId(hostName: String)

یا یہاں تک کہ

object Singleton
type NodeId = Singleton.type

نوڈس مختلف کردار ادا کرتے ہیں، وہ سروسز چلاتے ہیں اور ان کے درمیان TCP/HTTP کنکشن قائم کیے جا سکتے ہیں۔

TCP کنکشن کی وضاحت کرنے کے لیے ہمیں کم از کم ایک پورٹ نمبر کی ضرورت ہے۔ ہم اس پروٹوکول کی بھی عکاسی کرنا چاہیں گے جو اس پورٹ پر تعاون یافتہ ہے تاکہ یہ یقینی بنایا جا سکے کہ کلائنٹ اور سرور دونوں ایک ہی پروٹوکول کا استعمال کر رہے ہیں۔ ہم درج ذیل کلاس کا استعمال کرتے ہوئے کنکشن کی وضاحت کریں گے:

case class TcpEndPoint[Protocol](node: NodeId, port: Port[Protocol])

جہاں Port - صرف ایک عدد Int قابل قبول اقدار کی حد کی نشاندہی کرنا:

type PortNumber = Refined[Int, Closed[_0, W.`65535`.T]]

بہتر اقسام

لائبریری دیکھیں بہتر и میرا رپورٹ. مختصراً، لائبریری آپ کو ان اقسام میں رکاوٹیں شامل کرنے کی اجازت دیتی ہے جو مرتب وقت پر چیک کی جاتی ہیں۔ اس صورت میں، درست پورٹ نمبر کی قدریں 16 بٹ انٹیجرز ہیں۔ مرتب شدہ کنفیگریشن کے لیے، ریفائنڈ لائبریری کا استعمال لازمی نہیں ہے، لیکن یہ کمپائلر کی کنفیگریشن کو چیک کرنے کی صلاحیت کو بہتر بناتا ہے۔

HTTP (REST) ​​پروٹوکول کے لیے، پورٹ نمبر کے علاوہ، ہمیں سروس کے راستے کی بھی ضرورت ہو سکتی ہے:

type UrlPathPrefix = Refined[String, MatchesRegex[W.`"[a-zA-Z_0-9/]*"`.T]]
case class PortWithPrefix[Protocol](portNumber: PortNumber, pathPrefix: UrlPathPrefix)

پریت کی اقسام

مرتب وقت پر پروٹوکول کی شناخت کے لیے، ہم ایک قسم کا پیرامیٹر استعمال کرتے ہیں جو کلاس میں استعمال نہیں ہوتا ہے۔ یہ فیصلہ اس حقیقت کی وجہ سے ہے کہ ہم رن ٹائم پر پروٹوکول مثال استعمال نہیں کرتے ہیں، لیکن ہم چاہیں گے کہ کمپائلر پروٹوکول کی مطابقت کو چیک کرے۔ پروٹوکول کی وضاحت کرنے سے، ہم انحصار کے طور پر کسی نامناسب سروس کو پاس نہیں کر سکیں گے۔

عام پروٹوکولز میں سے ایک Json سیریلائزیشن کے ساتھ REST API ہے:

sealed trait JsonHttpRestProtocol[RequestMessage, ResponseMessage]

جہاں RequestMessage - درخواست کی قسم، ResponseMessage - جواب کی قسم۔
بلاشبہ، ہم دیگر پروٹوکول وضاحتیں استعمال کر سکتے ہیں جو ہمیں مطلوبہ وضاحت کی درستگی فراہم کرتی ہیں۔

اس پوسٹ کے مقاصد کے لیے، ہم پروٹوکول کا ایک آسان ورژن استعمال کریں گے:

sealed trait SimpleHttpGetRest[RequestMessage, ResponseMessage]

یہاں درخواست یو آر ایل کے ساتھ منسلک ایک سٹرنگ ہے اور جواب HTTP جواب کے باڈی میں واپس کی گئی سٹرنگ ہے۔

سروس کنفیگریشن سروس کے نام، بندرگاہوں اور انحصار کے ذریعہ بیان کی جاتی ہے۔ ان عناصر کو اسکیلا میں کئی طریقوں سے پیش کیا جا سکتا ہے (مثال کے طور پر، HList-s، الجبری ڈیٹا کی اقسام)۔ اس پوسٹ کے مقاصد کے لیے، ہم کیک پیٹرن کا استعمال کریں گے اور استعمال کرتے ہوئے ماڈیولز کی نمائندگی کریں گے۔ trait'او. (کیک پیٹرن اس نقطہ نظر کا مطلوبہ عنصر نہیں ہے۔ یہ صرف ایک ممکنہ نفاذ ہے۔)

خدمات کے درمیان انحصار کو ان طریقوں کے طور پر پیش کیا جا سکتا ہے جو بندرگاہوں کو واپس کرتے ہیں۔ EndPointدوسرے نوڈس کے:

  type EchoProtocol[A] = SimpleHttpGetRest[A, A]

  trait EchoConfig[A] extends ServiceConfig {
    def portNumber: PortNumber = 8081
    def echoPort: PortWithPrefix[EchoProtocol[A]] = PortWithPrefix[EchoProtocol[A]](portNumber, "echo")
    def echoService: HttpSimpleGetEndPoint[NodeId, EchoProtocol[A]] = providedSimpleService(echoPort)
  }

ایکو سروس بنانے کے لیے، آپ کو بس ایک پورٹ نمبر اور ایک اشارہ کی ضرورت ہے کہ پورٹ ایکو پروٹوکول کو سپورٹ کرتا ہے۔ ہم ایک مخصوص بندرگاہ کی وضاحت نہیں کر سکتے ہیں، کیونکہ... خصلتیں آپ کو بغیر عمل درآمد کے طریقوں کا اعلان کرنے کی اجازت دیتی ہیں (خلاصہ طریقے)۔ اس صورت میں، کنکریٹ کنفیگریشن بناتے وقت، مرتب کرنے والے کو ہم سے تجریدی طریقہ کا نفاذ فراہم کرنے اور پورٹ نمبر فراہم کرنے کی ضرورت ہوگی۔ چونکہ ہم نے اس طریقہ کو نافذ کیا ہے، جب ایک مخصوص کنفیگریشن بناتے ہیں، تو ہو سکتا ہے کہ ہم ایک مختلف پورٹ کی وضاحت نہ کریں۔ پہلے سے طے شدہ قدر استعمال کی جائے گی۔

کلائنٹ کنفیگریشن میں ہم ایکو سروس پر انحصار کا اعلان کرتے ہیں:

  trait EchoClientConfig[A] {
    def testMessage: String = "test"
    def pollInterval: FiniteDuration
    def echoServiceDependency: HttpSimpleGetEndPoint[_, EchoProtocol[A]]
  }

انحصار اسی قسم کا ہے جو برآمد شدہ سروس ہے۔ echoService. خاص طور پر، ایکو کلائنٹ میں ہمیں ایک ہی پروٹوکول کی ضرورت ہوتی ہے۔ لہذا، دو خدمات کو جوڑنے پر، ہم اس بات کا یقین کر سکتے ہیں کہ سب کچھ صحیح طریقے سے کام کرے گا۔

خدمات کا نفاذ

سروس شروع کرنے اور بند کرنے کے لیے ایک فنکشن درکار ہے۔ (سروس کو روکنے کی صلاحیت جانچ کے لیے اہم ہے۔) ایک بار پھر، ایسی خصوصیت کو لاگو کرنے کے لیے کئی اختیارات ہیں (مثال کے طور پر، ہم کنفیگریشن کی قسم کی بنیاد پر قسم کی کلاسیں استعمال کر سکتے ہیں)۔ اس پوسٹ کے مقاصد کے لیے ہم کیک پیٹرن استعمال کریں گے۔ ہم کلاس کا استعمال کرتے ہوئے سروس کی نمائندگی کریں گے۔ cats.Resourceکیونکہ یہ طبقہ پہلے سے ہی مسائل کی صورت میں وسائل کی رہائی کی محفوظ طریقے سے ضمانت فراہم کرتا ہے۔ وسیلہ حاصل کرنے کے لیے، ہمیں کنفیگریشن اور ایک ریڈی میڈ رن ٹائم سیاق و سباق فراہم کرنے کی ضرورت ہے۔ سروس اسٹارٹ اپ فنکشن اس طرح نظر آسکتا ہے:

  type ResourceReader[F[_], Config, A] = Reader[Config, Resource[F, A]]

  trait ServiceImpl[F[_]] {
    type Config
    def resource(
      implicit
      resolver: AddressResolver[F],
      timer: Timer[F],
      contextShift: ContextShift[F],
      ec: ExecutionContext,
      applicative: Applicative[F]
    ): ResourceReader[F, Config, Unit]
  }

جہاں

  • Config - اس سروس کے لیے کنفیگریشن کی قسم
  • AddressResolver - ایک رن ٹائم آبجیکٹ جو آپ کو دوسرے نوڈس کے پتے تلاش کرنے کی اجازت دیتا ہے (نیچے دیکھیں)

اور لائبریری سے دیگر اقسام cats:

  • F[_] - اثر کی قسم (سب سے آسان صورت میں F[A] صرف ایک فنکشن ہوسکتا ہے۔ () => A. اس پوسٹ میں ہم استعمال کریں گے۔ cats.IO.)
  • Reader[A,B] - فنکشن کا کم و بیش مترادف A => B
  • cats.Resource - ایک ایسا وسیلہ جو حاصل اور جاری کیا جا سکتا ہے۔
  • Timer - ٹائمر (آپ کو تھوڑی دیر کے لئے سونے اور وقت کے وقفوں کی پیمائش کرنے کی اجازت دیتا ہے)
  • ContextShift --.اینالاگ ExecutionContext
  • Applicative - ایک اثر قسم کی کلاس جو آپ کو انفرادی اثرات کو یکجا کرنے کی اجازت دیتی ہے (تقریبا ایک مونڈ)۔ زیادہ پیچیدہ ایپلی کیشنز میں یہ استعمال کرنا بہتر لگتا ہے۔ Monad/ConcurrentEffect.

اس فنکشن کے دستخط کا استعمال کرتے ہوئے ہم کئی خدمات کو نافذ کر سکتے ہیں۔ مثال کے طور پر، ایسی خدمت جو کچھ نہیں کرتی:

  trait ZeroServiceImpl[F[_]] extends ServiceImpl[F] {
    type Config <: Any
    def resource(...): ResourceReader[F, Config, Unit] =
      Reader(_ => Resource.pure[F, Unit](()))
  }

(دیکھیں ماخذ کوڈ، جس میں دیگر خدمات نافذ کی جاتی ہیں - بازگشت سروس, گونج کلائنٹ
и زندگی بھر کے کنٹرولرز.)

نوڈ ایک ایسی چیز ہے جو کئی خدمات شروع کر سکتی ہے (وسائل کی ایک زنجیر کا آغاز کیک پیٹرن کے ذریعے یقینی بنایا جاتا ہے):

object SingleNodeImpl extends ZeroServiceImpl[IO]
  with EchoServiceService
  with EchoClientService
  with FiniteDurationLifecycleServiceImpl
{
  type Config = EchoConfig[String] with EchoClientConfig[String] with FiniteDurationLifecycleConfig
}

براہ کرم نوٹ کریں کہ ہم اس نوڈ کے لیے مطلوبہ ترتیب کی قطعی قسم کی وضاحت کر رہے ہیں۔ اگر ہم کسی خاص سروس کے لیے مطلوبہ کنفیگریشن اقسام میں سے کسی ایک کی وضاحت کرنا بھول جاتے ہیں، تو تالیف کی خرابی ہوگی۔ نیز، ہم نوڈ شروع کرنے کے قابل نہیں ہوں گے جب تک کہ ہم تمام ضروری ڈیٹا کے ساتھ مناسب قسم کی کوئی چیز فراہم نہ کریں۔

میزبان نام کی قرارداد

ریموٹ ہوسٹ سے جڑنے کے لیے، ہمیں ایک حقیقی IP ایڈریس کی ضرورت ہے۔ یہ ممکن ہے کہ ایڈریس باقی کنفیگریشن کے مقابلے میں بعد میں معلوم ہوجائے۔ لہذا ہمیں ایک فنکشن کی ضرورت ہے جو نوڈ ID کو ایڈریس پر نقشہ بنائے:

case class NodeAddress[NodeId](host: Uri.Host)
trait AddressResolver[F[_]] {
  def resolve[NodeId](nodeId: NodeId): F[NodeAddress[NodeId]]
}

اس فنکشن کو نافذ کرنے کے کئی طریقے ہیں:

  1. اگر پتے تعیناتی سے پہلے ہمیں معلوم ہو جائیں، تو ہم اس کے ساتھ Scala کوڈ بنا سکتے ہیں۔
    ایڈریس اور پھر بلڈ چلائیں۔ یہ ٹیسٹ مرتب کرے گا اور چلائے گا۔
    اس صورت میں، فنکشن کو جامد طور پر جانا جائے گا اور کوڈ میں نقشہ سازی کے طور پر پیش کیا جا سکتا ہے۔ Map[NodeId, NodeAddress].
  2. بعض صورتوں میں، اصل پتہ نوڈ کے شروع ہونے کے بعد ہی معلوم ہوتا ہے۔
    اس صورت میں، ہم ایک "دریافت سروس" نافذ کر سکتے ہیں جو دوسرے نوڈس سے پہلے چلتی ہے اور تمام نوڈس اس سروس کے ساتھ رجسٹر ہوں گے اور دوسرے نوڈس کے پتے کی درخواست کریں گے۔
  3. اگر ہم ترمیم کر سکتے ہیں۔ /etc/hosts، پھر آپ پہلے سے طے شدہ میزبان نام استعمال کرسکتے ہیں (جیسے my-project-main-node и echo-backend) اور صرف ان ناموں کو لنک کریں۔
    تعیناتی کے دوران IP پتوں کے ساتھ۔

اس پوسٹ میں ہم ان معاملات پر مزید تفصیل سے غور نہیں کریں گے۔ ہمارے لیے
کھلونا کی مثال میں، تمام نوڈس کا ایک ہی IP پتہ ہوگا۔ 127.0.0.1.

اگلا، ہم تقسیم شدہ نظام کے لیے دو اختیارات پر غور کرتے ہیں:

  1. تمام خدمات کو ایک نوڈ پر رکھنا۔
  2. اور مختلف نوڈس پر ایکو سروس اور ایکو کلائنٹ کی میزبانی کرنا۔

کے لیے ترتیب ایک نوڈ:

سنگل نوڈ کنفیگریشن

object SingleNodeConfig extends EchoConfig[String] 
  with EchoClientConfig[String] with FiniteDurationLifecycleConfig
{
  case object Singleton // identifier of the single node 
  // configuration of server
  type NodeId = Singleton.type
  def nodeId = Singleton

  /** Type safe service port specification. */
  override def portNumber: PortNumber = 8088

  // configuration of client

  /** We'll use the service provided by the same host. */
  def echoServiceDependency = echoService

  override def testMessage: UrlPathElement = "hello"

  def pollInterval: FiniteDuration = 1.second

  // lifecycle controller configuration
  def lifetime: FiniteDuration = 10500.milliseconds // additional 0.5 seconds so that there are 10 requests, not 9.
}

آبجیکٹ کلائنٹ اور سرور دونوں کی ترتیب کو نافذ کرتا ہے۔ ٹائم ٹو لائیو کنفیگریشن بھی استعمال کی جاتی ہے تاکہ وقفہ کے بعد lifetime پروگرام کو ختم کریں. (Ctrl-C بھی کام کرتا ہے اور تمام وسائل کو صحیح طریقے سے آزاد کرتا ہے۔)

ترتیب اور نفاذ کی خصوصیات کا ایک ہی سیٹ پر مشتمل نظام بنانے کے لیے استعمال کیا جا سکتا ہے۔ دو الگ الگ نوڈس:

دو نوڈ کنفیگریشن

  object NodeServerConfig extends EchoConfig[String] with SigTermLifecycleConfig
  {
    type NodeId = NodeIdImpl

    def nodeId = NodeServer

    override def portNumber: PortNumber = 8080
  }

  object NodeClientConfig extends EchoClientConfig[String] with FiniteDurationLifecycleConfig
  {
    // NB! dependency specification
    def echoServiceDependency = NodeServerConfig.echoService

    def pollInterval: FiniteDuration = 1.second

    def lifetime: FiniteDuration = 10500.milliseconds // additional 0.5 seconds so that there are 10 request, not 9.

    def testMessage: String = "dolly"
  }

اہم! نوٹس کریں کہ خدمات کیسے منسلک ہیں۔ ہم ایک نوڈ کے ذریعے نافذ کردہ سروس کو دوسرے نوڈ کے انحصار کے طریقہ کار کے نفاذ کے طور پر بتاتے ہیں۔ انحصار کی قسم کو مرتب کرنے والے کے ذریعہ چیک کیا جاتا ہے، کیونکہ پروٹوکول کی قسم پر مشتمل ہے۔ جب چلایا جائے گا، انحصار میں صحیح ہدف نوڈ ID ہوگا۔ اس اسکیم کی بدولت، ہم پورٹ نمبر بالکل ایک بار بتاتے ہیں اور ہمیشہ صحیح پورٹ سے رجوع کرنے کی ضمانت دی جاتی ہے۔

دو نظام نوڈس کے نفاذ

اس ترتیب کے لیے، ہم بغیر کسی تبدیلی کے وہی سروس کے نفاذ کا استعمال کرتے ہیں۔ فرق صرف یہ ہے کہ اب ہمارے پاس دو اشیاء ہیں جو خدمات کے مختلف سیٹوں کو نافذ کرتی ہیں:

  object TwoJvmNodeServerImpl extends ZeroServiceImpl[IO] with EchoServiceService with SigIntLifecycleServiceImpl {
    type Config = EchoConfig[String] with SigTermLifecycleConfig
  }

  object TwoJvmNodeClientImpl extends ZeroServiceImpl[IO] with EchoClientService with FiniteDurationLifecycleServiceImpl {
    type Config = EchoClientConfig[String] with FiniteDurationLifecycleConfig
  }

پہلا نوڈ سرور کو نافذ کرتا ہے اور اسے صرف سرور کی ترتیب کی ضرورت ہوتی ہے۔ دوسرا نوڈ کلائنٹ کو لاگو کرتا ہے اور کنفیگریشن کا ایک مختلف حصہ استعمال کرتا ہے۔ نیز دونوں نوڈس کو زندگی بھر کے انتظام کی ضرورت ہے۔ سرور نوڈ غیر معینہ مدت تک چلتا ہے جب تک کہ اسے روکا نہ جائے۔ SIGTERM'om، اور کلائنٹ نوڈ کچھ وقت کے بعد ختم ہو جاتا ہے۔ سینٹی میٹر. لانچر ایپ.

عمومی ترقی کا عمل

آئیے دیکھتے ہیں کہ یہ کنفیگریشن اپروچ مجموعی ترقی کے عمل کو کیسے متاثر کرتا ہے۔

کنفیگریشن کو بقیہ کوڈ کے ساتھ مرتب کیا جائے گا اور ایک آرٹفیکٹ (.jar) تیار کیا جائے گا۔ ایسا لگتا ہے کہ ترتیب کو ایک علیحدہ نمونے میں رکھنا سمجھ میں آتا ہے۔ اس کی وجہ یہ ہے کہ ہمارے پاس ایک ہی کوڈ کی بنیاد پر متعدد کنفیگریشن ہو سکتے ہیں۔ ایک بار پھر، مختلف کنفیگریشن برانچوں کے مطابق نمونے تیار کرنا ممکن ہے۔ لائبریریوں کے مخصوص ورژن پر انحصار کنفیگریشن کے ساتھ محفوظ کیا جاتا ہے، اور جب بھی ہم کنفیگریشن کے اس ورژن کو تعینات کرنے کا فیصلہ کرتے ہیں تو یہ ورژن ہمیشہ کے لیے محفوظ ہو جاتے ہیں۔

کسی بھی ترتیب میں تبدیلی کوڈ کی تبدیلی میں بدل جاتی ہے۔ اور اس وجہ سے، ہر ایک
تبدیلی کو کوالٹی اشورینس کے معمول کے عمل میں شامل کیا جائے گا:

بگ ٹریکر میں ٹکٹ -> PR -> جائزہ -> متعلقہ شاخوں کے ساتھ ضم کریں ->
انضمام -> تعیناتی۔

مرتب کردہ ترتیب کو نافذ کرنے کے اہم نتائج یہ ہیں:

  1. ترتیب تقسیم شدہ نظام کے تمام نوڈس میں یکساں ہوگی۔ اس حقیقت کی وجہ سے کہ تمام نوڈس ایک ہی ذریعہ سے ایک جیسی ترتیب حاصل کرتے ہیں۔

  2. نوڈس میں سے صرف ایک میں کنفیگریشن کو تبدیل کرنا مشکل ہے۔ لہذا، "کنفیگریشن ڈرفٹ" کا امکان نہیں ہے۔

  3. کنفیگریشن میں چھوٹی تبدیلیاں کرنا زیادہ مشکل ہو جاتا ہے۔

  4. زیادہ تر کنفیگریشن تبدیلیاں مجموعی ترقیاتی عمل کے حصے کے طور پر ہوں گی اور ان کا جائزہ لیا جائے گا۔

کیا مجھے پروڈکشن کنفیگریشن کو ذخیرہ کرنے کے لیے علیحدہ ذخیرہ کی ضرورت ہے؟ اس ترتیب میں پاس ورڈ اور دیگر حساس معلومات شامل ہو سکتی ہیں جن تک ہم رسائی کو محدود کرنا چاہتے ہیں۔ اس کی بنیاد پر، ایسا لگتا ہے کہ حتمی ترتیب کو الگ ذخیرہ میں ذخیرہ کرنا سمجھ میں آتا ہے۔ آپ کنفیگریشن کو دو حصوں میں تقسیم کر سکتے ہیں — ایک عوامی طور پر قابل رسائی کنفیگریشن سیٹنگز پر مشتمل ہے اور دوسرا محدود سیٹنگز پر مشتمل ہے۔ یہ زیادہ تر ڈویلپرز کو عام ترتیبات تک رسائی حاصل کرنے کی اجازت دے گا۔ یہ علیحدگی پہلے سے طے شدہ اقدار پر مشتمل انٹرمیڈیٹ خصلتوں کا استعمال کرتے ہوئے حاصل کرنا آسان ہے۔

ممکنہ مختلف حالتیں

آئیے کچھ عام متبادلات کے ساتھ مرتب کردہ ترتیب کا موازنہ کرنے کی کوشش کریں:

  1. ٹارگٹ مشین پر ٹیکسٹ فائل۔
  2. مرکزی کلیدی قدر کی دکان (etcd/zookeeper).
  3. عمل کے اجزاء جو عمل کو دوبارہ شروع کیے بغیر دوبارہ تشکیل/دوبارہ شروع کیا جا سکتا ہے۔
  4. آرٹفیکٹ اور ورژن کنٹرول سے باہر ترتیب کو ذخیرہ کرنا۔

ٹیکسٹ فائلیں چھوٹی تبدیلیوں کے معاملے میں نمایاں لچک فراہم کرتی ہیں۔ سسٹم ایڈمنسٹریٹر ریموٹ نوڈ میں لاگ ان ہو سکتا ہے، مناسب فائلوں میں تبدیلیاں کر سکتا ہے اور سروس کو دوبارہ شروع کر سکتا ہے۔ تاہم، بڑے سسٹمز کے لیے اس طرح کی لچک مطلوبہ نہیں ہوسکتی ہے۔ کی گئی تبدیلیاں دوسرے سسٹمز میں کوئی نشان نہیں چھوڑتی ہیں۔ کوئی بھی تبدیلیوں کا جائزہ نہیں لیتا۔ یہ تعین کرنا مشکل ہے کہ تبدیلیاں کس نے کی ہیں اور کس وجہ سے کی ہیں۔ تبدیلیوں کا تجربہ نہیں کیا جاتا ہے۔ اگر نظام تقسیم کیا جاتا ہے، تو منتظم دوسرے نوڈس پر متعلقہ تبدیلی کرنا بھول سکتا ہے۔

(یہ بھی واضح رہے کہ مرتب شدہ کنفیگریشن کا استعمال مستقبل میں ٹیکسٹ فائلوں کے استعمال کے امکان کو بند نہیں کرتا۔ یہ ایک پارسر اور تصدیق کنندہ کو شامل کرنا کافی ہوگا جو ایک ہی قسم کی آؤٹ پٹ تیار کرتا ہے۔ Config، اور آپ ٹیکسٹ فائلیں استعمال کرسکتے ہیں۔ یہ فوری طور پر اس بات کی پیروی کرتا ہے کہ مرتب کردہ ترتیب والے سسٹم کی پیچیدگی ٹیکسٹ فائلوں کا استعمال کرنے والے سسٹم کی پیچیدگی سے کچھ کم ہے، کیونکہ ٹیکسٹ فائلوں کو اضافی کوڈ کی ضرورت ہوتی ہے۔)

تقسیم شدہ ایپلیکیشن کے میٹا پیرامیٹرز کو تقسیم کرنے کے لیے مرکزی کلیدی قدر اسٹور ایک اچھا طریقہ کار ہے۔ ہمیں یہ فیصلہ کرنے کی ضرورت ہے کہ کنفیگریشن پیرامیٹرز کیا ہیں اور صرف ڈیٹا کیا ہیں۔ آئیے ایک فنکشن کریں۔ C => A => B، اور پیرامیٹرز C شاذ و نادر ہی تبدیلیاں، اور ڈیٹا A --.اکثر. اس معاملے میں ہم یہ کہہ سکتے ہیں۔ C - ترتیب کے پیرامیٹرز، اور A - ڈیٹا ایسا معلوم ہوتا ہے کہ کنفیگریشن پیرامیٹرز ڈیٹا سے مختلف ہوتے ہیں کیونکہ وہ عام طور پر ڈیٹا کے مقابلے میں کم تبدیل ہوتے ہیں۔ اس کے علاوہ، ڈیٹا عام طور پر ایک ذریعہ سے آتا ہے (صارف کی طرف سے)، اور کنفیگریشن پیرامیٹرز دوسرے سے (سسٹم ایڈمنسٹریٹر کی طرف سے)۔

اگر پروگرام کو دوبارہ شروع کیے بغیر شاذ و نادر ہی تبدیل کرنے والے پیرامیٹرز کو اپ ڈیٹ کرنے کی ضرورت ہے، تو یہ اکثر پروگرام کی پیچیدگی کا باعث بن سکتا ہے، کیونکہ ہمیں کسی نہ کسی طرح پیرامیٹرز کی فراہمی، اسٹور، پارس اور چیک، اور غلط اقدار پر کارروائی کرنے کی ضرورت ہوگی۔ لہذا، پروگرام کی پیچیدگی کو کم کرنے کے نقطہ نظر سے، یہ سمجھ میں آتا ہے کہ ایسے پیرامیٹرز کی تعداد کو کم کیا جائے جو پروگرام کے آپریشن کے دوران تبدیل ہو سکتے ہیں (یا ایسے پیرامیٹرز کو بالکل بھی سپورٹ نہیں کرتے)۔

اس پوسٹ کے مقاصد کے لیے، ہم جامد اور متحرک پیرامیٹرز میں فرق کریں گے۔ اگر سروس کی منطق کو پروگرام کے آپریشن کے دوران پیرامیٹرز کو تبدیل کرنے کی ضرورت ہے، تو ہم ایسے پیرامیٹرز کو متحرک کہیں گے۔ بصورت دیگر اختیارات جامد ہیں اور مرتب کردہ ترتیب کا استعمال کرتے ہوئے ترتیب دیے جا سکتے ہیں۔ متحرک ری کنفیگریشن کے لیے، ہمیں پروگرام کے کچھ حصوں کو نئے پیرامیٹرز کے ساتھ دوبارہ شروع کرنے کے لیے ایک طریقہ کار کی ضرورت ہو سکتی ہے، جیسا کہ آپریٹنگ سسٹم کے عمل کو دوبارہ شروع کرنے کے طریقے سے۔ (ہماری رائے میں، ریئل ٹائم ری کنفیگریشن سے گریز کرنے کا مشورہ دیا جاتا ہے، کیونکہ اس سے سسٹم کی پیچیدگی بڑھ جاتی ہے۔ اگر ممکن ہو تو، عمل کو دوبارہ شروع کرنے کے لیے معیاری OS کی صلاحیتوں کو استعمال کرنا بہتر ہے۔)

جامد کنفیگریشن کے استعمال کا ایک اہم پہلو جو لوگوں کو متحرک ری کنفیگریشن پر غور کرنے پر مجبور کرتا ہے وہ وقت ہے جو سسٹم کو کنفیگریشن اپ ڈیٹ (ڈاؤن ٹائم) کے بعد دوبارہ شروع ہونے میں لگتا ہے۔ درحقیقت، اگر ہمیں سٹیٹک کنفیگریشن میں تبدیلیاں کرنے کی ضرورت ہے، تو ہمیں نئی ​​اقدار کے اثر انداز ہونے کے لیے سسٹم کو دوبارہ شروع کرنا پڑے گا۔ ڈاؤن ٹائم کا مسئلہ مختلف سسٹمز کے لیے شدت میں مختلف ہوتا ہے۔ کچھ صورتوں میں، آپ ایک ایسے وقت میں ریبوٹ شیڈول کر سکتے ہیں جب بوجھ کم ہو۔ اگر آپ کو مسلسل سروس فراہم کرنے کی ضرورت ہے، تو آپ لاگو کر سکتے ہیں۔ AWS ELB کنکشن کی نکاسی. ایک ہی وقت میں، جب ہمیں سسٹم کو ریبوٹ کرنے کی ضرورت ہوتی ہے، تو ہم اس سسٹم کی ایک متوازی مثال لانچ کرتے ہیں، بیلنسر کو اس میں سوئچ کرتے ہیں، اور پرانے کنکشنز کے مکمل ہونے کا انتظار کرتے ہیں۔ تمام پرانے کنکشن ختم ہونے کے بعد، ہم سسٹم کی پرانی مثال کو بند کر دیتے ہیں۔

آئیے اب نمونے کے اندر یا باہر کنفیگریشن کو ذخیرہ کرنے کے مسئلے پر غور کریں۔ اگر ہم کسی نمونے کے اندر کنفیگریشن کو اسٹور کرتے ہیں، تو کم از کم ہمیں آرٹفیکٹ کی اسمبلی کے دوران کنفیگریشن کی درستگی کی تصدیق کرنے کا موقع ملتا تھا۔ اگر کنفیگریشن کنٹرول شدہ آرٹفیکٹ سے باہر ہے، تو یہ معلوم کرنا مشکل ہے کہ اس فائل میں کس نے اور کیوں تبدیلیاں کیں۔ یہ کتنا اہم ہے؟ ہماری رائے میں، بہت سے پروڈکشن سسٹمز کے لیے ایک مستحکم اور اعلیٰ معیار کی ترتیب کا ہونا ضروری ہے۔

آرٹفیکٹ کا ورژن آپ کو یہ تعین کرنے کی اجازت دیتا ہے کہ یہ کب بنایا گیا تھا، اس میں کون سی اقدار ہیں، کون سے فنکشنز فعال/غیر فعال ہیں، اور کنفیگریشن میں کسی تبدیلی کا ذمہ دار کون ہے۔ بلاشبہ، کسی نمونے کے اندر کنفیگریشن کو ذخیرہ کرنے کے لیے کچھ محنت درکار ہوتی ہے، لہذا آپ کو باخبر فیصلہ کرنے کی ضرورت ہے۔

پیشہ اور cons

میں مجوزہ ٹیکنالوجی کے فوائد اور نقصانات پر غور کرنا چاہوں گا۔

فوائد

ذیل میں مرتب شدہ تقسیم شدہ نظام کی ترتیب کی اہم خصوصیات کی فہرست ہے۔

  1. جامد کنفیگریشن چیک۔ آپ کو اس بات کا یقین کرنے کی اجازت دیتا ہے۔
    ترتیب درست ہے.
  2. رچ کنفیگریشن لینگوئج۔ عام طور پر، ترتیب کے دیگر طریقے زیادہ سے زیادہ سٹرنگ متغیر متبادل تک محدود ہوتے ہیں۔ Scala استعمال کرتے وقت، آپ کی کنفیگریشن کو بہتر بنانے کے لیے زبان کی خصوصیات کی ایک وسیع رینج دستیاب ہوتی ہے۔ مثال کے طور پر ہم استعمال کر سکتے ہیں۔
    ڈیفالٹ قدروں کے لیے خصائص، اشیاء کو گروپ پیرامیٹرز کے لیے استعمال کرتے ہوئے، ہم انکلونگ اسکوپ میں صرف ایک بار اعلان کردہ والز (DRY) کا حوالہ دے سکتے ہیں۔ آپ کسی بھی کلاس کو براہ راست کنفیگریشن کے اندر شروع کر سکتے ہیں (Seq, Map، حسب ضرورت کلاسز)۔
  3. ڈی ایس ایل۔ Scala میں زبان کی بہت سی خصوصیات ہیں جو DSLs بنانا آسان بناتی ہیں۔ ان خصوصیات سے فائدہ اٹھانا اور کنفیگریشن لینگویج کو لاگو کرنا ممکن ہے جو صارفین کے ٹارگٹ گروپ کے لیے زیادہ آسان ہو، تاکہ کنفیگریشن کم از کم ڈومین کے ماہرین کے ذریعے پڑھنے کے قابل ہو۔ ماہرین، مثال کے طور پر، ترتیب کے جائزے کے عمل میں حصہ لے سکتے ہیں۔
  4. نوڈس کے درمیان سالمیت اور ہم آہنگی۔ پورے ڈسٹری بیوٹڈ سسٹم کی کنفیگریشن کو ایک پوائنٹ پر رکھنے کا ایک فائدہ یہ ہے کہ تمام قدروں کو بالکل ایک بار ڈکلیئر کیا جاتا ہے اور پھر جہاں بھی ان کی ضرورت ہوتی ہے اسے دوبارہ استعمال کیا جاتا ہے۔ بندرگاہوں کا اعلان کرنے کے لیے فینٹم اقسام کا استعمال یقینی بناتا ہے کہ نوڈس تمام درست سسٹم کنفیگریشنز میں ہم آہنگ پروٹوکول استعمال کر رہے ہیں۔ نوڈس کے درمیان واضح لازمی انحصار کا ہونا یقینی بناتا ہے کہ تمام خدمات منسلک ہیں۔
  5. اعلی معیار کی تبدیلیاں۔ ایک مشترکہ ترقیاتی عمل کا استعمال کرتے ہوئے ترتیب میں تبدیلیاں کرنا کنفیگریشن کے لیے بھی اعلیٰ معیار کے معیارات کو حاصل کرنا ممکن بناتا ہے۔
  6. بیک وقت کنفیگریشن اپ ڈیٹ۔ کنفیگریشن تبدیلیوں کے بعد خودکار نظام کی تعیناتی اس بات کو یقینی بناتی ہے کہ تمام نوڈس اپ ڈیٹ ہیں۔
  7. درخواست کو آسان بنانا۔ ایپلیکیشن کو پارس، کنفیگریشن چیکنگ، یا غلط اقدار کو سنبھالنے کی ضرورت نہیں ہے۔ یہ درخواست کی پیچیدگی کو کم کرتا ہے۔ (کچھ کنفیگریشن پیچیدگی جو ہماری مثال میں دیکھی گئی ہے وہ مرتب کردہ کنفیگریشن کا کوئی وصف نہیں ہے، بلکہ صرف ایک شعوری فیصلہ ہے جو کہ زیادہ قسم کی حفاظت فراہم کرنے کی خواہش سے کیا گیا ہے۔) معمول کی ترتیب پر واپس آنا کافی آسان ہے - صرف لاپتہ کو لاگو کریں۔ حصے لہذا، مثال کے طور پر، آپ غیر ضروری حصوں کے نفاذ کو اس وقت تک موخر کرتے ہوئے جب تک اس کی واقعی ضرورت ہو، مرتب کردہ ترتیب سے شروع کر سکتے ہیں۔
  8. تصدیق شدہ ترتیب۔ چونکہ کنفیگریشن تبدیلیاں کسی بھی دوسری تبدیلی کے معمول کی تقدیر کی پیروی کرتی ہیں، اس لیے ہمیں جو آؤٹ پٹ ملتا ہے وہ ایک منفرد ورژن کے ساتھ ایک نمونہ ہے۔ یہ ہمیں، مثال کے طور پر، اگر ضروری ہو تو ترتیب کے پچھلے ورژن پر واپس جانے کی اجازت دیتا ہے۔ ہم ایک سال پہلے کی ترتیب کو بھی استعمال کر سکتے ہیں اور سسٹم بالکل ویسا ہی کام کرے گا۔ ایک مستحکم ترتیب تقسیم شدہ نظام کی پیشین گوئی اور وشوسنییتا کو بہتر بناتی ہے۔ چونکہ ترتیب تالیف کے مرحلے پر طے کی گئی ہے، اس لیے اسے پیداوار میں جعلی بنانا کافی مشکل ہے۔
  9. ماڈیولرٹی مجوزہ فریم ورک ماڈیولر ہے اور ماڈیولز کو مختلف طریقوں سے ملا کر مختلف سسٹمز بنائے جا سکتے ہیں۔ خاص طور پر، آپ سسٹم کو کنفیگر کر سکتے ہیں کہ ایک مجسم میں ایک نوڈ پر اور دوسرے میں ایک سے زیادہ نوڈس پر۔ آپ سسٹم کی پیداواری مثالوں کے لیے کئی کنفیگریشنز بنا سکتے ہیں۔
  10. ٹیسٹنگ انفرادی خدمات کو فرضی اشیاء سے بدل کر، آپ سسٹم کے کئی ورژن حاصل کر سکتے ہیں جو جانچ کے لیے آسان ہیں۔
  11. انضمام کی جانچ۔ پورے تقسیم شدہ نظام کے لیے ایک کنفیگریشن کا ہونا انضمام کی جانچ کے حصے کے طور پر تمام اجزاء کو کنٹرول شدہ ماحول میں چلانا ممکن بناتا ہے۔ نقل کرنا آسان ہے، مثال کے طور پر، ایسی صورت حال جہاں کچھ نوڈس قابل رسائی ہو جاتے ہیں۔

نقصانات اور حدود

مرتب کردہ کنفیگریشن دوسرے کنفیگریشن طریقوں سے مختلف ہے اور ہو سکتا ہے کہ کچھ ایپلی کیشنز کے لیے موزوں نہ ہو۔ ذیل میں کچھ نقصانات ہیں:

  1. جامد ترتیب۔ بعض اوقات آپ کو تمام حفاظتی میکانزم کو نظرانداز کرتے ہوئے پیداوار میں ترتیب کو فوری طور پر درست کرنے کی ضرورت ہوتی ہے۔ اس نقطہ نظر کے ساتھ یہ زیادہ مشکل ہوسکتا ہے۔ بہت کم از کم، تالیف اور خودکار تعیناتی اب بھی درکار ہوگی۔ یہ نقطہ نظر کی ایک مفید خصوصیت ہے اور بعض صورتوں میں ایک نقصان۔
  2. کنفیگریشن جنریشن۔ اگر کنفیگریشن فائل ایک خودکار ٹول کے ذریعہ تیار کی جاتی ہے تو، بلڈ اسکرپٹ کو ضم کرنے کے لیے اضافی کوششوں کی ضرورت پڑ سکتی ہے۔
  3. اوزار. فی الحال، کنفیگریشن کے ساتھ کام کرنے کے لیے تیار کردہ افادیت اور تکنیک ٹیکسٹ فائلوں پر مبنی ہیں۔ ایسی تمام افادیتیں/تکنیکیں مرتب کردہ ترتیب میں دستیاب نہیں ہوں گی۔
  4. رویوں میں تبدیلی کی ضرورت ہے۔ ڈویلپرز اور ڈی او اوپس ٹیکسٹ فائلوں کے عادی ہیں۔ کنفیگریشن کو مرتب کرنے کا خیال کچھ غیر متوقع اور غیر معمولی ہو سکتا ہے اور مسترد ہونے کا سبب بن سکتا ہے۔
  5. ایک اعلی معیار کی ترقی کے عمل کی ضرورت ہے. مرتب کردہ ترتیب کو آرام سے استعمال کرنے کے لیے، ایپلیکیشن (CI/CD) کی تعمیر اور تعیناتی کے عمل کی مکمل آٹومیشن ضروری ہے۔ بصورت دیگر یہ کافی تکلیف دہ ہوگا۔

آئیے ہم زیر غور مثال کی متعدد حدود پر بھی غور کریں جو مرتب کردہ ترتیب کے خیال سے متعلق نہیں ہیں:

  1. اگر ہم غیر ضروری کنفیگریشن معلومات فراہم کرتے ہیں جو نوڈ کے ذریعہ استعمال نہیں ہوتی ہے، تو کمپائلر لاپتہ نفاذ کا پتہ لگانے میں ہماری مدد نہیں کرے گا۔ یہ مسئلہ کیک پیٹرن کو ترک کرکے اور مزید سخت قسموں کو استعمال کرکے حل کیا جاسکتا ہے، مثال کے طور پر، HList یا الجبری ڈیٹا کی اقسام (کیس کلاسز) کنفیگریشن کی نمائندگی کرنے کے لیے۔
  2. کنفیگریشن فائل میں ایسی لائنیں ہیں جو خود کنفیگریشن سے متعلق نہیں ہیں: (package, import,اعتراضات override defکے پیرامیٹرز کے لیے جن کی ڈیفالٹ قدریں ہیں)۔ اس سے جزوی طور پر بچا جا سکتا ہے اگر آپ اپنے DSL کو لاگو کریں۔ اس کے علاوہ، ترتیب کی دیگر اقسام (مثال کے طور پر، XML) بھی فائل کی ساخت پر کچھ پابندیاں عائد کرتی ہیں۔
  3. اس پوسٹ کے مقاصد کے لیے، ہم ملتے جلتے نوڈس کے کلسٹر کی متحرک ری کنفیگریشن پر غور نہیں کر رہے ہیں۔

حاصل يہ ہوا

اس پوسٹ میں، ہم نے اسکالا ٹائپ سسٹم کی جدید صلاحیتوں کا استعمال کرتے ہوئے سورس کوڈ میں کنفیگریشن کی نمائندگی کرنے کے خیال کی کھوج کی۔ یہ نقطہ نظر XML یا ٹیکسٹ فائلوں پر مبنی روایتی کنفیگریشن طریقوں کے متبادل کے طور پر مختلف ایپلی کیشنز میں استعمال کیا جا سکتا ہے۔ اگرچہ ہماری مثال اسکالا میں لاگو ہوتی ہے، اسی طرح کے خیالات کو دوسری مرتب شدہ زبانوں (جیسے کوٹلن، سی #، سوئفٹ، ...) میں منتقل کیا جا سکتا ہے۔ آپ مندرجہ ذیل منصوبوں میں سے کسی ایک میں اس نقطہ نظر کو آزما سکتے ہیں، اور، اگر یہ کام نہیں کرتا ہے تو، گمشدہ حصوں کو شامل کرتے ہوئے، ٹیکسٹ فائل پر جائیں۔

قدرتی طور پر، ایک مرتب شدہ ترتیب کے لیے اعلیٰ معیار کے ترقیاتی عمل کی ضرورت ہوتی ہے۔ بدلے میں، کنفیگریشنز کے اعلیٰ معیار اور وشوسنییتا کو یقینی بنایا جاتا ہے۔

زیر غور نقطہ نظر کو بڑھایا جا سکتا ہے:

  1. آپ کمپائل ٹائم چیک کرنے کے لیے میکرو استعمال کر سکتے ہیں۔
  2. آپ کنفیگریشن کو اس طریقے سے پیش کرنے کے لیے ڈی ایس ایل کو لاگو کر سکتے ہیں جو آخری صارفین کے لیے قابل رسائی ہو۔
  3. آپ خودکار کنفیگریشن ایڈجسٹمنٹ کے ساتھ متحرک وسائل کے انتظام کو نافذ کر سکتے ہیں۔ مثال کے طور پر، کلسٹر میں نوڈس کی تعداد کو تبدیل کرنے کے لیے ضروری ہے کہ (1) ہر نوڈ کو قدرے مختلف کنفیگریشن ملے۔ (2) کلسٹر مینیجر نے نئے نوڈس کے بارے میں معلومات حاصل کیں۔

اعترافات

میں آرٹیکل کے مسودے پر تعمیری تنقید کے لیے آندرے ساکسونوف، پاول پوپوف اور انتون نیکائیف کا شکریہ ادا کرنا چاہوں گا۔

ماخذ: www.habr.com

نیا تبصرہ شامل کریں