public void Start(ConsumerAkkaOption consumerActorOption) { IAutoSubscription makeshop_neworder = Subscriptions.Topics(consumerActorOption.Topics); var consumerSettings = ConsumerSettings <Null, string> .Create(consumerSystem, null, null) .WithBootstrapServers(consumerActorOption.BootstrapServers) .WithGroupId(consumerActorOption.KafkaGroupId); var materializer_consumer = consumerSystem.Materializer(); KafkaConsumer.CommittableSource(consumerSettings, makeshop_neworder) .RunForeach(result => { result.CommitableOffset.Commit(); Console.WriteLine($"Consumer: {result.Record.Partition}/{result.Record.Topic} {result.Record.Offset}: {result.Record.Value}"); if (consumerActorOption.RelayActor != null) { consumerActorOption.RelayActor.Tell(result.Record.Value); //ForgetAndFire 발송 } }, materializer_consumer); }
public ConsumerActor(ConsumerAkkaOption consumerAkkaOption) { topic = consumerAkkaOption.Topics; cancellationTokenSource = new CancellationTokenSource(); workActor = consumerAkkaOption.RelayActor; if (consumerAkkaOption.SecurityOption != null) { consumerConfig = new ConsumerConfig() { GroupId = consumerAkkaOption.KafkaGroupId, BootstrapServers = consumerAkkaOption.BootstrapServers, AutoOffsetReset = consumerAkkaOption.AutoOffsetReset, //For Security SecurityProtocol = consumerAkkaOption.SecurityOption.SecurityProtocol, SaslMechanism = consumerAkkaOption.SecurityOption.SaslMechanism, SaslUsername = consumerAkkaOption.SecurityOption.SaslUsername, SaslPassword = consumerAkkaOption.SecurityOption.SaslPassword, SslCaLocation = consumerAkkaOption.SecurityOption.SslCaLocation, }; } else { consumerConfig = new ConsumerConfig() { GroupId = consumerAkkaOption.KafkaGroupId, BootstrapServers = consumerAkkaOption.BootstrapServers, AutoOffsetReset = consumerAkkaOption.AutoOffsetReset, }; } consumer = new ConsumerBuilder <Ignore, string>(consumerConfig).Build(); ReceiveAsync <ConsumerStart>(async msg => { IActorRef selfActor = this.Self; consumer.Subscribe(topic); var cr = consumer.Consume(cancellationTokenSource.Token); selfActor.Tell(new KafkaTextMessage() { Topic = cr.Topic, Message = cr.Message.Value }); }); ReceiveAsync <ConsumerPull>(async msg => { await Task.Run(async() => { var cr = consumer.Consume(cancellationTokenSource.Token); var kafkamsg = new KafkaTextMessage() { Topic = cr.Topic, Message = cr.Message.Value }; return(kafkamsg); }).PipeTo(Self); }); ReceiveAsync <KafkaTextMessage>(async msg => { IActorRef selfActor = this.Self; //이 액터는 카프카 메시지소비만 담당하며 //소비된 메시지는 작업 액터에게 전달한다. string logText = $"Consumed message '{msg.Message}' Topic: '{msg.Topic}'."; logger.Debug(logText); if (workActor != null) { workActor.Tell(msg); } selfActor.Tell(new ConsumerPull()); }); }
static public void StartKafkaTest(IApplicationBuilder app, ActorSystem actorSystem) { // 기호에따라 사용방식이 약간 다른 KAFKA를 선택할수 있습니다. //################################################################## //##### Confluent.Kafka를 Akka액터 모드로 연결한 모드로 //##### 보안연결이 지원하기때문에 Saas형태의 Kafka에 보안연결이 가능합니다. //##### 커스텀한 액터를 생성하여,AkkaStream을 이해하고 직접 연결할수 있을때 유용합니다. //################################################################## //ProducerActor var producerAkkaOption = new ProducerAkkaOption() { BootstrapServers = "webnori.servicebus.windows.net:9093", ProducerName = "webnori-kafka", SecurityOption = new KafkaSecurityOption() { SecurityProtocol = SecurityProtocol.SaslSsl, SaslMechanism = SaslMechanism.Plain, SaslUsername = "******", SaslPassword = "******", SslCaLocation = "./cacert.pem" } }; string producerActorName = "producerActor"; var producerActor = AkkaLoad.RegisterActor(producerActorName /*AkkaLoad가 인식하는 유니크명*/, actorSystem.ActorOf(Props.Create(() => new ProducerActor(producerAkkaOption)), producerActorName /*AKKA가 인식하는 Path명*/ )); producerActor.Tell(new BatchData() { Data = new KafkaTextMessage() { Topic = "akka100", Message = "testData" } }); //ConsumerActor var consumerAkkaOption = new ConsumerAkkaOption() { BootstrapServers = "webnori.servicebus.windows.net:9093", Topics = "akka100", AutoOffsetReset = AutoOffsetReset.Earliest, KafkaGroupId = "akakTestGroup", RelayActor = null, //작업자 액터를 연결하면, 소비메시지가 작업자에게 전달된다 ( 컨슘기능과 작업자 기능의 분리) SecurityOption = new KafkaSecurityOption() { SecurityProtocol = SecurityProtocol.SaslSsl, SaslMechanism = SaslMechanism.Plain, SaslUsername = "******", SaslPassword = "******", SslCaLocation = "./cacert.pem" } }; string consumerActorName = "consumerActor"; var consumerActor = AkkaLoad.RegisterActor(consumerActorName /*AkkaLoad가 인식하는 유니크명*/, actorSystem.ActorOf(Props.Create(() => new ConsumerActor(consumerAkkaOption)), consumerActorName /*AKKA가 인식하는 Path명*/ )); //컨슈머를 작동시킨다. consumerActor.Tell(new ConsumerStart()); //################################################################## //##### Akka.Streams.Kafka(의존:Confluent.Kafka) 을 사용하는 모드로, Security(SSL)이 아직 지원되지 않습니다. //##### Private으로 구성된, Kafka Pass 모드일때 사용가능합니다. //##### AkkaStream.Kafka가 제공하는 스트림을 활용핼때 장점이 있습니다. //################################################################## // KAFKA - // 각 System은 싱글톤이기때문에 DI를 통해 Controller에서 참조획득가능 var consumerSystem = app.ApplicationServices.GetService <ConsumerSystem>(); var producerSystem = app.ApplicationServices.GetService <ProducerSystem>(); //소비자 : 복수개의 소비자 생성가능 consumerSystem.Start(new ConsumerAkkaOption() { KafkaGroupId = "testGroup", BootstrapServers = "kafka:9092", RelayActor = null, //소비되는 메시지가 지정 액터로 전달되기때문에,처리기는 액터로 구현 Topics = "akka100", }); //생산자 : 복수개의 생산자 생성가능 producerSystem.Start(new ProducerAkkaOption() { BootstrapServers = "kafka:9092", ProducerName = "producer1", }); List <string> messages = new List <string>(); for (int i = 0; i < 10; i++) { messages.Add($"message-{i}"); } //보너스 : 생산의 속도를 조절할수 있습니다. int tps = 10; producerSystem.SinkMessage("producer1", "akka100", messages, tps); }