// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApplicationLifetime lifetime) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); } app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapBlazorHub(); endpoints.MapFallbackToPage("/_Host"); }); lifetime.ApplicationStarted.Register(() => { var actorName = "countActor"; AkkaLoad.RegisterActor(actorName, actorSystem.ActorOf(Props.Create(() => new CountActor()), actorName)); }); }
public ClusterWorkActor() { id = Guid.NewGuid().ToString(); //logger.Info($"Create ClusterMsgActor:{id}"); msgCnt = 0; random = new Random(); StatisticsActor = AkkaLoad.ActorSelect("SingleToneActor"); ReceiveAsync <DelayMsg>(async msg => { Context.IncrementMessagesReceived(); if (msgCnt < 10) { logger.Debug($"### Message ClusterMsgActor {msgCnt}"); } msgCnt++; //랜덤 Delay를 줌( 외부 요소 : API OR DB ) int auto_delay = msg.Delay == 0 ? random.Next(1, 100) : msg.Delay; await Task.Delay(auto_delay); Context.IncrementCounter("akka.custom.received1"); msg.State = DelayMsgState.Completed; StatisticsActor.Tell(msg); if ((msgCnt % 100) == 0) { logger.Info($"Msg:{msg} Count:{msgCnt} Delay:{auto_delay}"); } }); }
static public void Start(IApplicationBuilder app, ActorSystem actorSystem) { // http://wiki.webnori.com/display/AKKA/Actors // HelloActor 기본액터 AkkaLoad.RegisterActor("helloActor" /*AkkaLoad가 인식하는 유니크명*/, actorSystem.ActorOf(Props.Create(() => new HelloActor("webnori")), "helloActor" /*AKKA가 인식하는 Path명*/)); var helloActor = actorSystem.ActorSelection("user/helloActor"); var helloActor2 = AkkaLoad.ActorSelect("helloActor"); helloActor.Tell("hello"); helloActor2.Tell("hello"); // 밸브 Work : 초당 작업량을 조절 int timeSec = 1; int elemntPerSec = 5; var throttleWork = AkkaLoad.RegisterActor("throttleWork", actorSystem.ActorOf(Props.Create(() => new ThrottleWork(elemntPerSec, timeSec)), "throttleWork")); // 실제 Work : 밸브에 방출되는 Task를 개별로 처리 var worker = AkkaLoad.RegisterActor("worker", actorSystem.ActorOf(Props.Create <WorkActor>(), "worker")); // 밸브 작업자를 지정 throttleWork.Tell(new SetTarget(worker)); //StartLoadTestByRouter(app,actorSystem); //StartLoadTestByPingPong(app,actorSystem); //StartKafkaTest(app, actorSystem); }
public ActorTestController() { basicActor = AkkaLoad.ActorSelect("basic"); printerActor = AkkaLoad.ActorSelect("printer"); highPassActor = AkkaLoad.ActorSelect("highpass"); cashPassActor = AkkaLoad.ActorSelect("cashpass"); clusterRoundbin1 = AkkaLoad.ActorSelect("clusterRoundRobin"); singleToneActor = AkkaLoad.ActorSelect("SingleToneActor"); throttleActor = AkkaLoad.ActorSelect("throttleActor"); }
// This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); var akkaConfig = AkkaLoad.Load(envName, Configuration); actorSystem = ActorSystem.Create("AkkaDotBootSystem", akkaConfig); services.AddAkka(actorSystem); services.AddRazorPages(); services.AddServerSideBlazor(); services.AddSingleton <WeatherForecastService>(); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { //services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddControllers(); services.AddSingleton(Configuration.GetSection("AppSettings").Get <AppSettings>());// * AppSettings // 액터에 DI적용시 사용 services.AddSingleton <KafkaService>(); services.AddScoped <TonerActor>(); services.AddScoped <PrinterActor>(); // Swagger services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = AppName, Description = $"{AppName} ASP.NET Core Web API", TermsOfService = new Uri("http://wiki.webnori.com/display/codesniper/TermsOfService"), Contact = new OpenApiContact { Name = Company, Email = Email, Url = new Uri(CompanyUrl), }, License = new OpenApiLicense { Name = $"Document", Url = new Uri(DocUrl), } }); // Set the comments path for the Swagger JSON and UI. var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); options.IncludeXmlComments(xmlPath); }); // *** Akka Service Setting var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); var akkaConfig = AkkaLoad.Load(envName, Configuration); var actorSystem = ActorSystem.Create(SystemNameForCluster, akkaConfig); var provider = services.BuildServiceProvider(); actorSystem.UseServiceProvider(provider); services.AddAkka(actorSystem); // API주소룰 소문자로... //services.AddRouting(options => options.LowercaseUrls = true); }
static public void StartLoadTestByRouter(IApplicationBuilder app, ActorSystem actorSystem) { //################################################################## //##### TPS 측정편 - 액터성능 //##### 라우터 //##### //################################################################## int testHitCount = 400000; int distributedCnt = 50; //튜닝요소 //custom-dispatcher , custom-task-dispatcher , default-fork-join-dispatcher string disPacther = "custom-task-dispatcher"; var roundPool = AkkaLoad.RegisterActor("roundPool", actorSystem.ActorOf(Props.Create(() => new InfiniteReflectionActor()).WithDispatcher(disPacther) .WithRouter(new RoundRobinPool(distributedCnt)), "roundPool")); // Wait for all tasks to complete. Task[] tasks = new Task[distributedCnt]; for (int i = 0; i < distributedCnt; i++) { tasks[i] = Task.Run(() => { for (int hitidx = 0; hitidx < testHitCount; hitidx++) { roundPool.Tell(new DistributedMessage() { Message = "test" }); } }); } try { //Task.WaitAll(tasks); Console.WriteLine("Completed,Tell"); } catch (AggregateException ae) { Console.WriteLine("One or more exceptions occurred: "); foreach (var ex in ae.Flatten().InnerExceptions) { Console.WriteLine(" {0}", ex.Message); } } }
static public void StartLoadTestByPingPong(IApplicationBuilder app, ActorSystem actorSystem) { //################################################################## //##### TPS 측정편 - 액터성능 //##### 핑퐁 //##### //################################################################## //튜닝요소 //custom-dispatcher , custom-task-dispatcher , default-fork-join-dispatcher string disPacther = "custom-task-dispatcher"; int pipongGroupCount = 1; // 핑퐁그룹,탁구대를 늘릴수있다. ( 2인1조) int ballCount = 6; // 핑퐁에 사용된 공개수 // 무한전송 셋트... for (int i = 0; i < pipongGroupCount; i++) { string actorFirstName = "infiniteReflectionActorA" + i; string actorSecondName = "infiniteReflectionActorB" + i; // 무한전송 Test Actor생성 var infiniteReflectionActorA = AkkaLoad.RegisterActor(actorFirstName, actorSystem.ActorOf(Props.Create(() => new InfiniteReflectionActor()).WithDispatcher(disPacther), actorFirstName)); var infiniteReflectionActorB = AkkaLoad.RegisterActor(actorSecondName, actorSystem.ActorOf(Props.Create(() => new InfiniteReflectionActor()).WithDispatcher(disPacther), actorSecondName)); //무한전송을 위한,응답대상을 크로스로 연결및 무한메시지 시작 infiniteReflectionActorA.Tell(infiniteReflectionActorB); infiniteReflectionActorB.Tell(infiniteReflectionActorA); for (int ballIdx = 0; ballIdx < ballCount; ballIdx++) { infiniteReflectionActorA.Tell(new InfiniteMessage() { Message = "서브A", Count = 0 }); } } }
public CashGateActor() { rnd = new Random(); id = Guid.NewGuid().ToString(); msgCnt = 0; logger.Info($"Create CashGateActor:{id}"); CountConsume = AkkaLoad.ActorSelect("SingleToneActor"); ReceiveAsync <DelayMsg>(async msg => { msgCnt++; Context.IncrementMessagesReceived(); Context.IncrementCounter("akka.custom.received1"); logger.Info($"{msg.Message}--{msgCnt}"); if (null != CountConsume) { CountConsume.Tell(msg); } //정산 소요 시간 int auto_delay = msg.Delay == 0 ? rnd.Next(300, 1000) : msg.Delay; await Task.Delay(auto_delay); //수신자가 있으면 보낸다. if (!Sender.IsNobody()) { if (msg.Message == "정산해주세요") { Sender.Tell($"정산완료 통과하세요"); } } }); ReceiveAsync <StopActor>(async msg => { Sender.Tell("Done"); Context.Stop(Self); }); }
public HighPassGateActor() { id = Guid.NewGuid().ToString(); logger.Info($"Create HigPassGateActor:{id}"); msgCnt = 0; random = new Random(); MatrixSingleActor = AkkaLoad.ActorSelect("SingleToneActor"); ReceiveAsync <DelayMsg>(async msg => { if (MonitorMode) { Context.IncrementMessagesReceived(); } msgCnt++; int auto_delay = msg.Delay == 0 ? random.Next(1, 100) : msg.Delay; await Task.Delay(auto_delay); var completeMsg = new DelayMsg() { State = DelayMsgState.Completed, Message = msg.Message, Seq = msg.Seq }; MatrixSingleActor.Tell(completeMsg); if (MonitorMode) { Context.IncrementCounter("akka.custom.received1"); } logger.Info($"Msg:{msg.Message} Count:{msgCnt} Delay:{auto_delay}"); }); }
public ActorTestController(ILogger <ActorTestController> logger, ActorSystem actorSystem) { _logger = logger; throttleWork = AkkaLoad.ActorSelect("throttleWork"); _actorSystem = actorSystem; }
public LoadTestController() { TPSCommandActor = AkkaLoad.ActorSelect("TPSCommandActor"); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSingleton(Configuration.GetSection("AppSettings").Get <AppSettings>());// * AppSettings services.AddSingleton <ConsumerSystem>(); services.AddSingleton <ProducerSystem>(); services.AddDbContext <UserRepository>(); // Akka 설정 // 참고 : https://getakka.net/articles/concepts/configuration.html // AKKASYSTEM : http://wiki.webnori.com/display/AKKA/AKKASYSTEM // Akka 셋팅 var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); var akkaConfig = AkkaLoad.Load(envName, Configuration); actorSystem = ActorSystem.Create("AkkaDotBootSystem", akkaConfig); services.AddAkka(actorSystem); //모니터링추가 var statsdConfig = new StatsdConfig { StatsdServerName = "127.0.0.1" }; //ActorMonitoringExtension.RegisterMonitor(actorSystem, new ActorDatadogMonitor(statsdConfig)); // Signal R 셋팅 services .AddSingleton(new ConnectionSourceSettings(102400, OverflowStrategy.DropBuffer)) .AddSignalRAkkaStream() .AddSignalR(); // Swagger services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = AppName, Description = $"{AppName} ASP.NET Core Web API", TermsOfService = new Uri(CompanyUrl), Contact = new OpenApiContact { Name = Company, Email = "*****@*****.**", Url = new Uri(CompanyUrl), }, License = new OpenApiLicense { Name = $"Document", Url = new Uri(DocUrl), } }); c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme { Name = "Authorization", Type = SecuritySchemeType.ApiKey, Scheme = "Bearer", BearerFormat = "JWT", In = ParameterLocation.Header, Description = "JWT Authorization header using the Bearer scheme." }); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } }, new string[] {} } }); // Set the comments path for the Swagger JSON and UI. var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile); c.IncludeXmlComments(xmlPath); }); }
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); }
public KafkaController(ProducerSystem _producerSystem) { producerSystem = _producerSystem; producerActor = AkkaLoad.ActorSelect("producerActor"); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApplicationLifetime lifetime) { app.UseSwagger(); // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), // specifying the Swagger JSON endpoint. app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); }); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); MetricServer metricServer = null; var appConfig = app.ApplicationServices.GetService <AppSettings>(); AppSettings = appConfig; //APP Life Cycle lifetime.ApplicationStarted.Register(() => { app.ApplicationServices.GetService <ILogger>(); var actorSystem = app.ApplicationServices.GetService <ActorSystem>(); // start Akka.NET ActorSystem = actorSystem; //싱글톤 클러스터 액터 actorSystem.BootstrapSingleton <SingleToneActor>("SingleToneActor", "akkanet"); var singleToneActor = actorSystem.BootstrapSingletonProxy("SingleToneActor", "akkanet", "/user/SingleToneActor", "singleToneActorProxy"); AkkaLoad.RegisterActor("SingleToneActor", singleToneActor); // ########### 로드 테스트 전용 // 클러스터내 싱글톤은 하나만 존재할수있음 : akka.cluster.singleton - akka.conf참고 //액터 선택 : AkkaLoad.ActorSelect("ClusterWorkerPoolActor") var worker = AkkaLoad.RegisterActor( "ClusterWorkerPoolActor", actorSystem.ActorOf(Props.Create <ClusterWorkerPoolActor>() .WithDispatcher("fast-dispatcher") .WithRouter(FromConfig.Instance), "cluster-workerpool" )); //커멘드 액터( 로드테스트용) //액터생성 AkkaLoad.RegisterActor( "TPSCommandActor", actorSystem.ActorOf(Props.Create <TPSCommandActor>(worker), "TPSCommandActor" )); // ########### 로드 테스트 전용 //액터생성 AkkaLoad.RegisterActor( "basic", actorSystem.ActorOf(Props.Create <BasicActor>(), "basic" )); // DI 연동 AkkaLoad.RegisterActor( "toner", actorSystem.ActorOf(actorSystem.DI().Props <TonerActor>() .WithRouter(new RoundRobinPool(1)), "toner" )); AkkaLoad.RegisterActor( "printer", actorSystem.ActorOf(actorSystem.DI().Props <PrinterActor>() .WithDispatcher("custom-dispatcher") .WithRouter(FromConfig.Instance).WithDispatcher("custom-task-dispatcher"), "printer-pool" )); // DI 미연동 AkkaLoad.RegisterActor( "highpass", actorSystem.ActorOf(Props.Create <HighPassGateActor>() .WithDispatcher("fast-dispatcher") .WithRouter(FromConfig.Instance), "highpass-gate-pool" )); AkkaLoad.RegisterActor( "cashpass", actorSystem.ActorOf(Props.Create <CashGateActor>() .WithDispatcher("slow-dispatcher") .WithRouter(FromConfig.Instance), "cashpass-gate-pool" )); AkkaLoad.RegisterActor( "clusterRoundRobin", actorSystem.ActorOf(Props.Create <ClusterPoolActor>() .WithDispatcher("fast-dispatcher") .WithRouter(FromConfig.Instance), "cluster-roundrobin" )); // 스트림 - 밸브조절액터 int timeSec = 1; var throttleActor = AkkaLoad.RegisterActor( "throttleActor", actorSystem.ActorOf(Props.Create <ThrottleActor>(timeSec) )); var throttleWork = actorSystem.ActorOf(Props.Create <ThrottleWork>(5, 1)); throttleActor.Tell(new SetTarget(throttleWork)); try { var MonitorTool = Environment.GetEnvironmentVariable("MonitorTool"); var MonitorToolCon = Environment.GetEnvironmentVariable("MonitorToolCon"); var MonitorToolCons = MonitorToolCon.Split(":"); string StatsdServerName = MonitorToolCons[0]; int StatsdPort = 8125; if (MonitorToolCons.Length > 1 && 10 > MonitorToolCons.Length) { StatsdPort = int.Parse(MonitorToolCons[1]); } switch (MonitorTool) { case "win": var win = ActorMonitoringExtension.RegisterMonitor(actorSystem, new ActorPerformanceCountersMonitor( new CustomMetrics { Counters = { "akka.custom.metric1", "akka.custom.metric2", "akka.custom.metric3", "akka.custom.received1", "akka.custom.received2" }, Gauges = { "akka.gauge.msg10", "akka.gauge.msg100", "akka.gauge.msg1000", "akka.gauge.msg10000" }, Timers = { "akka.handlertime" } })); // 윈도우 성능 모니터링 수집대상항목은 최초 Admin권한으로 akka항목으로 레지스트리에 프로그램실행시 자동 등록되며 // 커스텀항목 결정및 최초 1번 작동후 변경되지 않음으로 // 수집 항목 변경시 아래 Register 삭제후 다시 최초 Admin권한으로 작동 // Actor명으로 매트릭스가 분류됨으로 기능단위의 네이밍이 권장됨 // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Akka\Performance break; case "azure": var azure = ActorMonitoringExtension.RegisterMonitor(actorSystem, new ActorAppInsightsMonitor(appConfig.MonitorToolCon)); break; case "prometheus": // prometheusMonotor 를 사용하기위해서, MerticServer를 켠다...(수집형 모니터) // http://localhost:10250/metrics metricServer = new MetricServer(10250); metricServer.Start(); var prometheus = ActorMonitoringExtension.RegisterMonitor(actorSystem, new ActorPrometheusMonitor(actorSystem)); break; case "datadog": var statsdConfig = new StatsdConfig { StatsdServerName = StatsdServerName, StatsdPort = StatsdPort }; var dataDog = ActorMonitoringExtension. RegisterMonitor(actorSystem, new ActorDatadogMonitor(statsdConfig)); break; } } catch (Exception e) { Console.WriteLine("=============== Not Suport Window Monitor Tools ==============="); } ActorMonitoringExtension.Monitors(actorSystem).IncrementDebugsLogged(); }); lifetime.ApplicationStopping.Register(() => { Console.WriteLine("=============== Start Graceful Down ==============="); var actorSystem = app.ApplicationServices.GetService <ActorSystem>(); if (appConfig.MonitorTool == "prometheus") { metricServer.Stop(); } // Graceful Down Test,Using CashGateActor Actor AkkaLoad.ActorSelect("cashpass").Ask(new StopActor()).Wait(); var cluster = Akka.Cluster.Cluster.Get(actorSystem); cluster.RegisterOnMemberRemoved(() => MemberRemoved(actorSystem)); cluster.Leave(cluster.SelfAddress); asTerminatedEvent.WaitOne(); Console.WriteLine($"=============== Completed Graceful Down : {cluster.SelfAddress} ==============="); }); }