AKKA序列化


序列化

Akka有内置的序列化扩展,你可以使用内置的序列化器,也可以自定义。

序列化机制可以在Akka内部用于序列化消息,也可以用于专门序列化。

用法

配置

Akka要想知道那个序列化器用于那些消息,你需要编辑配置"akka.actor.serializers"部分,你将序列化器的名字绑定到你希望使用的akka.serialization.Serializer的实现上,就像这样:

akka {

  actor {

    serializers {

      java = "akka.serialization.JavaSerializer"

      proto = "akka.remote.serialization.ProtobufSerializer"

      myown = "docs.serialization.MyOwnSerializer"

    }

  }}

在将名字绑定到序列化器的实现后,你需要配置哪个类应该用哪个序列化器,这是通过配置"akka.actor.serialization-bindings"完成的:

akka {

  actor {

    serializers {

      java = "akka.serialization.JavaSerializer"

      proto = "akka.remote.serialization.ProtobufSerializer"

      myown = "docs.serialization.MyOwnSerializer"

    }

 

    serialization-bindings {

      "java.lang.String" = java

      "docs.serialization.Customer" = java

      "com.google.protobuf.Message" = proto

      "docs.serialization.MyOwnSerializable" = myown

      "java.lang.Boolean" = myown

    }

  }}

你只需要指定消息的接口或者抽象类的名字。如果出现模棱两可的情况,即消息实现几个配置类,那么将会使用最确定的配置类,即其它的候选类都是超类。如果这个条件不满足,例如因为java.io.SerializableMyOwnSerializable都适用,并且这两个都说不是其它的子类型,akka会发出警告。

Akka默认提供了java.io.Serializableprotobuf com.google.protobuf.GeneratedMessage消息的序列化器(后面这个只有在依赖了akka-remote module才会与),所以正常情况在你不需要为这种消息添加配置,由于com.google.protobuf.GeneratedMessage实现了java.io.Serializableprotobuf消息总是使用protobuf协议序列化,除非被明确地覆写。为了禁止默认的序列化器,将它的标记类型映射为“none”

akka.actor.serialization-bindings {

  "java.io.Serializable" = none}

验证

如果你想要验证你的消息是可序列化的,你可以使能下面的配置选项:

akka {

  actor {

    serialize-messages = on

  }}

警告

我们只推荐使用配置选项来打开什么时候运行测试。其它场景打开测试是没有意义的。

如果你想要验证你的Props是可序列化的,你可以使能下面的配置选项:

akka {

  actor {

    serialize-creators = on

  }}

警告

我们只推荐使用配置选项来打开什么时候运行测试。其它场景打开测试是没有意义的。

编码实现序列化

如果你想要以编码方式实现序列化和反序列化,这儿有一些例子:

import akka.actor.*;

import akka.serialization.*;

ActorSystem system = ActorSystem.create("example");

// 获取序列化 

ExtensionSerialization serialization = SerializationExtension.get(system);

// 要序列化的对象

String original = "woohoo";

// 找到对应的序列化器

Serializer serializer = serialization.findSerializerFor(original);

// 将对象转换为字节数组

byte[] bytes = serializer.toBinary(original);

// 将字节数组转换回对象,类和类加载器都是null

String back = (String) serializer.fromBinary(bytes);

// 瞧!

assertEquals(original, back);

要了解更多信息,请查看ScalaDoc学习akka.serialization._

自定义序列化器

如果你想要创建自己的序列化器,你看过上面配置示例docs.serialization.MyOwnSerializer?

创建新的序列化器

首先你需要创建序列化器的类定义,继承akka.serialization.JSerializer,就像这样:

import akka.actor.*;
import akka.serialization.*;

public class MyOwnSerializer extends JSerializer {
    // 这个方法定义fromBinary是否需要"clazz"
    @Override
    public boolean includeManifest() {
        return false;
    }

    // 选择序列化器的一个唯一标识符,你有数十亿的选择,但0 - 16 是为Akka自己保留的
    @Override
    public int identifier() {
        return 1234567;
    }

    // "toBinary"将给定对象序列化为一个字节数组
    @Override
    public byte[] toBinary(Object obj) {
        // Put the code that serializes the object here
        // ... ...
    }

    // "fromBinary"反序列化指定的数组,使用类型提示(如果存在,参见includeManifest)
    @Override
    public Object fromBinaryJava(byte[] bytes, Class<?> clazz) {
        // Put your code that deserializes here
        // ... ...
    }
}
然后你只需要填满空白的地方,在配置中将它绑定到一个名字上,然后在列出那些类应该使用它进行序列化。

序列化ActorRef

所有的ActorRef都使用JavaSerializer序列化,但是如果你正在编写自己的序列化器,你可能想要知道如何正确地序列化和反序列化它们。通常情况下,本地地址依赖于远程地址的类型,远程地址是序列化消息的接收者。使用Serialization.serializedActorPath(actorRef)

import akka.actor.*;import akka.serialization.*;

// 序列化// (底层是toBinary)

String identifier = Serialization.serializedActorPath(theActorRef);

// 然后直接序列化identifier

// 反序列化// (底层是fromBinary)

final ActorRef deserializedActorRef = extendedSystem.provider().resolveActorRef(

identifier);

// 然后直接使用ActorRef

这假设序列化发生在发送向远程端口消息的上下文中。序列化还有其它的用途,例如在actor应用外部(数据库等)存储actor应用。在这种情况下,记住actor路径是很重要的,它的地址决定了actor如何进行通信。存储本地的actor路径可能是一个正确的选择,如果读取发生在相同的逻辑上下文中,但是当在不同的网络主机上反序列化时这是远远不够的:因为它需要包含系统的远程传输地址。一个橘色系统不会限制一个系统只有一个远程传输端口,这使得问题变得有趣起来。为了在发送到远程地址时能够找到合适的地址,你可以使用ActorRefProvider.getExternalAddressFor(remoteAddr),就像这样:

public class ExternalAddressExt implements Extension {

  private final ExtendedActorSystem system;

  public ExternalAddressExt(ExtendedActorSystem system) {
    this.system = system;
  }

  public Address getAddressFor(Address remoteAddress) {
    final scala.Option<Address> optAddr = system.provider().getExternalAddressFor(remoteAddress);
    if (optAddr.isDefined()) {
      return optAddr.get();
    } else {
      throw new UnsupportedOperationException("cannot send to remote address " + remoteAddress);
    }
  }
}

public classExternalAddress extends AbstractExtensionId<ExternalAddressExt> implements ExtensionIdProvider {

  public static final ExternalAddress ID = new ExternalAddress();

  public ExternalAddress lookup() {
    return ID;
  }

  public ExternalAddressExt createExtension(ExtendedActorSystem system) {
    return new ExternalAddressExt(system);
  }
}

public class ExternalAddressExample {

  public String serializeTo(ActorRef ref, Address remote) {
    return ref.path().toSerializationFormatWithAddress(
        ExternalAddress.ID.get(system).getAddressFor(remote));
  }
}

注意

如果地址不没有hostport部分,即它只是插入本地的地址信息,那么ActorPath.toSerializationFormatWithAddresstoString的结果是不同的。toSerializationFormatWithAddress也为actor添加了唯一id,当actor停止时这个id会改变,然后用相同的名字再次创建一个actor。向指向老actor的引用发送消息不会分发到新的actor。如果不想要这个行为,例如长期存储引用,你可以使用toStringWithAddress,这个方法不会包含唯一的id

这需要你至少知道反序列化actor引用的系统支持的地址类型;如果没有具体的地址,你可以使用Address(protocol, "", "", 0)为协议创建一个虚拟的地址 (假设实际传送端口像AkkaRemoteActorRefProvider一样宽容)

还有一个默认的远程地址,是由集群支持使用的(通常系统只有这一个),你可以这样获取它:

public class DefaultAddressExt implements Extension {

  private final ExtendedActorSystem system;

  public DefaultAddressExt(ExtendedActorSystem system) {
    this.system = system;
  }

  public Address getAddress() {
    return system.provider().getDefaultAddress();
  }
}

public class DefaultAddress extends AbstractExtensionId<DefaultAddressExt> implements ExtensionIdProvider {

  public staticfinal DefaultAddress ID = new DefaultAddress();

  public DefaultAddress lookup() {
    return ID;
  }

  public DefaultAddressExt createExtension(ExtendedActorSystem system) {
    return new DefaultAddressExt(system);

  }
}

深度序列化Actor

推荐使用Akka Persistence完成内部actor状态的深度序列化。

谈谈Java序列化

当你使用Java序列化,而没有采用JavaSerializer,你必须确保为动态变量JavaSerializer.currentSystem提供一个合法的ExtendedActorSystem。这将用于读取ActorRef的表示,将字符串表示转换为真正的引用。DynamicVariable是一个thread-local变量,所以当反序列化包含actor引用的消息时,确保设置了该变量。

外部Akka序列化器

Akka-protostuff by Roman Levenstein

Akka-quickser by Roman Levenstein

Akka-kryo by Roman Levenstein

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值