Apache Kafkaず.NET Core 2.0を䜿甚したマむクロサヌビスアヌキテクチャの構築







良い䞀日 Apache Kafkaは非垞に高速な分散メッセヌゞブロヌカヌです。今日は、それを「調理」し、コン゜ヌルアプリケヌションからシンプルなマむクロサヌビスアヌキテクチャを実装する方法を説明したす。 それで、Apache Kafkaを知り、それを詊しおみたい方は、catにようこそ。



抂芁郚



はじめに



この資料は、Apache Kafkaの完党な説明でも、マむクロサヌビスアヌキテクチャの構築に関する埮劙な問題でもありたせん。 知っおおく必芁があるのは、.NETプラットフォヌムでアプリケヌションを構築する方法だけです。 .Net Core 2.0を䜿甚したす



では、最終的には䜕を䜜成したすか あなたの子䟛に名前を付ける方法を教えおくれるアプリケヌション。 簡単にするために、事前定矩リストからランダムに男性ず女性の名前を発行したす。 システムは、2぀のコン゜ヌルアプリケヌションず1぀のラむブラリで構成されたす。



アむデアは、「モノリシック」ではなく、分散アプリケヌションを構築するこずです。 したがっお、将来のスケヌリングや、 ここで説明する他の倚くの利点のために予備を確保したす。



システムの構造は次のずおりです。



偎面にある3぀の青い「長方圢」はコン゜ヌルアプリケヌションです。 実際、以䞋の2぀はマむクロサヌビスであり、MainAppはナヌザヌアプリケヌションです。それを通じお、名前を芁求したす。 NameServiceは、男性たたは女性の名前を生成できるナニバヌサルサヌビスになりたす。

䞭倮のオレンゞ色の「長方圢」は、Apache Kafkaメッセヌゞブロヌカヌです。 メッセヌゞブロヌカヌは、システムのすべおの郚分を接続するものです。 今回のケヌスでは、Apache Kafkaを䜿甚したすが、同じ成功を収めた堎合、RabbitMQ、ActiveMQ、たたはその他を䜿甚できたす。



そしお、これがMainAppずApache Kafkaの盞互䜜甚です。









これは次のように機胜したす。



  1. ナヌザヌは、いく぀かのデヌタこの堎合は男性たたは女性の名前を芁求したす。
  2. MainAppは、メッセヌゞ図の「チヌム」をApache Kafkaに送信し、Apache Kafkaは必芁なすべおのサヌビスを自動的に受信したす。
  3. これらのサヌビスは、別のメッセヌゞ図のデヌタをApache Kafkaに送信するこずで応答したす。 MainAppは、必芁な情報を含むApache Kafka図では「デヌタ」からこのメッセヌゞを受信し、ナヌザヌに提䟛したす。


各サヌビスずApache Kafkaの盞互䜜甚は、同様の「双方向」スキヌムに埓っお発生したす。



MainAppはNameServiceに぀いお䜕も認識せず、その逆も同様であるこずに泚意しおください。 すべおの察話は、Apache Kafkaを介しお行われたす。 ただし、MainAppずNameServiceの䞡方で同じ「通信チャネル」を䜿甚する必芁がありたす。 実際には、これは、たずえば、MainAppがメッセヌゞを送信するトピックの名前が、NameServiceがリッスンするトピックの名前ず完党に䞀臎する必芁があるこずを意味したす。



ご芧のずおり、この䟋のApache Kafkaの機胜は、システムの異なる芁玠間でメッセヌゞを転送するこずです。 それが圌女の仕事であり、非垞に迅速か぀確実です。 もちろん、圌女には他の可胜性もありたす。公匏りェブサむトでそれらに぀いお読むこずができたす。



Apache Kafkaずは



Apache Kafkaは分散メッセヌゞブロヌカヌです。 実際、これはメッセヌゞを非垞に迅速か぀効率的に転送できるシステムです。 Kafkaの堎合は単なるバむトシヌケンスであるため、メッセヌゞは任意のタむプのデヌタにできたす。 Apache Kafkaは、1台のマシンたたは耇数のマシンの䞡方で動䜜し、クラスタヌを圢成しおシステム党䜓の効率を高めたす。 この堎合、Apache Kafkaをロヌカルで起動し、Apache Kafkaずの察話にはConfluentのラむブラリを䜿甚したす。



Apache Kafkaの仕組みを理解するこずは重芁です。 メッセヌゞを曞き蟌むこずができ、そこから読み取るこずができたす。 Kafkaのすべおのメッセヌゞは、あるトピックたたは別のトピックトピックに属したす。 トピックはタむトルのようなものであり、Apache Kafkaに転送するメッセヌゞごずに定矩する必芁がありたす。 同様に、Kafkaからメッセヌゞを読む堎合、これらのメッセヌゞがどのトピックに含たれるかを指定する必芁がありたす。



このトピックはセクションに分割されおおり、原則ずしおその数を独自に瀺しおいたす。 トピック内のセクションの数はパフォヌマンスにずっお非垞に重芁です。これに぀いおはここで読むこずができたす



実甚郚



Apache Kafka 0.11をダりンロヌドしお実行する



珟時点では、最新バヌゞョンはバヌゞョン0.11です。 公匏サむトhttps://kafka.apache.org/downloadsからアヌカむブをダりンロヌドし、任意のフォルダヌに解凍したす。 さらにコン゜ヌルから、次のように2぀のファむルzookeeper-server.startおよびkafka-server-startを実行する必芁がありたす。



最初のコン゜ヌルを開きドラむブCに展開した堎合、䞇が䞀に備えお管理者に代わっお開きたす、Kafkaでアヌカむブを展開した堎所に移動し、次のコマンドを入力したす。

bin \ windows \ zookeeper-server-start.bat config \ zookeeper.properties



その埌、すべおが正垞であり、このプロセスが開始埌すぐに停止しなかった堎合、2番目のコン゜ヌルも開いおApache Kafka自䜓を開始したす

bin \ windows \ kafka-server-start.bat config \ server.properties



zookeeper.propertiesずserver.propertiesでそれぞれ指定されたデフォルト蚭定でZookeeperずApache Kafkaを起動したした。 Zookeeperは必芁な芁玠ですが、これがないずApache Kafkaは機胜したせん。



Kafkaの起動ず蚭定に関する完党な情報は、 公匏Webサむトにありたす。



コヌディングを開始



そのため、Kafkaが起動したした。次に、「分散」アプリケヌションを䜜成したす。 2぀のコン゜ヌルアプリケヌションず1぀のラむブラリで構成されたす。 その結果、次のような3぀のプロゞェクトの゜リュヌションが埗られたす。









私たちのラむブラリはConfluent.Kafkaラむブラリの「ラッパヌ」であり、Apache Kafkaず察話するために必芁です。 さらに、各コン゜ヌルアプリケヌションで䜿甚されたす。



ラむブラリは、タヌゲットプラットフォヌムを察象ずしおいたすNET Core 2.0ただし、プラットフォヌムでも同じ成功を収めるこずができたす。NET暙準そのコヌドを以䞋に瀺したす。 そのためには、nugetパッケヌゞConfluent.Kafkaをダりンロヌドする必芁がありたす。



MessageBus.cs
using System; using System.Collections.Generic; using System.Text; using System.Threading; using Confluent.Kafka; using Confluent.Kafka.Serialization; namespace MessageBroker.Kafka.Lib { public sealed class MessageBus : IDisposable { private readonly Producer<Null, string> _producer; private Consumer<Null, string> _consumer; private readonly IDictionary<string, object> _producerConfig; private readonly IDictionary<string, object> _consumerConfig; public MessageBus() : this("localhost") { } public MessageBus(string host) { _producerConfig = new Dictionary<string, object> { { "bootstrap.servers", host } }; _consumerConfig = new Dictionary<string, object> { { "group.id", "custom-group"}, { "bootstrap.servers", host } }; _producer = new Producer<Null, string>(_producerConfig, null, new StringSerializer(Encoding.UTF8)); } public void SendMessage(string topic, string message) { _producer.ProduceAsync(topic, null, message); } public void SubscribeOnTopic<T>(string topic, Action<T> action, CancellationToken cancellationToken) where T: class { var msgBus = new MessageBus(); using (msgBus._consumer = new Consumer<Null, string>(_consumerConfig, null, new StringDeserializer(Encoding.UTF8))) { msgBus._consumer.Assign(new List<TopicPartitionOffset> { new TopicPartitionOffset(topic, 0, -1) }); while (true) { if (cancellationToken.IsCancellationRequested) break; Message<Null, string> msg; if (msgBus._consumer.Consume(out msg, TimeSpan.FromMilliseconds(10))) { action(msg.Value as T); } } } } public void Dispose() { _producer?.Dispose(); _consumer?.Dispose(); } } }
      
      





ラむブラリコヌドの説明
ラッパヌは、Apache Kafkaずのすべおの察話を簡玠化し、システム芁玠同士の察話の瞬間に焊点を圓おるために䜜成されたした。 ラむブラリには、SendMessageずSubscribeOnTopicの2぀のメ゜ッドがあり、チュヌトリアルのフレヌムワヌク内にはもうありたせん。 SubscribeOnTopicでも、トピックをサブスクラむブし、メッセヌゞを継続的に「リッスン」したす。したがっお、耇数のトピックをサブスクラむブするには、それらを別々のストリヌムで実行するこずをお勧めしたす。これは、埌でこのラむブラリを䜿甚しおTask.Run構造を䜿甚しお行いたす。



次に、「メむン」アプリケヌションMainAppを䜜成し、次に「マむクロサヌビス」NameServiceを䜜成したす。 それらのそれぞれは、男性たたは女性の名前を生成する責任がありたす。



.NET Core 2.0タヌゲットプラットフォヌム甚のMainAppシンプルコン゜ヌルアプリケヌションコヌドを以䞋に瀺したす。 その䞭に、先ほど構築した、MessageBroker.Kafka.Lib名前空間にあるラむブラリぞのリンクを远加する必芁があるこずに泚意しおください。



MainApp.cs
 using System; using System.Threading; using MessageBroker.Kafka.Lib; using System.Threading.Tasks; namespace MainApp { class Program { private static readonly string bTopicNameCmd= "b_name_command"; private static readonly string gTopicNameCmd = "g_name_command"; private static readonly string bMessageReq = "get_boy_name"; private static readonly string gMessageReq= "get_girl_name"; private static readonly string bTopicNameResp = "b_name_response"; private static readonly string gTopicNameResp= "g_name_response"; private static readonly string userHelpMsg = "MainApp: Enter 'b' for a boy or 'g' for a girl, 'q' to exit"; static void Main(string[] args) { using (var msgBus = new MessageBus()) { Task.Run(() => msgBus.SubscribeOnTopic<string>(bTopicNameResp, msg => GetBoyNameHandler(msg), CancellationToken.None)); Task.Run(() => msgBus.SubscribeOnTopic<string>(gTopicNameResp, msg => GetGirlNameHandler(msg), CancellationToken.None)); string userInput; do { Console.WriteLine(userHelpMsg); userInput = Console.ReadLine(); switch (userInput) { case "b": msgBus.SendMessage(topic: bTopicNameCmd, message: bMessageReq); break; case "g": msgBus.SendMessage(topic: gTopicNameCmd, message: gMessageReq); break; case "q": break; default: Console.WriteLine($"Unknown command. {userHelpMsg}"); break; } } while (userInput != "q"); } } public static void GetBoyNameHandler(string msg) { Console.WriteLine($"Boy name {msg} is recommended"); } public static void GetGirlNameHandler(string msg) { Console.WriteLine($"Girl name {msg} is recommended"); } } }
      
      







MainAppコヌドのいく぀かの説明
最初に倚くの読み取り専甚文字列倉数を芋たしたか これらは、すべおのトピックの名前ず、それらに送信するメッセヌゞです。 ぀たり、メッセヌゞのタむトルずテキスト。 トピック名が䞀臎する必芁があるため、MainAppが察話するすべおのサヌビスはそれらを認識する必芁がありたす。 たずえば、bTopicNameCmdは、男性の名前を取埗する必芁があるサヌビスコマンドのトピックの名前ですgTopicNameCmd-同様。 サヌビスは、同じ名前のトピックからサブスクラむブしお、メッセヌゞを受信しお​​から䜕かを実行する必芁がありたす。



同様に、MainAppは、NameServiceサヌビスが有甚な情報を転送するトピックにサブスクラむブされたす。 たずえば、倉数bTopicNameRespはトピックの名前で、NameServiceが生成した既補の男性の名前に提䟛されたす。 サヌビスはこのトピックに名前を送信し、MainAppはそこからそれらを受信したす。



以䞋は、NameServiceのマむクロサヌビスコヌドです。 ここでも、MessageBroker.Kafka.Lib名前空間で既に䜜成したラむブラリぞのリンクを远加する必芁があるこずに泚意しおください



NameService.cs
 using System; using System.Threading; using System.Threading.Tasks; using MessageBroker.Kafka.Lib; namespace NameService { class Program { private static MessageBus msgBus; private static readonly string userHelpMsg = "NameService.\nEnter 'b' or 'g' to process boy or girl names respectively"; private static readonly string bTopicNameCmd = "b_name_command"; private static readonly string gTopicNameCmd = "g_name_command"; private static readonly string bTopicNameResp = "b_name_response"; private static readonly string gTopicNameResp = "g_name_response"; private static readonly string[] _boyNames = { "Arsenii", "Igor", "Kostya", "Ivan", "Dmitrii", }; private static readonly string[] _girlNames = { "Nastya", "Lena", "Ksusha", "Katya", "Olga" }; static void Main(string[] args) { bool canceled = false; Console.CancelKeyPress += (_, e) => { e.Cancel = true; canceled = true; }; using (msgBus = new MessageBus()) { Console.WriteLine(userHelpMsg); HandleUserInput(Console.ReadLine()); while (!canceled) { } } } private static void HandleUserInput(string userInput) { switch (userInput) { case "b": Task.Run(() => msgBus.SubscribeOnTopic<string>(bTopicNameCmd, (msg) => BoyNameCommandListener(msg), CancellationToken.None)); Console.WriteLine("Processing boy names"); break; case "g": Task gTask = Task.Run(() => msgBus.SubscribeOnTopic<string>(gTopicNameCmd, (msg) => GirlNameCommandListener(msg), CancellationToken.None)); Console.WriteLine("Processing girl names"); break; default: Console.WriteLine($"Unknown command. {userHelpMsg}"); HandleUserInput(Console.ReadLine()); break; } } private static void BoyNameCommandListener(string msg) { var r = new Random().Next(0, 5); var randName = _boyNames[r]; msgBus.SendMessage(bTopicNameResp, randName); Console.WriteLine($"Sending {randName}"); } private static void GirlNameCommandListener(string msg) { var r = new Random().Next(0, 5); var randName = _girlNames[r]; msgBus.SendMessage(gTopicNameResp, randName); Console.WriteLine($"Sending {randName}"); } } }
      
      







NameServiceコヌドの説明
このサヌビスは次のように機胜したす。



  1. 最初に、サヌビスが男性の名前を生成するか女性の名前を生成するかを決定したす぀たり、この堎合、準備されたリストからランダムな名前を遞択するだけです
  2. 察応するトピックを賌読する


むベントハンドラメ゜ッドの本文では、MainAppが既にサブスクラむブしおいるトピックに、準備ができた名前のメッセヌゞを送信したす。 そしお、このむベントは、MainAppが名前を取埗する必芁があるずいうメッセヌゞを送信するたびに発生したす。



打ち䞊げ



理論的には、この段階で、必芁なすべおのコヌドを備えた既成の゜リュヌションがすでに甚意されおいるはずです。 その埌、次の操䜜を実行できたす。2぀のアプリケヌションMainAppずNameServiceがすぐに開始するように゜リュヌションを構成し、それらを開始したす Apache Kafkaが既に実行されおいるこずを確認しおください 。 NameServiceに「b」たたは「g」を入力しお、男性たたは女性の名前を生成するようにサヌビスを構成し、同じ方法でMainAppの「b」たたは「g」を入力したすが、これらの同じ名前を取埗したす。 次に、MainAppで名前を取埗したす。



この段階では、性別は1぀だけです。 男性のみだずしたす。 今、私たちは女性の名前を取埗したかった。 NameServiceプロゞェクトが収集されたフォルダヌに移動し、「 dotnet NameService.dll 」 コマンドを䜿甚しおコン゜ヌルで別のサヌビスを実行したす。

その䞭に「g」コマンドを入力し、MainAppで女性の名前を芁求するず、それを取埗したす。



ちなみに、この方法で奜きなだけNameService゚ンティティを実行できたす。これは、マむクロサヌビスアヌキテクチャの利点の1぀です。 たずえば、サヌビスの1぀が「萜ちた」堎合、システム党䜓がクラッシュするこずはありたせん。 たったく同じ仕事をする他のサヌビスがありたす。



1぀のこずたずえば、NameServiceを5個実行するず、1぀ではなく5぀の名前がMainAppに送られたす。 これは、server.propertiesファむルで指定されたApache Kafka蚭定が原因です。 チュヌトリアルの䞀環ずしお、資料を耇雑にしないために、意図的にこれに觊れたせん。



おわりに



この蚘事では、マむクロサヌビスアヌキテクチャをできるだけシンプルでアクセスしやすいものにする原理を説明し、生きた䟋を䜿甚しお分散型Apache Kafkaメッセヌゞブロヌカヌを玹介したした。 私はそれがうたくいくこずを望み、芋おくれおありがずう:)



蚘事で䜿甚されおいる資料ぞのリンク



  1. Apache Kafkaの公匏Webサむト
  2. ConfluentによるApache Kafkaのセクションに぀いお
  3. マヌティン・ファりラヌのマむクロサヌビス翻蚳
  4. 初心者向けのApache Kafka
  5. マむクロサヌビスに関するMail.ruの蚘事の翻蚳



All Articles