Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Info

분산 어플리케이션 내에서 고성능 메시지전송을 위해 이러한 라우팅 전략이 필요하며

어플리케이션에 내재하여 손쉽게

액터는 양방향 메시지를 주고받을수 있는 객체이며 하나의 장비에 수십만객체 생성이가능합니다.

네트워크에 있어서도 체계적이고 효과적인 패킷전송을위해 라우팅설계가 중요하며

액터를 사용하는 분산 어플리케이션에서도 이러한 전략이 필요하며 단순하게 사용이 가능합니다.


라우팅전략

Expand
titleConcept > 라우팅전략

Include Page
Routing strategy
Routing strategy라우팅 전략라우팅 전략



심플 라우팅 워커 설계

draw.io Diagram
bordertrue
viewerToolbartrue
fitWindowfalse
diagramNamewokersactor
simpleViewerfalse
width
diagramWidth613
revision2

Wokers구현

라우팅 전략은 다양한 유입처리를 어떻게 분배하여 성능적 또는 안정적으로 유실없이 모두 처리할것인가?

에 대한 해결책으로 ,  AKKA에서는 서비스코드 내에 직접 설계가 가능하도록 몇가지 툴을 제공해줍니다.

물론 이러한 목표를 달성하기위해 KAFKA와 같은 외부 MQ를 잘 사용하는것도 방법중에 하나일것입니다.

라우팅은 주로 분배및 작동방식을 의미를 하고, 라우티는 실제 Task를 처리할수있는 종단점에 위치한 액터입니다.

라우팅 방법 설정

Code Block
languagebash
themeEmacs
titleapplication.conf
akka { 
 actor.deployment {
 /workers/router1 {
 router = 
Expand
title라운드로빈설정
akka {
actor.deployment {
/workers/router1 {
router =
round-robin-pool


 nr-of-instances = 5

}
}
}

 }
 } 
}

random-pool,balancing-pool 등 원하는 분배방식을 설정을 통해 변경 할수가 있으며

라우팅기능이 추상화되어 사용가능함으로, 코드내에서 간단하게 활용가능합니다.

일반적으로 스레드풀관리를 직접 구현하여 라우팅기능을 구현하는것보다는 훨씬 쉬우며

외부 MQ시스템을 활용하는것보다는 번거러울수 있으나, 이러한 기능들을 직접 설계에반영하여

쉽게 내재화할수 있다란것은 장점중하나이며, 직접구현을 시도할시

클러스터를 포함하여 라우팅기능에대한 이해가 높아지기때문에 외부시스템과 연동하거나 이용할때도 도움이됩니다. 



라우팅기능을 포함한, 작업을 분배시키는 Wokers의 구현과 사용이 얼마나 심플해지는지 살펴보겠습니다.


Works구현

Code Block
languagejava
themeEmacs
@Component
@Scope("prototype")
public class Workers extends AbstractActor {
    private final LoggingAdapter log = Logging
            .getLogger(getContext().system(), "Workers");
    
    @Autowired
    private ApplicationContext context;    
    
    private ActorRef roundrobinpool = null;
        
    @Override public void preStart() {
		log.info("Workers::preStart");
		SpringExtension ext = context.getBean(SpringExtension.class);		


        //application.conf에 설정된 라우터방식으로 작동방식결정됨
		roundrobinpool = getContext().actorOf( FromConfig.getInstance().props( ext.props("testActor") ) , "router1"); //액터설정에 의해 5개의 라우티가 생성됨
	}
	
	@Override
	public Receive createReceive() {
		return receiveBuilder()
		.matchEquals("hi1",s->{  //hi1 메시지를 받으면 라운드로빈처리를함
			log.info("Workers::Received String message: {}", s);
			roundrobinpool.tell(s, getSender() );	// 입력된 잡(hi1)을 라운드 로빈라우터에게 메시지전송				
		})
		.matchAny(o -> log.info("received unknown message"))
		.build();
	}
}

...

Code Block
languagejava
themeEmacs
	protected void routerTest(ActorSystem system,SpringExtension ext) {
	    new TestKit(system) {{
	    	final ActorRef workers = system.actorOf( ext.props("workers"),"workers");  // workers 액터생성	    		    
	    	workers.tell( "hi1", getRef() );
		      // await the correct response
		    expectMsg(java.time.Duration.ofSeconds(1), "hi too"); //라우티가 응답한 메시지를 검사
		    
	    	workers.tell( "hi1", getRef() );
		      // await the correct response
		    expectMsg(java.time.Duration.ofSeconds(1), "hi too");


		    workers.tell( "hi1", getRef() );
		    workers.tell( "hi1", getRef() );
		    workers.tell( "hi1", getRef() );
		    workers.tell( "hi1", getRef() );
		    workers.tell( "hi1", getRef() );
	    }};
	}



Expand
title수행결과

[INFO] [05/1718/2018 2013:1426:3218.578720] [AkkaTestApp-akka.actor.default-dispatcher-4] [Workers] Workers::Received String message: hi1

[INFO] [05/18/2018 13::preStart26:18.726] [AkkaTestApp-akka.actor.default-dispatcher-2] [TestActor] akka://AkkaTestApp/user/workers/router1/$a::Incommessage hi1
[INFO] [05/18/2018 13:26:18.732] [AkkaTestApp-akka.actor.default-dispatcher-2] [Workers] Workers::Received String message: hi1
[INFO] [05/18/2018 13:26:18.733] [AkkaTestApp-akka.actor.default-dispatcher-5] [TestActor] akka://AkkaTestApp/user/workers/router1/$b::Incommessage hi1
[INFO] [05/18/2018 13:26:18.733] [AkkaTestApp-akka.actor.default-dispatcher-5] [Workers] Workers::Received String message: hi1
[INFO] [05/18/2018 13:26:18.733] [AkkaTestApp-akka.actor.default-dispatcher-5] [Workers] Workers::Received String message: hi1
[INFO] [05/18/2018 13:26:18.733] [AkkaTestApp-akka.actor.default-dispatcher-5] [Workers] Workers::Received String message: hi1
[INFO] [05/18/2018 13:26:18.733] [AkkaTestApp-akka.actor.default-dispatcher-3] [TestActor] akka://AkkaTestApp/user/workers/router1/$c::Incommessage hi1
[INFO] [05/18/2018 13:26:18.733] [AkkaTestApp-akka.actor.default-dispatcher-5] [Workers] Workers::Received String message: hi1
[INFO] [05/18/2018 13:26:18.733] [AkkaTestApp-akka.actor.default-dispatcher-3] [TestActor] akka://AkkaTestApp/user/workers/router1/$d::Incommessage hi1
[INFO] [05/1718/2018 2013:1426:3218.590733] [AkkaTestApp-akka.actor.default-dispatcher-45] [Workers] Workers::Received String message: hihi1
[INFO] [05/18/2018 13:26:18.733] [AkkaTestApp-akka.actor.default-dispatcher-4] [TestActor] akka://AkkaTestApp/user/workers/router1/$e::Incommessage hi1
[INFO] [05/18/2018 13:26:18.733] [AkkaTestApp-akka.actor.default-dispatcher-9] [TestActor] akka://AkkaTestApp/user/workers/router1/$b::Incommessage hi1
[INFO] [05/1718/2018 2013:1426:3218.596734] [AkkaTestApp-akka.actor.default-dispatcher-3] [TestActor] akka://AkkaTestApp/user/workers/router1/$a::Incommessage hi1


라우팅 기능 추가및 변경

Info

위 작성된 코드에서, 랜던 라우트를 추가해보고 작동방식을 확인해보겠습니다.

얼마나 최소의 코드로 기존 가진기능을 유지하면서 기능추가가되는지 확인해보겠습니다.

Code Block
languagejava
themeEmacs
//router2 설정
		/workers/router2 {
			router = random-pool
			nr-of-instances = 5
		}
// 랜덤풀 생성
randomPool = getContext().actorOf( FromConfig.getInstance().props( ext.props("testActor") ) , "router2");


//hi1을 받으면 라운드로빈, hi2를 받으면 랜덤풀로 작동된다.
		.matchEquals("hi1",s->{
			log.info("Workers::Received String message: {}", s);
			roundrobinpool.tell(s, getSender() );					
		})
		.matchEquals("hi2",s->{
			log.info("Workers::Received String message: {}", s);
			randomPool.tell(s, getSender() );					
		})

Expand
title작동결과

[INFO] [05/18/2018 13:50:08.645] [AkkaTestApp-akka.actor.default-dispatcher-6] [TestActor] akka://AkkaTestApp/user/workers/router2/$e::Incommessage hi2
[INFO] [05/18/2018 13:50:08.645] [AkkaTestApp-akka.actor.default-dispatcher-4] [Workers] Workers::Received String message: hi2
[INFO] [05/18/2018 13:50:08.645] [AkkaTestApp-akka.actor.default-dispatcher-4] [Workers] Workers::Received String message: hi2
[INFO] [05/18/2018 13:50:08.645] [AkkaTestApp-akka.actor.default-dispatcher-12] [TestActor] akka://AkkaTestApp/user/workers/router2/$a::Incommessage::Incommessage hi2
[INFO] [05/18/2018 13:50:08.645] [AkkaTestApp-akka.actor.default-dispatcher-6] [TestActor] akka://AkkaTestApp/user/workers/router2/$e::Incommessage hi2
[INFO] [05/18/2018 13:50:08.645] [AkkaTestApp-akka.actor.default-dispatcher-4] [Workers] Workers::Received String message: hi2
[INFO] [05/18/2018 13:50:08.645] [AkkaTestApp-akka.actor.default-dispatcher-8] [TestActor] akka://AkkaTestApp/user/workers/router2/$d::Incommessage hi2 hi
[INFO] [05/1718/2018 2013:1450:3208.603645] [AkkaTestApp-akka.actor.default-dispatcher-34] [Workers] Workers::Received String message: hihi2
[INFO] [05/1718/2018 2013:1450:3208.603645] [AkkaTestApp-akka.actor.default-dispatcher-6] [TestActor] akka://AkkaTestApp/user/workers/router1router2/$b::Incommessage hi hi2
[INFO] [05/18/2018 13:50:08.645] [AkkaTestApp-akka.actor.default-dispatcher-4] [Workers] Workers::Received String message: hi2
[INFO] [05/18/2018 13:50:08.645] [AkkaTestApp-akka.actor.default-dispatcher-4] [Workers] Workers::Received String message: hi2
[INFO] [05/18/2018 13:50:08.645] [AkkaTestApp-akka.actor.default-dispatcher-6] [TestActor] akka://AkkaTestApp/user/workers/router2/$c::Incommessage hi2


고급적 라우팅기능 부여하기

Info

우리가 지정한 옵션에따라 라우티가 동적으로 확장이되고 축소되는 기능을 부여할수가 있으며

성능문제를 해결하기위한 장치중 하나로 활용할수가 있습니다.


동적 라우티조절

Code Block
languagejava
themeEmacs
akka.actor.deployment {
  /parent/router29 {
    router = round-robin-pool
    resizer {
      lower-bound = 2
      upper-bound = 15
      messages-per-resize = 100
    }
  }
}

위와같이 유입에따른 메시지처리능력에 따라 (보통 사용자가 늘어나는 구간을 램프업이라고 합니다.) 

라우티를 동적으로 늘리고 줄일수있으며, AKKA에서 몇가지 유용한 유틸을 제공합니다. 


커스텀한 라우터 설계

Code Block
languagejava
themeEmacs
public class RedundancyRoutingLogic implements RoutingLogic {
  private final int nbrCopies;
  
  public RedundancyRoutingLogic(int nbrCopies) {
    this.nbrCopies = nbrCopies;  
  }
  RoundRobinRoutingLogic roundRobin = new RoundRobinRoutingLogic();
  
  @Override
  public Routee select(Object message, IndexedSeq<Routee> routees) {
    List<Routee> targets = new ArrayList<Routee>();
    for (int i = 0; i < nbrCopies; i++) {
      targets.add(roundRobin.select(message, routees));
    }
    return new SeveralRoutees(targets);
  }
}

AKKA에서 대부분 우리가 알고있고 쓸만한 라우터를 제공하지만, 제공되지 않는 특수한 라우터 방식을

사용할시 위와같이 라우터 설계가 가능합니다.


Dispatcher설정

Code Block
languagebash
themeEmacs
akka.actor.deployment {
  /poolWithDispatcher {
    router = random-pool
    nr-of-instances = 5
    pool-dispatcher {
      fork-join-executor.parallelism-min = 5
      fork-join-executor.parallelism-max = 5
    }
  }
}

라우터에 병렬처리에 관련된 능력을 지정하고자할때 설정될수 있습니다.

최대 5개의 스레드를 동시에 사용하겠다고 하는 설정사항입니다.

성능을 고려하여 이값을 잘 지정하는것은 생각보다 어려운주제이지만, 설정은 쉽습니다.


참고링크: 여기 샘플코드는,아래문서를 참고하여 Spring Boot에 호환이되도록 재구성되고 테스트 되었습니다.