Page History
Info |
---|
클러스터를 이해하기위해 실제 실습 코드를 통해 클러스터를 작동시켜보겠습니다. 클러스터 라우터는 이전에 설명한 Actor-라우터와 동일함으로 라우터별로 별도의 부가 설명을 생략하겠습니다. |
클러스터 설계
다음과 같은 클러스터를 설계한다고 합시다.
- 클러스터내 라우터는 라운드 로빈으로 순차적으로 수행됩니다.
- 클러스터는 설정정변경없이 스케일업/스케일아웃이 가능합니다.
- 노드가 늘어나고/줄어남에 따라 순차적실행 즉 라운드 로빈 기능 수행도 동적으로 확장됩니다.
클러스터 액터설계
기존에 설계된 액터와 약간다른점은, 클러스터에 필요한 잡담(클러스터 멤버의 변경사항을 스스로 감지하는)
기능을 추가 할것입니다. 잡담기능을 추가한것외에는 기존의 액터메시지 처리와 동일합니다.
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
public class ClusterActor : UntypedActor
{
protected Akka.Cluster.Cluster Cluster = Akka.Cluster.Cluster.Get(Context.System);
protected ILoggingAdapter Log = Context.GetLogger();
protected override void PreStart()
{
// subscribe to IMemberEvent and UnreachableMember events
Cluster.Subscribe(Self, ClusterEvent.InitialStateAsEvents,
new[] { typeof(ClusterEvent.IMemberEvent), typeof(ClusterEvent.UnreachableMember) });
}
/// <summary>
/// Re-subscribe on restart
/// </summary>
protected override void PostStop()
{
//정상적으로 Actor를 정지하면 멤버에서 탈퇴가됩니다.
Cluster.Unsubscribe(Self);
}
protected void OnReceiveData(object message)
{
Log.Info("Data:" + message.ToString());
}
protected override void OnReceive(object message)
{
var up = message as ClusterEvent.MemberUp;
if (up != null)
{
var mem = up;
Log.Info("Member is Up: {0}", mem.Member);
}
else if (message is ClusterEvent.UnreachableMember)
{
var unreachable = (ClusterEvent.UnreachableMember)message;
Log.Info("Member detected as unreachable: {0}", unreachable.Member);
//멤버와 갑자기 연락 두절이 되었기때문에, 멈버를 제거합니다.
Cluster.Down(unreachable.Member.Address);
}
else if (message is ClusterEvent.MemberRemoved)
{
var removed = (ClusterEvent.MemberRemoved)message;
Log.Info("Member is Removed: {0}", removed.Member);
}
else if (message is ClusterEvent.IMemberEvent)
{
//IGNORE
}
else if (message is Hello)
{
OnReceiveData(message); //실제 처리할 DataProcess
}
else
{
Unhandled(message);
}
}
} |
클러스터 시드 노드 어플리케이션
시드노드는 앞서 설명했듯이, 마스터 노드에 해당하는것으로 1개가 필요합니다. 다중기기에 서비스를 뛰울시 설정사항이나
구동방식의 차이가 없지만, 학습용 테스트이기때문에 시드노드는 9999 포트로 고정을하고 시드노드에 붙게되는 일반노드는
0포트(동적) 로 스케일아웃 테스트를 하겠습니다. 일반적으로 노드 확장은 동일 포트로 확장을 하는것이니 이 차이점을 인지하고
코드 파악을 하시면 되겠습니다.
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
public void ClusterUpSeedNode()
{
var baseConfig = ConfigurationFactory.ParseString(@"akka {
actor{
provider = ""Akka.Cluster.ClusterActorRefProvider, Akka.Cluster""
deployment {
/myClusterPoolRouter {
routees.paths = [""/user/myClusterPoolRouter""]
router = round-robin-pool # routing strategy
nr-of-instances = 10 # max number of total routees
cluster {
enabled = on
allow-local-routees = on
use-role = crawler
max-nr-of-instances-per-node = 1
}
}
}
}
remote {
helios.tcp {
port = 9999 #bound to a specific port
hostname = 127.0.0.1
}
}
cluster {
seed-nodes = [""akka.tcp://ClusterSystem@127.0.0.1:9999""] # address of seed node
roles = [""crawler""] # roles this member is in
#role.[""crawler""].min-nr-of-members = 3 # crawler role minimum node count
}
}");
actorSystem = ActorSystem.Create("ClusterSystem", baseConfig);
//var clusterActor2 = actorSystem.ActorOf<EchoActor2>("myClusterPoolRouter");
var router = actorSystem.ActorOf(Props.Create<ClusterActor>().WithRouter(FromConfig.Instance), "myClusterPoolRouter");
Task.Delay(2000).Wait();
var msg = new Hello("hi");
router.Tell(msg);
router.Tell(msg);
router.Tell(msg);
} |
클러스터 일반노드 어플리케이션
Code Block | ||||||
---|---|---|---|---|---|---|
| ||||||
public void ClusterUpNode()
{
var baseConfig = ConfigurationFactory.ParseString(@"akka {
actor{
provider = ""Akka.Cluster.ClusterActorRefProvider, Akka.Cluster""
deployment {
/myClusterPoolRouter {
routees.paths = [""/user/myClusterPoolRouter""]
router = round-robin-pool # routing strategy
nr-of-instances = 10 # max number of total routees
cluster {
enabled = on
allow-local-routees = on
use-role = crawler
max-nr-of-instances-per-node = 1
}
}
}
}
remote {
helios.tcp {
port = 0 #bound to a specific port
hostname = 127.0.0.1
}
}
cluster {
seed-nodes = [""akka.tcp://ClusterSystem@127.0.0.1:9999""] # address of seed node
roles = [""crawler""] # roles this member is in
#role.[""crawler""].min-nr-of-members = 3 # crawler role minimum node count
}
}");
actorSystem = ActorSystem.Create("ClusterSystem", baseConfig);
//var clusterActor2 = actorSystem.ActorOf<EchoActor2>("myClusterPoolRouter");
var router = actorSystem.ActorOf(Props.Create<ClusterActor>().WithRouter(FromConfig.Instance), "myClusterPoolRouter");
Task.Delay(2000).Wait();
var msg = new Hello("hi");
router.Tell(msg);
router.Tell(msg);
router.Tell(msg);
} |
테스트 케이스
우리는 위 어플리케이션을 다음과 같은 테스트를 할것입니다.
- 각 어플리케이션은 Up이되고 나면, 라운드로빈 메시지를 3개보냅니다.( 멤버에게 분배)
- 시드 노드를 뛰우고 메시지 3개를 보냅니다. ( 일반노드가 없기때문에 시드노드가 3개다처리)
- 일반 노드1개를 뛰우고 메시지 3개를 보냅니다. ( 시드노드 + 일반노드 두 노드분산처리)
- 일반 노드1개를 더 뛰우고 메시지 3개를 보냅니다. ( 시드+ 일반노드2 노드분산처리)
- 일반 노드1개를 내리고 메시지 3개를 보냅니다. ( 시드 노드 + 일반노드 분산처리)
일반 노드를 늘리고/줄이고 함에 따라 자동으로 분산처리가 축소되고 확장되는것을 확인할수가 있습니다.
Panel | ||
---|---|---|
| ||