最近在一些服务中使用了akka,主要用来做异步解耦和本地消息分发(路由),这里简单总结一下用法.
与spring集成
网上有不少集成的例子,要使用到spring的扩展.
我这边没有这样处理,而是简单把ActorSystem
创建的actor的过程放在了spring configuration里,把ActorRef
作为bean,毕竟actor本身不能为单例但是ref可以.
actor要使用一些bean的话就全部都由构造函数传入.
如下:
1 | public class UserActor extends AbstractLoggingActor { |
在java中使用
使用中能感觉和java还是有些隔阂的…
特别是容器(Collection)之间的转换和处理.
给出一些例子,可能是我没发现更方便的写法..
1 | List<Routee> routees = new ArrayList<>(); |
这边本来用一个map就能解决了,但会要求类型强转Router的第二个参数是java.lang.Iterable<Routee>
而不是java.lang.Iterable<? extends Routee>
(ActorRefRoutee是其子类),再加上还要处理重名错误,还是用了这种错误的forEach使用方式.
1 | private void stopRoutee(Router router) { |
router.routees()返回的是个IndexSeq…和java的没有兼容和对应.
最开始找了一些,后来发现有scala.collection.JavaConversions
可以做兼容…感受到了隔阂.
此外ask模式,akka为java提供了PatternsCS
.这个类返回的是java的CompletionStage
类型相比Patterns
,比较友好.
使用误区
最容易误用的一点就是消息处理中有阻塞操作,比如直接在actor中进行数据库操作和处理网络请求,而使用的actor也没有和线程绑定,这种情况需要使用额外的线程池,这个其实和用netty是一样的情况,用来进行应用调度的线程数有限.
由于上面提到的点,于是在actor内可能就会存在一些回调,一不小心就可能直接调用/改变actor内部的状态(例如在回调直接访问field).
1 | private List<String> list; |
这样等于内部状态被多线程访问,破坏了actor内部的状态,正确的做法是在回调中给自己发送消息.