1. 功能
  1. 客户端交互
  1. 客户端获取服务器列表客户端
  2. 获取公告
  1. CDN服务
  1. 资源包上传
  2. 更新资源
  1. 服务端交互
  1. 服务端验证登录
  2. 支付分发给服务端
  1. 数据配置
  2. GM功能
  3. 数据统计
  1. 技术
  1. 高级语言--Java8
  2. 框架--SpringBoot2.0
  3. 项目管理--Gradle
  4. 缓存--redis
  5. 数据库Mysql5.6
  6. 通信框架--Netty
  7. 传输框架--Protostuff
  1. 启动流程
  1. 不占用端口启动
  2. 通过注解获取协议进行初始化
  3. 初始化线程池用于有序处理客户端消息
  4. 初始化游戏数据
  5. 向后台获取游戏配置数据
  6. 初始化定时任务用于定时更新数据
  7. 启动Netty
  1. 关闭流程
  1. 实现接口ApplicationListener<ContextClosedEvent>
  2. 配置中注册监听
  3. 服务器关闭前处理临时数据落地到数据库
  1. 开发总结
  1. 关于缓存
  1. 缓存使用的redis
  2. 使用Jackson2JsonRedisSerializer替换了value的序列化与反序列化,但是对应map的序列化如果有排序要求,即使使用ConcurrentSkipListMap有序的集合,依然在反序列化的时候回出现顺序错误,在redis可视化工具看到的数据顺序是对的
  1. 关于成就设计
  1. 成就采用的是spring自带的事件系统
  2. 业务处理完回到数据完成后可以提交事件
  3. 在成就或任务系统中对事件进行处理
  1. 关于线程池
  1. 重写了拒绝策略
/**
     * 在线程池提交任务的最后一步——被线程池拒绝的任务,可以在拒绝后调用队列的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();
            }
        }
    }
  1. 关于业务初始化
  1. 使用了接口
/**
 * 初始化接口
 *
 * @date :2019/3/14 17:15
 */
public interface InitBaseHandler {
    /**
     * 实现此方法将在服务器启动时进行初始化操作
     */
    void init();
}
  1. 此接口可以在应用初始化时调用,将调用实现此接口的所有实现类
LogHandler.info("no2.初始化游戏数据......");
        applicationContext.getBeansOfType(InitBaseHandler.class).values().forEach(InitBaseHandler::init);
  1. 关于定时器
  1. 先使用的每分钟一次的定时器
/**
     * 定时更新
     */
    @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);
    }
  1. 后期会优化为扩展性更高的类似linux的cron月日时分定时器
  1. 没有考虑年
  2. 如果需要可以增加季度
  3. 没有考虑秒
  4. 聊天队列会单独使用秒定时器
  1. 数据解码
  1. 协议ID
  2. 参数长度
  3. 是否压缩
  4. 参数