Java8 Lambda運算式應用案例 -- 單線程遊戲伺服器+非同步資料庫操作

來源:互聯網
上載者:User

標籤:java   伺服器   非同步   遊戲   lambda   

前段時間我們遊戲伺服器的開發環境升級到了Java8,這兩天我又把伺服器的執行緒模式重新設計了一下,用上了Lambda運算式。Lambda運算式確實能夠大幅簡化Java代碼,特別是醜陋不堪的匿名內部類,這篇文章主要就是想和大家分享這一點。

執行緒模式

首先簡單介紹一下我們遊戲伺服器的執行緒模式,大致如所示:

Netty線程池只處理訊息的收發,當Netty收到訊息之後,會交給遊戲邏輯線程處理。由於是單線程在處理遊戲邏輯,所以每一個訊息必須很快處理完,也就是說,不能有資料庫等耗時操作,不然邏輯線程很可能會被卡住。為了不卡住邏輯線程,資料庫操作由單獨的線程池來處理。邏輯線程發起資料庫操作後便立即返回繼續處理其他訊息,資料庫線程池處理完畢後,再通知邏輯線程,從而達到了非同步資料庫操作的效果。

GameAction

Netty部分的網路代碼,在收到訊息後,會根據訊息找到對應的Action,然後執行。具體代碼省略,下面是簡化後的GameAction的代碼:

public abstract class GameAction {        /**     * 在邏輯線程裡處理訊息.     * @param gs     * @param req 請求訊息     */    public void execute(GameSession gs, Object req) {        GameLogicExecutor.execute(() -> {            doExecute(gs, req);        });    }        // 子類實現    public abstract void doExecute(GameSession gs, Object req);    }
execute()方法裡,使用了Lambda運算式來實現Runnable介面。

GameLogicExecutor

GameLogicExecutor是遊戲邏輯執行線程,代碼如下所示:

import java.util.concurrent.Executor;import java.util.concurrent.Executors;/** * 遊戲邏輯線程. */public class GameLogicExecutor {        // todo 限制隊列長度    private static final Executor executor = Executors.newSingleThreadExecutor();        /**     * 把遊戲邏輯放到隊列.     * @param gameLogic      */    public static void execute(Runnable gameLogic) {        executor.execute(gameLogic);    }    }

GetPlayerListAction

下面看一個具體的GameAction實現,這個Action根據使用者ID返回使用者建立的玩家列表,代碼如下:

import java.util.List;public class GetPlayerListAction extends GameAction {    @Override    public void doExecute(GameSession gs, Object req) {        int userId = (Integer) req;        PlayerDao.getPlayerList(userId, (List<Player> players) -> {            gs.write(players);        });    }    }
假設請求參數是玩家ID,doExecute()方法並沒有等待資料庫操作,而是馬上就返回了。傳遞給DAO的回調對象是個Lambda運算式,在回調方法裡,玩家列表通過GameSession被寫到用戶端(這隻是示範,實際的響應訊息可能是protobuf或JSON)。

PlayerDao

import java.util.ArrayList;import java.util.List;public class PlayerDao {        public static void getPlayerList(int userId, DbOpCallback<List<Player>> cb) {        DbOpExecutor.execute(() -> {            try {                List<Player> players = getPlayerList(userId);                cb.ok(players);            } catch (Exception e) {                cb.fail(e);            }        });    }        // 耗時的資料庫操作    private static List<Player> getPlayerList(int userId) {        return new ArrayList<>();    }    }

getPlayerList()方法接收兩個參數,第一個參數是使用者ID,第二個參數是個callback,當資料庫操作完畢(成功或失敗)時,會通知這個callback。DAO內部使用的可能是JDBC或MyBatis等,總之是耗時的操作,由資料庫線程池執行。

DbOpExecutor

DbOpExecutor的代碼比較簡單,和GameLogicExecutor相似,如下所示:

import java.util.concurrent.Executor;import java.util.concurrent.Executors;/** * 資料庫操作線程池. */public class DbOpExecutor {        // todo 根據cpu數等確定線程數    private static final Executor executor = Executors.newFixedThreadPool(4);        /**     * 把資料庫操作放進隊列.     * @param dbOp      */    public static void execute(Runnable dbOp) {        executor.execute(dbOp);    }    }

DbOpCallback

最後看一下DbOpCallback代碼:

/** * 資料庫操作回調. * @param <T> */@FunctionalInterfacepublic interface DbOpCallback<T> {        /**     * 處理資料庫返回結果.     * @param result      */    void handleResult(T result);        /**     * 資料庫操作正常結束.     * @param result      */    default void ok(T result) {        // 在遊戲邏輯線程裡處理結果        GameLogicExecutor.execute(() -> {            try {                handleResult(result);            } catch (Exception e) {                // todo 處理異常            }        });    }        /**     * 資料庫操作出現異常.     * @param e      */    default void fail(Exception e) {        // todo 處理異常    }    }

@FunctionalInterface說明這是一個函數式介面,簡單的說,就是只有一個抽象方法的介面。介面的default方法是Java8的新文法,具體請參考Java8相關方面的資料。ok()方法確保資料庫操作的結果是在邏輯線程裡處理。

結論

上面的代碼,在Java8之前用匿名內部類也是可以寫的,只是相比Lambda運算式,更加冗長醜陋而已。另外要注意,上面的代碼只是簡化後的範例程式碼,並非真實代碼。如果想將上面的代碼應用到自己的項目中,還請注意異常的處理。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.