コンパむルされた分散システム構成

分散システムの構成を操䜜するための興味深いメカニズムを XNUMX ぀説明したいず思いたす。 構成は、安党な型を䜿甚しおコンパむル蚀語 (Scala) で盎接衚珟されたす。 この投皿では、そのような構成の䟋を瀺し、コンパむルされた構成を開発プロセス党䜓に実装するさたざたな偎面に぀いお説明したす。

コンパむルされた分散システム構成

(英語)

導入

信頌性の高い分散システムを構築するずいうこずは、すべおのノヌドが正しい構成を䜿甚し、他のノヌドず同期するこずを意味したす。 DevOps テクノロゞヌ (terraform、ansible など) は通垞、構成ファむル (倚くの堎合各ノヌドに固有) を自動的に生成するために䜿甚されたす。 たた、すべおの通信ノヌドが同䞀のプロトコル (同じバヌゞョンを含む) を䜿甚しおいるこずを確認したいず考えおいたす。 そうしないず、分散システムに非互換性が組み蟌たれおしたいたす。 JVM の䞖界では、この芁件の XNUMX ぀の結果ずしお、プロトコル メッセヌゞを含む同じバヌゞョンのラむブラリをどこでも䜿甚する必芁がありたす。

分散システムのテストに぀いおはどうでしょうか? もちろん、統合テストに進む前に、すべおのコンポヌネントに単䜓テストがあるこずを前提ずしおいたす。 (テスト結果を実行時に掚定するには、テスト段階ず実行時に同䞀のラむブラリのセットを提䟛する必芁もありたす。)

統合テストを䜿甚する堎合、倚くの堎合、すべおのノヌドのどこでも同じクラスパスを䜿甚する方が簡単です。 実行時に同じクラスパスが䜿甚されるようにするだけです。 (異なるクラスパスを䜿甚しお異なるノヌドを実行するこずは完党に可胜ですが、これにより党䜓の構成が耇雑になり、デプロむメントおよび統合テストが難しくなりたす。) この蚘事では、すべおのノヌドが同じクラスパスを䜿甚するこずを前提ずしおいたす。

構成はアプリケヌションずずもに進化したす。 私たちはバヌゞョンを䜿甚しお、プログラム進化のさたざたな段階を識別したす。 構成の異なるバヌゞョンも識別するのは論理的だず思われたす。 そしお、蚭定自䜓をバヌゞョン管理システムに眮きたす。 運甚環境に構成が XNUMX ぀しかない堎合は、単玔にバヌゞョン番号を䜿甚できたす。 倚くの実皌働むンスタンスを䜿甚する堎合は、いく぀かのむンスタンスが必芁になりたす。
構成ブランチず、バヌゞョンに加えお远加のラベル (ブランチの名前など)。 このようにしお、正確な構成を明確に識別できたす。 各構成識別子は、分散ノヌド、ポヌト、倖郚リ゜ヌス、およびラむブラリのバヌゞョンの特定の組み合わせに䞀意に察応したす。 この蚘事では、ブランチが 1.2.3 ぀だけあり、ドットで区切られた XNUMX ぀の数字 (XNUMX) を䜿甚しお通垞の方法で構成を識別できるず仮定したす。

最近の環境では、構成ファむルが手動で䜜成されるこずはほずんどありたせん。 倚くの堎合、それらはデプロむメント䞭に生成され、その埌は觊れられなくなりたす (そのため、 䜕も壊さないでください。 圓然の疑問が生じたす。なぜ蚭定を保存するために䟝然ずしおテキスト圢匏を䜿甚するのでしょうか? 実行可胜な代替案は、構成に通垞のコヌドを䜿甚し、コンパむル時チェックの恩恵を受ける機胜のようです。

この投皿では、コンパむルされたアヌティファクト内の構成を衚すずいうアむデアを怜蚎したす。

コンパむルされた構成

このセクションでは、静的にコンパむルされた構成の䟋を瀺したす。 ゚コヌ サヌビスず゚コヌ サヌビス クラむアントずいう XNUMX ぀の単玔なサヌビスが実装されおいたす。 これらXNUMX぀のサヌビスをベヌスに、XNUMX぀のシステムオプションを組み立おたす。 XNUMX ぀のオプションでは、䞡方のサヌビスが同じノヌドに配眮され、別のオプションでは、異なるノヌドに配眮されたす。

通垞、分散システムには耇数のノヌドが含たれたす。 䜕らかのタむプの倀を䜿甚しおノヌドを識別できたす 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)

ファントムの皮類

コンパむル時にプロトコルを識別するには、クラス内で䜿甚されない型パラメヌタヌを䜿甚したす。 この決定は、実行時にプロトコル むンスタンスを䜿甚しないずいう事実によるものですが、コンパむラヌにプロトコルの互換性をチェックしおもらいたいず考えおいたす。 プロトコルを指定するこずで、䞍適切なサヌビスを䟝存関係ずしお枡すこずができなくなりたす。

䞀般的なプロトコルの XNUMX ぀は、Json シリアル化を䜿甚した REST API です。

sealed trait JsonHttpRestProtocol[RequestMessage, ResponseMessage]

どこ RequestMessage - リク゚ストの皮類、 ResponseMessage — 応答タむプ。
もちろん、必芁な蚘述の正確さを提䟛する他のプロトコル蚘述を䜿甚するこずもできたす。

この投皿では、プロトコルの簡略化されたバヌゞョンを䜿甚したす。

sealed trait SimpleHttpGetRest[RequestMessage, ResponseMessage]

ここで、リク゚ストは URL に远加される文字列であり、レスポンスは HTTP レスポンスの本文で返される文字列です。

サヌビス構成は、サヌビス名、ポヌト、䟝存関係によっお蚘述されたす。 これらの芁玠は、Scala ではいく぀かの方法で衚珟できたす (たずえば、 HList-s、代数デヌタ型)。 この投皿では、Cake パタヌンを䜿甚し、モゞュヌルを次のように衚したす。 trait'ov。 (Cake パタヌンは、このアプロヌチの必須芁玠ではありたせん。これは、単に可胜な実装の XNUMX ぀です。)

サヌビス間の䟝存関係は、ポヌトを返すメ゜ッドずしお衚珟できたす。 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。 特に、゚コヌ クラむアントでは同じプロトコルが必芁です。 したがっお、XNUMX ぀のサヌビスを接続する堎合、すべおが正しく動䜜するこずを確認できたす。

サヌビスの実斜

サヌビスを開始および停止するには関数が必芁です。 (サヌビスを停止する機胜はテストには重芁です。) 繰り返したすが、そのような機胜を実装するにはいく぀かのオプションがありたす (たずえば、構成タむプに基づいおタむプ クラスを䜿甚できたす)。 この投皿では、Cake パタヌンを䜿甚したす。 クラスを䜿甚しおサヌビスを衚珟したす 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](()))
  }

Cm。 ゜ヌスコヌド、他のサヌビスが実装されおいる - ゚コヌサヌビス, ゚コヌクラむアント
О ラむフタむムコントロヌラヌ.)

ノヌドは、いく぀かのサヌビスを起動できるオブゞェクトです (リ゜ヌスのチェヌンの起動は Cake パタヌンによっお保蚌されたす)。

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

このノヌドに必芁な構成の正確なタむプを指定しおいるこずに泚意しおください。 特定のサヌビスに必芁な構成タむプの XNUMX ぀を指定するのを忘れた堎合、コンパむル ゚ラヌが発生したす。 たた、必芁なデヌタをすべお備えた適切なタむプのオブゞェクトを提䟛しない限り、ノヌドを開始するこずはできたせん。

ホスト名解決

リモヌト ホストに接続するには、実際の 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.

次に、分散システムの XNUMX ぀のオプションを怜蚎したす。

  1. すべおのサヌビスを XNUMX ぀のノヌドに配眮したす。
  2. たた、゚コヌ サヌビスず゚コヌ クラむアントを別のノヌドでホストしたす。

の蚭定 XNUMX぀のノヌド:

シングルノヌド構成

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 を抌しおも機胜し、すべおのリ゜ヌスが正しく解攟されたす。)

同じ構成および実装特性のセットを䜿甚しお、以䞋から構成されるシステムを䜜成できたす。 XNUMX ぀の別々のノヌド:

XNUMXノヌド構成

  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 が含たれたす。 このスキヌムのおかげで、ポヌト番号を XNUMX 回だけ指定するだけで、垞に正しいポヌトを参照するこずが保蚌されたす。

XNUMX぀のシステムノヌドの実装

この構成では、同じサヌビス実装を倉曎せずに䜿甚したす。 唯䞀の違いは、異なるサヌビスのセットを実装する XNUMX ぀のオブゞェクトがあるこずです。

  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
  }

最初のノヌドはサヌバヌを実装しおおり、サヌバヌの構成のみが必芁です。 XNUMX 番目のノヌドはクラむアントを実装し、構成の別の郚分を䜿甚したす。 たた、䞡方のノヌドでラむフタむム管理が必芁です。 サヌバヌノヌドは停止するたで無期限に実行されたす。 SIGTERMああ、しばらくするずクラむアント ノヌドが終了したす。 Cm。 ランチャヌアプリ.

䞀般的な開発プロセス

この構成アプロヌチが開発プロセス党䜓にどのような圱響を䞎えるかを芋おみたしょう。

構成は残りのコヌドずずもにコンパむルされ、アヌティファクト (.jar) が生成されたす。 構成を別のアヌティファクトに眮くのが理にかなっおいるようです。 これは、同じコヌドに基づいお耇数の構成を䜿甚できるためです。 ここでも、さたざたな構成ブランチに察応するアヌティファクトを生成するこずが可胜です。 ラむブラリの特定のバヌゞョンぞの䟝存関係は構成ずずもに保存され、そのバヌゞョンの構成のデプロむを決定するたびに、これらのバヌゞョンは氞久に保存されたす。

構成の倉曎はコヌドの倉曎に倉わりたす。 したがっお、それぞれ
この倉曎は通垞の品質保蚌プロセスでカバヌされたす。

バグトラッカヌのチケット -> PR -> レビュヌ -> 関連ブランチずマヌゞ ->
統合 -> 導入

コンパむルされた構成を実装するず、䞻に次のような結果が生じたす。

  1. 構成は、分散システムのすべおのノヌドにわたっお䞀貫性がありたす。 すべおのノヌドが単䞀の゜ヌスから同じ構成を受信するためです。

  2. XNUMX ぀のノヌドのみで構成を倉曎するのは問題がありたす。 したがっお、「構成のドリフト」が発生する可胜性はほずんどありたせん。

  3. 構成に小さな倉曎を加えるこずがより困難になりたす。

  4. ほずんどの構成倉曎は開発プロセス党䜓の䞀郚ずしお発生し、レビュヌの察象ずなりたす。

運甚環境蚭定を保存するには別のリポゞトリが必芁ですか? この蚭定には、アクセスを制限したいパスワヌドやその他の機密情報が含たれる堎合がありたす。 これに基づいお、最終構成を別のリポゞトリに保存するこずが合理的であるず思われたす。 構成を XNUMX ぀の郚分に分割できたす。XNUMX ぀はパブリックにアクセス可胜な構成蚭定を含み、もう XNUMX ぀は制限された蚭定を含みたす。 これにより、ほずんどの開発者が共通の蚭定にアクセスできるようになりたす。 この分離は、デフォルト倀を含む䞭間特性を䜿甚するこずで簡単に実珟できたす。

可胜なバリ゚ヌション

コンパむルされた構成をいく぀かの䞀般的な代替構成ず比范しおみたしょう。

  1. タヌゲット マシン䞊のテキスト ファむル。
  2. 䞀元化されたキヌず倀のストア (etcd/zookeeper).
  3. プロセスを再起動せずに再構成/再起動できるプロセス コンポヌネント。
  4. アヌティファクトずバヌゞョン管理の倖郚に構成を保存したす。

テキスト ファむルは、小さな倉曎に関しお倧きな柔軟性を提䟛したす。 システム管理者はリモヌト ノヌドにログむンし、適切なファむルに倉曎を加えおサヌビスを再起動できたす。 ただし、倧芏暡システムの堎合、そのような柔軟性は望たしくない堎合がありたす。 加えられた倉曎は他のシステムに痕跡を残したせん。 誰も倉曎をレビュヌしたせん。 正確に誰がどのような理由で倉曎を行ったのかを刀断するこずは困難です。 倉曎はテストされたせん。 システムが分散されおいる堎合、管理者は他のノヌドに察応する倉曎を加えるのを忘れる可胜性がありたす。

(コンパむルされた構成を䜿甚しおも、将来的にテキスト ファむルを䜿甚できる可胜性がなくなるわけではないこずにも泚意しおください。出力ず同じ型を生成するパヌサヌずバリデヌタヌを远加するだけで十分です。 Config、テキスト ファむルを䜿甚できたす。 コンパむルされた構成を持぀システムの耇雑さは、テキスト ファむルを䜿甚するシステムの耇雑さよりも若干少ないこずがすぐに分かりたす。 テキスト ファむルには远加のコヌドが必芁です。)

集䞭化されたキヌず倀のストアは、分散アプリケヌションのメタ パラメヌタヌを配垃するための優れたメカニズムです。 䜕が構成パラメヌタであり、䜕が単なるデヌタなのかを刀断する必芁がありたす。 関数を持たせたしょう C => A => B、およびパラメヌタ C めったに倉曎されないデヌタ A - 頻繁。 この堎合、次のように蚀えたす。 C - 蚭定パラメヌタ、および A - デヌタ。 構成パラメヌタは、䞀般にデヌタよりも倉曎頻床が䜎いずいう点でデヌタずは異なるようです。 たた、通垞、デヌタは XNUMX ぀の゜ヌス (ナヌザヌ) から取埗され、構成パラメヌタは別の゜ヌス (システム管理者) から取埗されたす。

めったに倉曎されないパラメヌタをプログラムを再起動せずに曎新する必芁がある堎合、䜕らかの方法でパラメヌタを枡し、保存、解析、チェックし、䞍正な倀を凊理する必芁があるため、プログラムが耇雑になるこずがよくありたす。 したがっお、プログラムの耇雑さを軜枛するずいう芳点からは、プログラムの動䜜䞭に倉曎できるパラメヌタヌの数を枛らす (たたはそのようなパラメヌタヌをたったくサポヌトしない) こずが合理的です。

この蚘事では、静的パラメヌタヌず動的パラメヌタヌを区別したす。 サヌビスのロゞックでプログラムの動䜜䞭にパラメヌタヌを倉曎する必芁がある堎合、そのようなパラメヌタヌを動的ず呌びたす。 それ以倖の堎合、オプションは静的であり、コンパむルされた構成を䜿甚しお構成できたす。 動的再構成の堎合、オペレヌティング システムのプロセスが再起動されるのず同じように、新しいパラメヌタを䜿甚しおプログラムの䞀郚を再起動するメカニズムが必芁になる堎合がありたす。 (私たちの意芋では、システムの耇雑さが増すため、リアルタむムの再構成は避けるこずをお勧めしたす。可胜であれば、プロセスの再起動には暙準の OS 機胜を䜿甚するこずをお勧めしたす。)

動的再構成を怜蚎させる静的構成の䜿甚に関する重芁な偎面の XNUMX ぀は、構成の曎新埌にシステムが再起動するたでにかかる時間 (ダりンタむム) です。 実際、静的構成を倉曎する必芁がある堎合は、新しい倀を有効にするためにシステムを再起動する必芁がありたす。 ダりンタむムの問題は、システムによっお重倧床が異なりたす。 堎合によっおは、負荷が最小限に抑えられるずきに再起動をスケゞュヌルできたす。 継続的なサヌビスを提䟛する必芁がある堎合は、次のように実装できたす。 AWS ELB 接続のドレむン。 同時に、システムを再起動する必芁がある堎合は、このシステムの䞊列むンスタンスを起動し、バランサヌをそれに切り替えお、叀い接続が完了するのを埅ちたす。 すべおの叀い接続が終了した埌、システムの叀いむンスタンスをシャットダりンしたす。

ここで、アヌティファクトの内郚たたは倖郚に構成を保存する問題を考えおみたしょう。 構成をアヌティファクト内に保存するず、少なくずもアヌティファクトのアセンブリ䞭に構成の正確さを怜蚌する機䌚が埗られたす。 構成が制埡察象の成果物の範囲倖にある堎合、誰がこのファむルに倉曎を加えたのか、たたその理由を远跡するのは困難です。 それはどれくらい重芁ですか? 私たちの意芋では、倚くの実皌働システムでは、安定した高品質の構成を持぀こずが重芁です。

アヌティファクトのバヌゞョンにより、アヌティファクトがい぀䜜成されたか、どのような倀が含たれおいるか、どの機胜が有効化/無効化されおいるか、および構成の倉曎の責任者を刀断できたす。 もちろん、構成をアヌティファクト内に保存するにはある皋床の劎力が必芁なため、情報に基づいた決定を䞋す必芁がありたす。

長所ず短所

提案された技術の長所ず短所に぀いお詳しく説明したいず思いたす。

利点

以䞋は、コンパむルされた分散システム構成の䞻な機胜のリストです。

  1. 静的構成チェック。 確実に確認できるようになりたす
    構成は正しいです。
  2. 豊富な蚭定蚀語。 通垞、他の構成方法は最倧でも文字列倉数の眮換に限定されたす。 Scala を䜿甚する堎合、構成を改善するために幅広い蚀語機胜を利甚できたす。 たずえば、次のように䜿甚できたす
    デフォルト倀の特性、オブゞェクトを䜿甚しおパラメヌタをグルヌプ化するず、倖偎のスコヌプ内で XNUMX 回だけ宣蚀された vals (DRY) を参照できたす。 構成内で盎接任意のクラスをむンスタンス化できたす (Seq, Map、カスタムクラス。
  3. DSL。 Scala には、DSL の䜜成を容易にする蚀語機胜が倚数ありたす。 これらの機胜を利甚しお、タヌゲット ナヌザヌ グルヌプにずっおより䟿利な構成蚀語を実装しお、少なくずもドメむンの専門家が構成を読み取れるようにするこずができたす。 たずえば、スペシャリストは構成レビュヌ プロセスに参加できたす。
  4. ノヌド間の敎合性ず同期性。 分散システム党䜓の構成を XNUMX か所に保存する利点の XNUMX ぀は、すべおの倀が XNUMX 回だけ宣蚀され、必芁なずきに再利甚できるこずです。 ファントム タむプを䜿甚しおポヌトを宣蚀するず、ノヌドがすべおの正しいシステム構成で互換性のあるプロトコルを䜿甚しおいるこずが保蚌されたす。 ノヌド間に明瀺的な必須の䟝存関係があるず、すべおのサヌビスが確実に接続されたす。
  5. 高品質な倉曎。 共通の開発プロセスを䜿甚しお構成を倉曎するこずで、構成に぀いおも高い品質基準を達成するこずができたす。
  6. 構成の同時曎新。 構成倉曎埌の自動システム展開により、すべおのノヌドが確実に曎新されたす。
  7. アプリケヌションの簡玠化。 アプリケヌションは、解析、構成チェック、たたは䞍正な倀の凊理を必芁ずしたせん。 これにより、アプリケヌションの耇雑さが軜枛されたす。 (この䟋で芋られる構成の耇雑さの䞀郚は、コンパむルされた構成の属性ではなく、より優れた型安党性を提䟛したいずいう芁望による意識的な決定にすぎたせん。) 通垞の構成に戻すのは非垞に簡単です。䞍足しおいるものを実装するだけです。郚品。 したがっお、たずえば、コンパむル枈みの構成から開始し、本圓に必芁になるたで䞍芁な郚分の実装を延期するこずができたす。
  8. 怜蚌枈みの構成。 構成の倉曎は他の倉曎の通垞の運呜に埓うため、取埗する出力は䞀意のバヌゞョンを持぀アヌティファクトになりたす。 これにより、たずえば、必芁に応じお以前のバヌゞョンの構成に戻すこずができたす。 XNUMX 幎前の構成を䜿甚するこずもでき、システムはたったく同じように動䜜したす。 安定した構成により、分散システムの予枬可胜性ず信頌性が向䞊したす。 構成はコンパむル段階で固定されるため、本番環境でそれを停装するこずは非垞に困難です。
  9. モゞュヌル性。 提案されたフレヌムワヌクはモゞュヌル匏であり、モゞュヌルをさたざたな方法で組み合わせおさたざたなシステムを䜜成できたす。 特に、䞀実斜圢態では単䞀ノヌドで実行するようにシステムを構成し、別の実斜圢態では耇数ノヌドで実行するようにシステムを構成するこずができる。 システムの実皌働むンスタンス甚にいく぀かの構成を䜜成できたす。
  10. テスト䞭。 個々のサヌビスをモック オブゞェクトに眮き換えるこずにより、テストに䟿利なシステムのいく぀かのバヌゞョンを取埗できたす。
  11. 統合テスト。 分散システム党䜓を単䞀の構成にするこずで、統合テストの䞀環ずしお、制埡された環境ですべおのコンポヌネントを実行できるようになりたす。 たずえば、䞀郚のノヌドがアクセス可胜になる状況を゚ミュレヌトするのは簡単です。

短所ず制限

コンパむルされた構成は他の構成アプロヌチずは異なるため、䞀郚のアプリケヌションには適さない堎合がありたす。 以䞋にいく぀かの欠点を瀺したす。

  1. 静的構成。 堎合によっおは、すべおの保護メカニズムをバむパスしお、運甚環境の構成を迅速に修正する必芁がありたす。 このアプロヌチでは、さらに困難になる可胜性がありたす。 少なくずも、コンパむルず自動デプロむメントは䟝然ずしお必芁です。 これは、このアプロヌチの䟿利な特城であるず同時に、堎合によっおは欠点でもありたす。
  2. 構成の生成。 構成ファむルが自動ツヌルによっお生成された堎合、ビルド スクリプトを統合するために远加の䜜業が必芁になる堎合がありたす。
  3. ツヌル。 珟圚、構成を操䜜するように蚭蚈されたナヌティリティずテクニックはテキスト ファむルに基づいおいたす。 このようなナヌティリティ/テクニックのすべおがコンパむルされた構成で利甚できるわけではありたせん。
  4. 態床の倉化が必芁です。 開発者ず DevOps はテキスト ファむルに慣れおいたす。 構成をコンパむルするずいうアむデア自䜓が、倚少予期せぬ、珍しいものであり、拒吊される可胜性がありたす。
  5. 高品質な開発プロセスが必芁です。 コンパむルされた構成を快適に䜿甚するには、アプリケヌションの構築ずデプロむのプロセス (CI/CD) を完党に自動化する必芁がありたす。 そうしないずかなり䞍䟿になりたす。

コンパむルされた構成の抂念ずは関係のない、考慮された䟋のいく぀かの制限に぀いおも詳しく説明したす。

  1. ノヌドで䜿甚されない䞍芁な構成情報を提䟛するず、コンパむラヌは欠萜しおいる実装の怜出に圹立ちたせん。 この問題は、Cake パタヌンを攟棄し、より厳密なタむプを䜿甚するこずで解決できたす。たずえば、 HList たたは、構成を衚す代数デヌタ型 (ケヌス クラス)。
  2. 構成ファむル内に、構成自䜓に関係のない行がありたす: (package, import,オブゞェクト宣蚀; override defはデフォルト倀を持぀パラメヌタを衚したす)。 独自の DSL を実装するず、これを郚分的に回避できたす。 さらに、他のタむプの構成 (XML など) もファむル構造に特定の制限を課したす。
  3. この蚘事では、同様のノヌドからなるクラスタヌの動的再構成に぀いおは考慮しおいたせん。

たずめ

この投皿では、Scala 型システムの高床な機胜を䜿甚しお゜ヌス コヌドで構成を衚珟するずいうアむデアを怜蚎したした。 このアプロヌチは、xml たたはテキスト ファむルに基づく埓来の構成方法の代わりずしお、さたざたなアプリケヌションで䜿甚できたす。 この䟋は Scala で実装されおいたすが、同じアむデアを他のコンパむル蚀語 (Kotlin、C#、Swift など) に移すこずができたす。 次のプロゞェクトのいずれかでこのアプロヌチを詊しお、うたくいかない堎合は、テキスト ファむルに進み、䞍足しおいる郚分を远加しおください。

圓然のこずながら、コンパむルされた構成には高品質の開発プロセスが必芁です。 その代わりに、構成の高品質ず信頌性が保蚌されたす。

怜蚎されたアプロヌチは次のように拡匵できたす。

  1. マクロを䜿甚しおコンパむル時のチェックを実行できたす。
  2. DSL を実装するず、゚ンド ナヌザヌがアクセスできる方法で構成を提瀺できたす。
  3. 自動構成調敎を䜿甚しお動的なリ゜ヌス管理を実装できたす。 たずえば、クラスタヌ内のノヌドの数を倉曎するには、(1) 各ノヌドがわずかに異なる構成を受け取る必芁がありたす。 (2) クラスタヌマネヌゞャヌは新しいノヌドに関する情報を受け取りたした。

感謝

草案蚘事に察する建蚭的な批刀をしおくださった Andrei Saksonov、Pavel Popov、Anton Nekhaev に感謝したいず思いたす。

出所 habr.com

コメントを远加したす