- 功能
- 客戶端交互
- 客戶端獲取服務器列表客戶端
- 獲取公告
- CDN服務
- 資源包上傳
- 更新資源
- 服務端交互
- 服務端驗證登錄
- 支付分發給服務端
- 數據配置
- GM功能
- 數據統計
- 客戶端交互
- 技術
- 高級語言--Java8
- 框架--SpringBoot2.0
- 項目管理--Gradle
- 緩存--redis
- 數據庫Mysql5.6
- 通信框架--Netty
- 傳輸框架--Protostuff
- 啟動流程
- 不占用端口啟動
- 通過注解獲取協議進行初始化
- 初始化線程池用於有序處理客戶端消息
- 初始化游戲數據
- 向后台獲取游戲配置數據
- 初始化定時任務用於定時更新數據
- 啟動Netty
- 關閉流程
- 實現接口ApplicationListener<ContextClosedEvent>
- 配置中注冊監聽
- 服務器關閉前處理臨時數據落地到數據庫
- 開發總結
- 關於緩存
- 緩存使用的redis
- 使用Jackson2JsonRedisSerializer替換了value的序列化與反序列化,但是對應map的序列化如果有排序要求,即使使用ConcurrentSkipListMap有序的集合,依然在反序列化的時候回出現順序錯誤,在redis可視化工具看到的數據順序是對的
- 關於成就設計
- 成就采用的是spring自帶的事件系統
- 業務處理完回到數據完成后可以提交事件
- 在成就或任務系統中對事件進行處理
- 關於線程池
- 重寫了拒絕策略
-
/** * 在線程池提交任務的最后一步——被線程池拒絕的任務,可以在拒絕后調用隊列的put()方法,讓任務的提交者阻塞,直到隊列中任務被被線程池執行后,隊列有了多余空間,調用方才返回 */ private static class BlockCallerPolicy implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { try { executor.getQueue().put(r); } catch (InterruptedException e) { e.printStackTrace(); } } }
- 關於業務初始化
- 使用了接口
-
/** * 初始化接口 * * @date :2019/3/14 17:15 */ public interface InitBaseHandler { /** * 實現此方法將在服務器啟動時進行初始化操作 */ void init(); }
- 此接口可以在應用初始化時調用,將調用實現此接口的所有實現類
-
LogHandler.info("no2.初始化游戲數據......"); applicationContext.getBeansOfType(InitBaseHandler.class).values().forEach(InitBaseHandler::init);
- 關於定時器
- 先使用的每分鍾一次的定時器
-
/** * 定時更新 */ @Override public void init() { LogHandler.info("初始化定時任務"); int second = Calendar.getInstance().get(Calendar.SECOND); ExecutorHandler.scheduledExecutorService.scheduleAtFixedRate(() -> { Calendar calendar = Calendar.getInstance(); int minute = calendar.get(Calendar.MINUTE); if (minute % INTERVAL == 0) { playerService.updatePlayer(); playerService.updatePlayerData(); globalService.updateGlobalData(); } }, 60 - second, 60, TimeUnit.SECONDS); }
- 后期會優化為擴展性更高的類似linux的cron月日時分定時器
- 沒有考慮年
- 如果需要可以增加季度
- 沒有考慮秒
- 聊天隊列會單獨使用秒定時器
- 數據解碼
- 協議ID
- 參數長度
- 是否壓縮
- 參數
- 關於緩存