Akka實踐一些總結(java專案)

fairjm發表於2018-11-28

本文來自 fairjm@圖靈社群 轉截請註明出處


最近在一些服務中使用了akka,主要用來做非同步解耦和本地訊息分發(路由).
總體使用比較簡單,這裡做下小結,具體的業務和拓撲圖就不提供了~.

與spring整合

網上有不少整合的例子,要使用到spring的擴充套件.
我這邊沒有這樣處理,而是簡單把ActorSystem建立的actor的過程放在了spring configuration裡,把ActorRef作為bean,畢竟actor本身不能為單例但是ref可以.

actor要使用一些bean的話就全部都由建構函式傳入.
如下:

public class UserActor extends AbstractLoggingActor {

    private UserService userService;

    public static Props props(UserService userService) {
        return Props.create(UserActor.class, () -> new UserActor(userService));
    }

    public User(UserService userService) {
        this.userService = userService;
    }

在java中使用

使用中能感覺和java還是有些隔閡的...
特別是容器(Collection)之間的轉換和處理.
給出一些例子,可能是我沒發現更方便的寫法..

List<Routee> routees = new ArrayList<>();
senders.forEach(e -> {
            try {
                ActorRef ref = getContext().actorOf(createdProps.apply(e), prefix + "-" + e.getId());
                routees.add(new ActorRefRoutee(ref));
            } catch (InvalidActorNameException ex) {
                log().error(ex, "name already in use");
            }
        });
router = new Router(new RoundRobinRoutingLogic(), routees);

這邊本來用一個map就能解決了,但會要求型別強轉Router的第二個引數是java.lang.Iterable<Routee>而不是java.lang.Iterable<? extends Routee>(ActorRefRoutee是其子類),再加上還要處理重名錯誤,還是用了這種錯誤的forEach使用方式.

private void stopRoutee(Router router) {
    List<Routee> routees = new ArrayList<>(seqAsJavaList(router.routees()));
    routees.forEach(e -> {
        ActorRefRoutee ae = (ActorRefRoutee) e;
        ActorRef ref = ae.ref();
        // 移除這個routee
        router.removeRoutee(ae);
        // 停止這個ref
        context().stop(ref);
    });
}

router.routees()返回的是個IndexSeq...和java的沒有相容和對應.
最開始找了一些,後來發現有scala.collection.JavaConversions可以做相容...感受到了隔閡.

此外ask模式,akka為java提供了PatternsCS.這個類返回的是java的CompletionStage型別相比Patterns,比較友好.

使用誤區

最容易誤用的一點就是訊息處理中有阻塞操作,比如直接在actor中進行資料庫操作和處理網路請求,而使用的actor也沒有和執行緒繫結,這種情況需要使用額外的執行緒池,這個其實和用netty是一樣的情況,用來進行應用排程的執行緒數有限.

由於上面提到的點,於是在actor內可能就會存在一些回撥,一不小心就可能直接呼叫/改變actor內部的狀態(例如在回撥直接訪問field).

private List<String> list;
... ...
public Receive createReceive() {
    return receiveBuilder()
            .match(Message.class, e -> xxxx.onSuccess(r -> list.add(r)))
            ... ...

這樣等於內部狀態被多執行緒訪問,破壞了actor內部的狀態,正確的做法是在回撥中給自己傳送訊息.

相關文章