반응형
Akka는 액터에 메시지를 전달하고 응답을 받을 때 자바 인터페이스를 사용할 수 있는 기능을 제공하고 있다. 즉, ActorRef의 sendOneWay()나 sendRequestReply()와 같은 메서드가 아닌 자바 인터페이스에 정의된 메서드를 이용해서 액터에 메시지를 전달하고 응답을 받을 수 있도록 하고 있다. 본 글에서는 Akka가 제공하는 TypedActor를 이용해서 자바 인터페이스를 액터와의 통신 인터페이스로 사용하는 방법을 설명한다.
TypedActor를 이용한 자바 인터페이스 기반 액터 생성
TypedActor 클래스를 사용하면 인터페이스를 구현한 자바 클래스를 액터로 사용할 수 있다. TypedActor를 사용하려면 다음과 같이 인터페이스와 그 인터페이스를 구현한 클래스를 필요로 한다. 이때 인터페이스를 구현한 클래스는 TypedActor 클래스를 상속 받아야 한다.
액터와 통신할 때 사용할 인터페이스를 구현하고 TypedActor를 상속받은 클래스를 구현했다면, 다음의 코드를 이용해서 액터를 생성하고 사용할 수 있다.
TypedActor.newInstance()의 첫 번째 파라미터는 액터와 통신할 때 사용할 인터페이스 타입을 지정하며, 두 번째 파라미터는 실제 TypedActor로 사용될 클래스를 지정한다. TypedActor.newInstance() 메서드가 생성한 객체는 액터와 통신을 수행해주는 프록시 객체가 된다. 위 코드에서는 migrator가 프록시 객체가 되는데, 이 프록시 객체의 메서드를 호출하면, 내부적으로 TypedActor 객체에 메시지를 전송하게 되고 TypedActor 객체는 일치하는 메서드를 호출하게 된다.
[참고]
Fire-And-Forget
메서드의 리턴 타입이 void 이면, 해당 메서드에 대한 메시지는 sendOneWay()와 동일하게 Fire-And-Forget 방식으로 전송된다. 따라서, 메서드를 호출하면 액터가 메시지를 처리 여부에 상관없이 즉시 리턴한다. DataMigrator 인터페이스의 run() 메서드가 이에 해당한다.
Send-And-Receive-Eventually
메서드가 리턴 타입을 가지면, sendRequestReply()와 동일하게 Send-And-Receive-Eventually 방식으로 메시지가 전송된다. 따라서, 액터로부터 응답이 도착할 때 까지 블럭킹 된다.
Send-And-Receive-Future
메서드의 리턴 타입이 akka.dispatch.Future이면, Send-And-Receive-Future 방식으로 메서드를 호출한다.
리모트 TypedActor 생성하기
UntypedActor와 마찬가지로 TypedActor도 간단하게 리모트 액터로 제공할 수 있다.
리모트 서버에서 TypedActor 생성하기
리모트 서버에서, 액터를 리모트 액터로 등록하려면 registerTypedActor()를 사용하면 된다.
클라이언트 코드에서는 Actors.remote().typedActorFor() 메서드를 이용해서 리모트 액터에 대한 프록시 객체를 구한 뒤 알맞은 메서드를 호출하면 된다.
[주의]
참고자료
TypedActor를 이용한 자바 인터페이스 기반 액터 생성
TypedActor 클래스를 사용하면 인터페이스를 구현한 자바 클래스를 액터로 사용할 수 있다. TypedActor를 사용하려면 다음과 같이 인터페이스와 그 인터페이스를 구현한 클래스를 필요로 한다. 이때 인터페이스를 구현한 클래스는 TypedActor 클래스를 상속 받아야 한다.
public interface DataMigrator {
public void run();
public int restCount();
}
public class DataMigratorImpl extends TypedActor implements DataMigrator {
private int count = 0;
@Override
public void run() {
System.out.println("DataMigratorImpl: 작업 시작");
// 뭔가 작업을 비동기로 처리
}
@Override
public int restCount() {
return 100 - count;
}
}
public void run();
public int restCount();
}
public class DataMigratorImpl extends TypedActor implements DataMigrator {
private int count = 0;
@Override
public void run() {
System.out.println("DataMigratorImpl: 작업 시작");
// 뭔가 작업을 비동기로 처리
}
@Override
public int restCount() {
return 100 - count;
}
}
액터와 통신할 때 사용할 인터페이스를 구현하고 TypedActor를 상속받은 클래스를 구현했다면, 다음의 코드를 이용해서 액터를 생성하고 사용할 수 있다.
DataMigrator migrator =
TypedActor.newInstance(DataMigrator.class, DataMigratorImpl.class);
// migrator는 액터와 통신을 위한 프록시
migrator.run();
int rest = migrator.restCount();
do {
Thread.sleep(10);
rest = migrator.restCount();
} while(rest > 0);
TypedActor.stop(migrator); // TypedActor 종료
// Actors.registry().shutdownAll(); 코드도 TypedActor 종료
TypedActor.newInstance(DataMigrator.class, DataMigratorImpl.class);
// migrator는 액터와 통신을 위한 프록시
migrator.run();
int rest = migrator.restCount();
do {
Thread.sleep(10);
rest = migrator.restCount();
} while(rest > 0);
TypedActor.stop(migrator); // TypedActor 종료
// Actors.registry().shutdownAll(); 코드도 TypedActor 종료
TypedActor.newInstance()의 첫 번째 파라미터는 액터와 통신할 때 사용할 인터페이스 타입을 지정하며, 두 번째 파라미터는 실제 TypedActor로 사용될 클래스를 지정한다. TypedActor.newInstance() 메서드가 생성한 객체는 액터와 통신을 수행해주는 프록시 객체가 된다. 위 코드에서는 migrator가 프록시 객체가 되는데, 이 프록시 객체의 메서드를 호출하면, 내부적으로 TypedActor 객체에 메시지를 전송하게 되고 TypedActor 객체는 일치하는 메서드를 호출하게 된다.
[참고]
Akka는 TypedActor에 대한 프록시를 객체를 생성하기 위해 AspectWerkz(http://aspectwerkz.codehaus.org/ 참고)를 사용한다.
Fire-And-Forget
메서드의 리턴 타입이 void 이면, 해당 메서드에 대한 메시지는 sendOneWay()와 동일하게 Fire-And-Forget 방식으로 전송된다. 따라서, 메서드를 호출하면 액터가 메시지를 처리 여부에 상관없이 즉시 리턴한다. DataMigrator 인터페이스의 run() 메서드가 이에 해당한다.
DataMigrator migrator = TypedActor.newInstance(DataMigrator.class, DataMigratorImpl.class);
migrator.run(); // 리턴 타입이 void 이므로 Fire-And-Forget 방식
migrator.run(); // 리턴 타입이 void 이므로 Fire-And-Forget 방식
Send-And-Receive-Eventually
메서드가 리턴 타입을 가지면, sendRequestReply()와 동일하게 Send-And-Receive-Eventually 방식으로 메시지가 전송된다. 따라서, 액터로부터 응답이 도착할 때 까지 블럭킹 된다.
DataMigrator migrator = TypedActor.newInstance(DataMigrator.class, DataMigratorImpl.class);
int rest = migrator.restCount(); // Send-and-receive-Eventually 방식
int rest = migrator.restCount(); // Send-and-receive-Eventually 방식
Send-And-Receive-Future
메서드의 리턴 타입이 akka.dispatch.Future이면, Send-And-Receive-Future 방식으로 메서드를 호출한다.
리모트 TypedActor 생성하기
UntypedActor와 마찬가지로 TypedActor도 간단하게 리모트 액터로 제공할 수 있다.
리모트 서버에서 TypedActor 생성하기
리모트 서버에서, 액터를 리모트 액터로 등록하려면 registerTypedActor()를 사용하면 된다.
Actors.remote().start("0.0.0.0", 2553);
DataMigrator migrator = TypedActor.newInstance(DataMigrator.class, DataMigratorImpl.class);
Actors.remote().registerTypedActor("data-migrator", migrator);
DataMigrator migrator = TypedActor.newInstance(DataMigrator.class, DataMigratorImpl.class);
Actors.remote().registerTypedActor("data-migrator", migrator);
클라이언트 코드에서는 Actors.remote().typedActorFor() 메서드를 이용해서 리모트 액터에 대한 프록시 객체를 구한 뒤 알맞은 메서드를 호출하면 된다.
DataMigrator migrator = Actors.remote()
.typedActorFor(DataMigrator.class, "data-migrator", "172.20.1.2", 2553);
migrator.run();
int rest = migrator.restCount();
do {
Thread.sleep(1000);
rest = migrator.restCount();
} while(rest > 0);
Actors.remote().shutdown();
.typedActorFor(DataMigrator.class, "data-migrator", "172.20.1.2", 2553);
migrator.run();
int rest = migrator.restCount();
do {
Thread.sleep(1000);
rest = migrator.restCount();
} while(rest > 0);
Actors.remote().shutdown();
[주의]
클라이언트에서 리모트 액터를 생성할 수도 있으나, 현재 버전에서는 기능이 예상하는 대로 동작하지 않아 본 글에서는 소개하지 않는다.
참고자료
- http://doc.akka.io/typed-actors-java