Akka 【二】Create Actor

在介绍如何创建actor之前,先来看一个类Props。Props是一个配置类,用于指定创建actor的参数等。以下是Props的几个典型应用,

Props


1)直接传入所需要创建的actor对应的类

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.UntypedActor;

public class CreateTest {
	enum MSG {
		HI, OK
	}

	static class Actor1 extends UntypedActor {

		@Override
		public void onReceive(Object message) throws Exception {
			// TODO Auto-generated method stub
			if (message instanceof MSG) {
				if (message.equals(MSG.OK)) {
					System.out.println("i receive ok");
				} else {
					unhandled(message);
				}
			} else {
				unhandled(message);
			}

		}

	}

	static class Actor2 extends UntypedActor {

		@Override
		public void onReceive(Object message) throws Exception {
			if (message instanceof MSG) {
				if (message.equals(MSG.HI)) {
					System.out.println("i receive hi");
					getSender().tell(MSG.OK, getSelf());
				} else {
					unhandled(message);
				}
			} else {
				unhandled(message);
			}
		}

	}
	
	public static void communication(){
		
		// 这里直接传入所需要创建的Actor对应的类
		Props props1 = Props.create(Actor1.class);
		Props props2 = Props.create(Actor2.class);
		
		// 使用Props创建Actor
		final ActorSystem system = ActorSystem.create("MySystem");
		final ActorRef actor1 = system.actorOf(props1, "Actor1");
		final ActorRef actor2 = system.actorOf(props2, "Actor2");
		actor2.tell(MSG.HI, actor1);
		
		system.stop(actor1);
		system.stop(actor2);
		system.shutdown();
	}

	public static void main(String[] args) {
		communication();
	}
}



在这种情况下,创建actor的时候会调用默认的构造函数。


2)直接传入所需要创建的actor对应的类,以及构造函数的参数

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.UntypedActor;

public class CreateTest {
	enum MSG {
		HI, OK
	}

	static class Actor1 extends UntypedActor {
		String name;
		int index;
		
		Actor1(String name, int index){
			this.name = name;
			this.index = index;
		}
		
		@Override
		public void onReceive(Object message) throws Exception {
			System.out.println(name + "_" + index + " receive message");
			if (message instanceof MSG) {
				if (message.equals(MSG.OK)) {
					System.out.println("i receive ok");
				} else {
					unhandled(message);
				}
			} else {
				unhandled(message);
			}

		}

	}

	static class Actor2 extends UntypedActor {
		String name;
		int index;
		
		Actor2(String name, int index){
			this.name = name;
			this.index = index;
		}
		
		@Override
		public void onReceive(Object message) throws Exception {
			System.out.println(name + "_" + index + " receive message");
			if (message instanceof MSG) {
				if (message.equals(MSG.HI)) {
					System.out.println("i receive hi");
					getSender().tell(MSG.OK, getSelf());
				} else {
					unhandled(message);
				}
			} else {
				unhandled(message);
			}
		}

	}
	
	public static void communication(){
		
		// 这里直接传入所需要创建的Actor对应的类,同时传入创建actor时所需要的构造参数。
		Props props1 = Props.create(Actor1.class, "Actor1", 1);
		Props props2 = Props.create(Actor2.class, "Actor2", 1);
		
		// 使用Props创建Actor
		final ActorSystem system = ActorSystem.create("MySystem");
		final ActorRef actor1 = system.actorOf(props1, "Actor1");
		final ActorRef actor2 = system.actorOf(props2, "Actor2");
		actor2.tell(MSG.HI, actor1);
		
		system.stop(actor1);
		system.stop(actor2);
		system.shutdown();
	}

	public static void main(String[] args) {
		communication();
	}
}

上面创造的props1,传入的参数分别是Actor1.class ,“Actor1”,1。其中"Actor1"和1对应的是构造函数的两个参数。回到Actor1,里面定义两个显示构造函数,传入的参数分别是name(String)和index(int),因此上面说的“Actor1”和1正好能够匹配成功这两个参数。props2同理。

运行上面的程序可以看到,

Actor2_1 receive message
i receive hi
Actor1_1 receive message
i receive ok
[INFO] [07/12/2014 11:24:16.189] [MySystem-akka.actor.default-dispatcher-3] [akka://MySystem/user] Message [akka.actor.StopChild] from Actor[akka://MySystem/deadLetters] to Actor[akka://MySystem/user] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
[INFO] [07/12/2014 11:24:16.189] [MySystem-akka.actor.default-dispatcher-3] [akka://MySystem/user] Message [akka.actor.StopChild] from Actor[akka://MySystem/deadLetters] to Actor[akka://MySystem/user] was not delivered. [2] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.

那么如果传入的参数无法和构造函数的参数匹配上,会发生什么呢?

修改props1的创建语句如下,

		Props props1 = Props.create(Actor1.class, "Actor1", 1, 2);

这种情况无法匹配任何一个构造函数,运行后出现如下信息

Exception in thread "main" java.lang.IllegalArgumentException: no matching constructor found on class CreateTest$Actor1 for arguments [class java.lang.String, class java.lang.Integer, class java.lang.Integer]
	at akka.util.Reflect$.error$1(Reflect.scala:82)
	at akka.util.Reflect$.findConstructor(Reflect.scala:106)
	at akka.actor.ArgsReflectConstructor.<init>(Props.scala:350)
	at akka.actor.IndirectActorProducer$.apply(Props.scala:309)
	at akka.actor.Props.producer(Props.scala:176)
	at akka.actor.Props.<init>(Props.scala:189)
	at akka.actor.Props$.create(Props.scala:99)
	at akka.actor.Props$.create(Props.scala:99)
	at akka.actor.Props.create(Props.scala)
	at CreateTest.communication(CreateTest.java:66)
	at CreateTest.main(CreateTest.java:81)

很明显在这种情况下会抛出通用的IllegalArgumentException异常,而后面的error message很明确的进行了提示。

3)传入Creator<T>接口的实例

import java.util.concurrent.atomic.AtomicInteger;

import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.japi.Creator;

public class CreateTest {
	enum MSG {
		HI, OK
	}
	
	static class Creator1 implements Creator<Actor1>{
		// 多线程保证
		AtomicInteger index = new AtomicInteger();
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Actor1 create() throws Exception {
			// TODO Auto-generated method stub
			return new Actor1("Actor1", index.getAndAdd(1));
		}
		
	}
	
	static class Creator2 implements Creator<Actor2>{
		// 多线程保证
		AtomicInteger index = new AtomicInteger();
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public Actor2 create() throws Exception {
			// TODO Auto-generated method stub
			return new Actor2("Actor2", index.getAndAdd(1));
		}
		
	}

	static class Actor1 extends UntypedActor {
		String name;
		int index;
		
		Actor1(String name, int index){
			this.name = name;
			this.index = index;
		}
		
		@Override
		public void onReceive(Object message) throws Exception {
			System.out.println(name + "_" + index + " receive message");
			if (message instanceof MSG) {
				if (message.equals(MSG.OK)) {
					System.out.println("i receive ok");
				} else {
					unhandled(message);
				}
			} else {
				unhandled(message);
			}

		}

	}

	static class Actor2 extends UntypedActor {
		String name;
		int index;
		
		Actor2(String name, int index){
			this.name = name;
			this.index = index;
		}
		
		@Override
		public void onReceive(Object message) throws Exception {
			System.out.println(name + "_" + index + " receive message");
			if (message instanceof MSG) {
				if (message.equals(MSG.HI)) {
					System.out.println("i receive hi");
					getSender().tell(MSG.OK, getSelf());
				} else {
					unhandled(message);
				}
			} else {
				unhandled(message);
			}
		}

	}
	
	public static void communication(){
		
		// 创建Creator实例,这个Creator用于创建Actor,然后将Creator传入到Props中
		Creator1 creator1 = new Creator1();
		Creator2 creator2 = new Creator2();
		Props props1 = Props.create(creator1);
		Props props2 = Props.create(creator2);
		
		// 使用Props创建Actor
		final ActorSystem system = ActorSystem.create("MySystem");
		final ActorRef actor1 = system.actorOf(props1);
		final ActorRef actor2 = system.actorOf(props2);
		actor2.tell(MSG.HI, actor1);
		
		system.stop(actor1);
		system.stop(actor2);
		system.shutdown();
	}

	public static void main(String[] args) {
		communication();
	}
}


这里看到Creator1和Creator2十分像一个工厂,它的create方法用于创建actor。需要注意的是,creator类必须是静态的!在创建Props的时候会进行校验。Creator<T>是一个模板类,在实现这个接口的时候指定T,就决定了这个Creator未来创建的actor的类型。

如果creator不是静态的会出现什么情况?

修改上面的代码,将Creator1定义如下,

	public class Creator1 implements Creator<Actor1>{
然后修改creator1的创建代码如下,

		CreateTest createrTest = new CreateTest();
		Creator1 creator1 = createrTest.new Creator1();
运行代码后出现如下结果,

Exception in thread "main" java.lang.IllegalArgumentException: cannot use non-static local Creator to create actors; make it static (e.g. local to a static method) or top-level
	at akka.actor.Props$.create(Props.scala:112)
	at akka.actor.Props.create(Props.scala)
	at CreateTest.communication(CreateTest.java:102)
	at CreateTest.main(CreateTest.java:117)

不能使用非静态的局部Creator创建actors。

Props最佳实践

在Akka文档中提到:在Actor类中定义一个静态的方法,用于获取Props是一个不错的方法,这样可以使得Props的定义与Actor的定义尽量的靠近。
根据官网上的例子我们将上面的例子中的Actor2修改如下(单独提取出来成为一个类)
import java.util.concurrent.atomic.AtomicInteger;

import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.japi.Creator;

public class Actor2 extends UntypedActor {
	String name;
	int index;

	public Actor2(String name, int index) {
		this.name = name;
		this.index = index;
	}

	@Override
	public void onReceive(Object message) throws Exception {
		System.out.println(name + "_" + index + " receive message");
		if (message instanceof MSG) {
			if (message.equals(MSG.HI)) {
				System.out.println("i receive hi");
				getSender().tell(MSG.OK, getSelf());
			} else {
				unhandled(message);
			}
		} else {
			unhandled(message);
		}
	}

	public static Props props() {
		return Props.create(new Creator<Actor2>() {
			/**
				 * 
				 */
			private static final long serialVersionUID = 1L;
			private AtomicInteger index = new AtomicInteger();

			public Actor2 create() throws Exception {
				// TODO Auto-generated method stub
				return new Actor2("Actor2", index.getAndAdd(1));
			}
		});
	}
}

这里采用的传入匿名类的实例到create方法中。
运行后出现的错误和上面提到的一样,都是提示Creator必须是静态类。
虽然没有系统学习过Scala,但是Create方法中最开始的校验还是很明确的表示了Creator必须是静态类,
def create[T <: Actor](creator: Creator[T]): Props = {
    val cc = creator.getClass
    if ((cc.getEnclosingClass ne null) && (cc.getModifiers & Modifier.STATIC) == 0)
      throw new IllegalArgumentException("cannot use non-static local Creator to create actors; make it static (e.g. local to a static method) or top-level")
    val ac = classOf[Actor]
    val coc = classOf[Creator[_]]
    val actorClass = Reflect.findMarker(cc, coc) match {
      case t: ParameterizedType ⇒
        t.getActualTypeArguments.head match {
          case c: Class[_] ⇒ c // since T <: Actor
          case v: TypeVariable[_] ⇒
            v.getBounds collectFirst { case c: Class[_] if ac.isAssignableFrom(c) && c != ac ⇒ c } getOrElse ac
          case x ⇒ throw new IllegalArgumentException(s"unsupported type found in Creator argument [$x]")
        }
      case c: Class[_] if (c == coc) ⇒
        throw new IllegalArgumentException(s"erased Creator types are unsupported, use Props.create(actorClass, creator) instead")
    }
    apply(defaultDeploy, classOf[CreatorConsumer], actorClass :: creator :: Nil)
  }

具体为什么这样做,不得而知。我猜想是不是因为Scala语言的特性,虽然我们可以基于Java使用Akka,但是底层的Akka包还是由Scala编写,因此其对于参数有某些要求。
既然上面的方法不行,那么怎么办呢?
在匿名类上使用eclipse快捷键ctrl+1,


选择第二个后,代码被重构为,
import java.util.concurrent.atomic.AtomicInteger;

import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.japi.Creator;

public class Actor2 extends UntypedActor {
	private static final class CreatorImplementation implements Creator<Actor2> {
		/**
			 * 
			 */
		private static final long serialVersionUID = 1L;
		private AtomicInteger index = new AtomicInteger();

		public Actor2 create() throws Exception {
			// TODO Auto-generated method stub
			return new Actor2("Actor2", index.getAndAdd(1));
		}
	}

	String name;
	int index;

	public Actor2(String name, int index) {
		this.name = name;
		this.index = index;
	}

	@Override
	public void onReceive(Object message) throws Exception {
		System.out.println(name + "_" + index + " receive message");
		if (message instanceof MSG) {
			if (message.equals(MSG.HI)) {
				System.out.println("i receive hi");
				getSender().tell(MSG.OK, getSelf());
			} else {
				unhandled(message);
			}
		} else {
			unhandled(message);
		}
	}

	public static Props props() {
		return Props.create(new CreatorImplementation());
	}
}

原有的匿名类被显示地提取为了一个静态内部类,这种方法其实和我们最开始提到的方法相类似。

然后将Actor1按照同样的方式进行重构,修改测试类如下所示,

import akka.actor.ActorRef;
import akka.actor.ActorSystem;

public class CreateTest {

	public void communication() {

		// 使用Props创建Actor
		final ActorSystem system = ActorSystem.create("MySystem");
		final ActorRef actor1 = system.actorOf(Actor1.props());
		final ActorRef actor2 = system.actorOf(Actor2.props());
		actor2.tell(MSG.HI, actor1);
		
		system.stop(actor1);
		system.stop(actor2);
		system.shutdown();
	}

	public static void main(String[] args) {
		new CreateTest().communication();
	}
}



运行后一切正常。
Actor2_0 receive message
i receive hi
Actor1_0 receive message
i receive ok
[INFO] [07/13/2014 01:27:22.357] [MySystem-akka.actor.default-dispatcher-5] [akka://MySystem/user/$a] Message [akka.dispatch.sysmsg.Terminate] from Actor[akka://MySystem/user/$a#1651324613] to Actor[akka://MySystem/user/$a#1651324613] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
[INFO] [07/13/2014 01:27:22.359] [MySystem-akka.actor.default-dispatcher-5] [akka://MySystem/user] Message [akka.actor.StopChild] from Actor[akka://MySystem/deadLetters] to Actor[akka://MySystem/user] was not delivered. [2] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.


不同的Actor类型

顶级actor


前面的例子中创建Actor都是调用的SystemActor, 使用这种方法创建的Actor属于顶级Actor,它们由系统提供的guardian actor直接监管。

修改测试类,添加如下语句,
		System.out.println(system.guardian().path());
		System.out.println(actor1.path());
		System.out.println(actor2.path());

运行后得到如下信息,

akka://MySystem/user
Actor2_0 receive message
akka://MySystem/user/$a
i receive hi
akka://MySystem/user/$b
Actor1_0 receive message
i receive ok
[INFO] [07/13/2014 11:43:02.350] [MySystem-akka.actor.default-dispatcher-2] [akka://MySystem/user/$a] Message [akka.dispatch.sysmsg.Terminate] from Actor[akka://MySystem/user/$a#1387654750] to Actor[akka://MySystem/user/$a#1387654750] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
[INFO] [07/13/2014 11:43:02.352] [MySystem-akka.actor.default-dispatcher-2] [akka://MySystem/user] Message [akka.actor.StopChild] from Actor[akka://MySystem/deadLetters] to Actor[akka://MySystem/user] was not delivered. [2] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.

相比之前的结果,多了几个路径,路径的结构是akka://xxx/xxx/...。可以看到MySystem是我们设置的SystemActor的名字,而user是guardian actor的路径,guardian actor主要负责顶级actor的监管,这在前面已经提到了。而由于我们没有设置actor1和actor2的路径,所以系统为这两个actor生成了默认的路径$a和$b。如果设置了actor1的路径是"actor1",那么actor1的路径就是akka://MySystem/user/actor1,actor2同理。

其他actor

使用一个actor的context可以创建这个actor的子actor。

修改Actor1,重写preStart方法,
	@Override
	public void preStart() throws Exception {
		final ActorRef actor2 = getContext().actorOf(Actor2.props(), "actor2");
		
		actor2.tell(MSG.HI, getSelf());
		
		System.out.println(getSelf().path().parent());
		System.out.println(getSelf().path());
		System.out.println(actor2.path());
	}

修改测试类如下,

import akka.actor.ActorRef;
import akka.actor.ActorSystem;

public class CreateTest {

	public void communication() {

		// 使用Props创建Actor
		final ActorSystem system = ActorSystem.create("MySystem");
		final ActorRef actor1 = system.actorOf(Actor1.props(), "actor1");

		system.shutdown();
	}

	public static void main(String[] args) {
		new CreateTest().communication();
	}
}

运行后得到如下信息,
Actor2_0 receive message
i receive hi
akka://MySystem/user
akka://MySystem/user/actor1
akka://MySystem/user/actor1/actor2
Actor1_0 receive message
i receive ok

可以推出,

guardian___
                      |___actor1___
                      |___                 |___actor2

akk文档中建议:最好创建树状的结构,也就是为每个actor创建适当数量的子actor,以便其能适应应用的容错架构。
(It is recommended to create a hierarchy of children, grand-children and so on such that it fits the logical failurehandling structure of the application, see Actor Systems)

ActorRef

如论是创建顶级Actor还是非顶级Actor,actorOf的返回值类型都是ActorRef。ActorRef持有actor的实例,也是唯一可以与actor交互的。ActorRef是不变的,并且和其表示的Actor一一对应。同时ActorRef也是可序列化的,也就是说你可以序列化ActorRef,并且通过网络将其传送到远端节点,在远端主机上通过反序列化,它仍然代表了在原始节点的同一个Actor。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值