zoukankan      html  css  js  c++  java
  • java--游戏后端--项目开发总结--服务端

    1. 功能
      1. 客户端交互
        1. 客户端获取服务器列表客户端
        2. 获取公告
      2. CDN服务
        1. 资源包上传
        2. 更新资源
      3. 服务端交互
        1. 服务端验证登录
        2. 支付分发给服务端
      4. 数据配置
      5. GM功能
      6. 数据统计
    2. 技术
      1. 高级语言--Java8
      2. 框架--SpringBoot2.0
      3. 项目管理--Gradle
      4. 缓存--redis
      5. 数据库Mysql5.6
      6. 通信框架--Netty
      7. 传输框架--Protostuff
    3. 启动流程
      1. 不占用端口启动
      2. 通过注解获取协议进行初始化
      3. 初始化线程池用于有序处理客户端消息
      4. 初始化游戏数据
      5. 向后台获取游戏配置数据
      6. 初始化定时任务用于定时更新数据
      7. 启动Netty
    4. 关闭流程
      1. 实现接口ApplicationListener<ContextClosedEvent>
      2. 配置中注册监听
      3. 服务器关闭前处理临时数据落地到数据库
    5. 开发总结
      1. 关于缓存
        1. 缓存使用的redis
        2. 使用Jackson2JsonRedisSerializer替换了value的序列化与反序列化,但是对应map的序列化如果有排序要求,即使使用ConcurrentSkipListMap有序的集合,依然在反序列化的时候回出现顺序错误,在redis可视化工具看到的数据顺序是对的
      2. 关于成就设计
        1. 成就采用的是spring自带的事件系统
        2. 业务处理完回到数据完成后可以提交事件
        3. 在成就或任务系统中对事件进行处理
      3. 关于线程池
        1. 重写了拒绝策略
        2. /**
               * 在线程池提交任务的最后一步——被线程池拒绝的任务,可以在拒绝后调用队列的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();
                      }
                  }
              }
      4. 关于业务初始化
        1. 使用了接口
        2. /**
           * 初始化接口
           *
           * @date :2019/3/14 17:15
           */
          public interface InitBaseHandler {
              /**
               * 实现此方法将在服务器启动时进行初始化操作
               */
              void init();
          }
        3. 此接口可以在应用初始化时调用,将调用实现此接口的所有实现类
        4.         LogHandler.info("no2.初始化游戏数据......");
                  applicationContext.getBeansOfType(InitBaseHandler.class).values().forEach(InitBaseHandler::init);
      5. 关于定时器
        1. 先使用的每分钟一次的定时器
        2. /**
               * 定时更新
               */
              @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);
              }
        3. 后期会优化为扩展性更高的类似linux的cron月日时分定时器
          1. 没有考虑年
          2. 如果需要可以增加季度
          3. 没有考虑秒
          4. 聊天队列会单独使用秒定时器
      6. 数据解码
        1. 协议ID
        2. 参数长度
        3. 是否压缩
        4. 参数
  • 相关阅读:
    允许 使用接口传递对象,为什么?
    一道猫和老鼠吵醒主人的笔试题(C#)
    随心所欲操作Enum枚举类型
    SmartPhone 2003 手机编程实战之一:简单上手 2005年01月08日
    SmartPhone 2003 手机编程实战之二:自己开发一个天气预报服务 2005116
    QQ是危险的、MSN是危险的,所有即时通讯都是危险的
    PWN通用技巧
    Jarvis Oj Pwn 学习笔记level2
    Jarvis Oj Pwn 学习笔记Tell Me Something
    Jarvis Oj Pwn 学习笔记level1
  • 原文地址:https://www.cnblogs.com/plxz/p/11040516.html
Copyright © 2011-2022 走看看