7.3 有关消息投递的一些说明
整个Akka应用是由消息驱动的。消息是除了Actor之外最重要的核心组件。作为在并发程序中的核心组件,在Actor之间传递的消息应该满足不可变性,也就是不变模式。因为可变的消息无法高效的在并发环境中使用。理论上Akka中的消息可以使用任何对象实例,但实际使用中,强烈推荐使用不可变的对象。一个典型的不可变对象的实现如下:
01 public final class ImmutableMessage { 02 private final int sequenceNumber; 03 04 private final List<String> values; 05 06 public ImmutableMessage(int sequenceNumber, List<String> values) { 07 this.sequenceNumber = sequenceNumber; 08 this.values = Collections.unmodifiableList(new ArrayList<String>(values)); 09 } 10 11 public int getSequenceNumber() { 12 return sequenceNumber; 13 } 14 15 public List<String> getValues() { 16 return values; 17 } 18 }
上述代码实现了一个不可变的消息。注意代码中对final的使用,它申明了当前消息中的几个字段都是常量,在消息构造完成后,就不能再发生改变了。更加需要注意的是,对于values字段,final关键字只能保证values引用的不可变性,并无法保证values对象的不可变性。为了实现彻底的不可变性,代码第8行构造了一个不可变的List对象。
对于消息投递,大家可能还有另外一个疑问,那就是消息投递究竟是以何种策略进行的呢?也就是发出去的消息一定会被对方接收到吗?如果接收不到会重发吗?有没有可能重复接收消息呢?
实际上,对于消息投递,我们可以有3种不同的策略:
第1种,称为至多一次投递。在这种策略中,每一条消息最多会被投递一次。在这种情况下,可能偶尔会出现消息投递失败,而导致消息丢失。
第2种称为至少一次投递。在这种策略中,每一条消息至少会被投递一次,直到成功为止。因此在一些偶然的场合,接受者可能会收到重复的消息,但不会发生消息丢失。
第3种称为精确的消息投递。也就是所有的消息保证被精确地投递并成功接收一次,既不会有丢失,也不会有重复接收。
很明显,第1种策略是最高性能,最低成本的。因为系统只要负责把消息送出去就可以了,不需要关注是否成功。第2种策略则需要保存消息投递的状态并不断充实。而第3种策略则是成本最高且最不容易实现的。
那我们是否真的需要保证消息投递的可靠性呢?
答案是否定的。实际上,我们没有必要在Akka层保证消息的可靠性。这样做,成本太高了,也是没有必要的。消息的可靠性更应该在应用的业务层去维护,因为也许在有些时候,丢失一些消息完全是符合应用要求的。因此,在使用Akka时,需要在业务层对此进行保证。
此外,对于消息投递Akka可以在一定程度上保证顺序性。比如,Actor A1向A2顺序发送了M1、M2和M3三条消息。Actor A3向A2顺序发送了M4、M5和M6三条消息。那么系统可以保证:
(1)如果M1没有丢失,那它一定先于M2和M3被A2收到。
(2)如果M2没有丢失,那它一定先于M3被A2收到。
(3)如果M4没有丢失,那它一定先于M5和M6被A2收到。
(4)如果M5没有丢失,那它一定先于M6被A2收到。
(5)对A2来说,来自A1和A3的消息可能交织在一起,没有顺序保证。
在这里,值得注意的一点是,这种消息投递规则不具备可传递性,比如:
Actor A向C发送了M1,接着,Actor A向B发送了M2,B将M2转发给Actor C。那么在这种情况下,C收到M1和M2的先后顺序是没有保证的。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论