手游服务端框架之每日重置逻辑
2017-09-17 19:38
211 查看
实现背景
该系列打了这么久的地基,还没写过业务代码。实际上我是这么考虑的,因为不同游戏项目的业务代码差异性非常大,业务逻辑只有你想不到,没有策划大大想不到的。要想做一个拓展性强的框架,按理说业务代码越少越好。但再三考虑,还是要尽量构造出一些游戏公共的业务,也算是这套框架的示例代码吧。玩过手机游戏的人应该知道,不管是什么游戏,都会经常出现每日重置的逻辑。比如,玩家打副本每日有一个上限次数,玩家购买某种商品也会有每日购买最大次数……这些业务有个特点,就是每天有上限,隔天次数会被重置。至于隔天是0点重置,还是5点重置,就要看项目的运营更新时间了。
每日重置业务的注意事项
每日重置的逻辑看似简单,要完美实现还是要花点功夫的。因为每日重置有这样的特点:1. 在家玩家和离线玩家的处理方式是不同的;
2. 每日重置的时候要考虑线程安全,避免出现并发问题。
针对第一个问题,我们的处理方式是,处理在家玩家,我们通过遍历在线玩家列表,依次执行重置业务。而离线玩家就比较麻烦,我们不可能把所有不在线的玩家从数据库捞出来对其处理。实际上,我们可以在玩家登录的时候处理,玩家每天登录的时候,我们将玩家身上的重置标识与系统公共的重置时间进行比较,若不相同,则可以执行重置业务。
针对第二个问题,考虑线程并发问题,就会跟系统所使用的异步线程模型相结合。由于我们的线程模型是根据玩家的分发id作负载均衡的。所以,每日重置也应该与其相适应。
quartz在游戏中的应用
quartz是一个非常优秀的作业调度框架。在游戏开发中,我们经常用quartz来实现定点任务和频率任务。使用quartz只需要引入相关的jar包,然后使用配置文件对任务触发进行配置。
每日重置业务实现
1. 首先,我们在当前线程模型的基础上,定义一种timer事件。该事件需要满足,对玩家业务的执行来说是线程安全的。同时,为了拓展性,该事件可以设置timer的执行次数。要达到线程安全,只要让timer事件继承自AbstractDistributeTask就可以了。具体原因,可参考该系列关于线程模型的文章手游服务端框架之消息线程模型/** * timer任务 * @author kingston */ public abstract class TimerTask extends AbstractDistributeTask { private int currLoop; /** 小于0表示无限任务 */ private int maxLoop; public TimerTask(int distributeKey) { this(distributeKey, 1); } public TimerTask(int distributeKey, int maxLoop) { this.distributeKey = distributeKey; this.maxLoop = maxLoop; } public void updateLoopTimes() { this.currLoop += 1; } public boolean canRunAgain() { if (this.maxLoop <= 0) { return true; } return this.currLoop < this.maxLoop; } }
2. 玩家每日重置timer事件(DailyResetTask),只要继承上面的TimerTask就可以了
public class DailyResetTask extends TimerTask { private Player player; public DailyResetTask(int distributeKey, Player player) { super(distributeKey); this.player = player; } @Override public void action() { System.err.println("玩家"+player.getName()+"进行每日重置"); PlayerManager.getInstance().checkDailyReset(player); } }
3.编写quartz作业。假设我们每日重置的时间发生在每天5点,那么我们可以在jobs.xml里加上这样的配置
<!-- 每日重置 --> <job> <name>DailyResetJob</name> <group>DEFAULT</group> <job-class>com.kingston.game.cronjob.DailyResetJob</job-class> </job> <trigger> <cron> <name>DailyResetJobTrigger</name> <group>DEFAULT</group> <job-name>DailyResetJob</job-name> <job-group>DEFAULT</job-group> <cron-expression>0 0 5 * * ?</cron-expression> <!-- 每天05:00运行 --> </cron> </trigger>
4. quartz在调试的时候,肯定是在自己的线程上跑的。重置job触发的时候,我们遍历所有玩家,对每个玩家执行每日重置业务。这个过程中,我们需要将触发点封装成timer事件,丢到主业务线程里处理。这些操作在DailyResetJob上实现。
/** * 每日5点定时job * @author kingston */ @DisallowConcurrentExecution public class DailyResetJob implements Job { private Logger logger = LoggerSystem.CRON_JOB.getLogger(); @Override public void execute(JobExecutionContext arg0) throws JobExecutionException { logger.info("每日5点定时任务开始"); long now = System.currentTimeMillis(); SystemParameters.update("dailyResetTimestamp", now); Collection<Player> onlines = PlayerManager.getInstance().getOnlinePlayers().values(); for (Player player:onlines) { int distributeKey = player.distributeKey(); //将事件封装成timer任务,丢回业务线程处理 TaskHandlerContext.INSTANCE.acceptTask(new DailyResetTask(distributeKey, player)); } } }
5. 对于离线玩家的处理,我们需要将当次重置的时刻记录在一张公共的系统表(systemrecord)。在DailyResetJob里的
SystemParameters.update("dailyResetTimestamp", now);玩家登录的时候检查一下,在LoginManager.handlSelectPlayer()方法。登录逻辑发生在玩家发出登录消息,本来就在架构里的线程模型,本身就是线程安全的了。
/** * 选角登录 * @param session * @param playerId */ public void handleSelectPlayer(IoSession session, long playerId) { Player player = PlayerManager.getInstance().get(playerId); if (player != null) { //绑定session与玩家id session.setAttribute(SessionProperties.PLAYER_ID, playerId); //加入在线列表 PlayerManager.getInstance().add2Online(player); SessionManager.INSTANCE.registerNewPlayer(playerId, session); //推送进入场景 ResPlayerEnterSceneMessage response = new ResPlayerEnterSceneMessage(); response.setMapId(1001); MessagePusher.pushMessage(session, response); //检查日重置 PlayerManager.getInstance().checkDailyReset(player); } }
至此,游戏的每日重置业务的介绍到这里就结束啦。
手游服务端开源框架系列完整的代码请移步github ->> jforgame
相关文章推荐
- 手游服务端框架之模仿SpringMvc处理玩家请求
- 手游服务端框架之消息线程模型
- 手游服务端框架之配置与玩家数据库设计
- 手游服务端框架之配置与玩家数据库设计
- 手游服务端框架之客户端协议组合下发
- 完美世界手游《圣斗士星矢》lua逻辑框架分析
- 手游服务端框架之使用Redis实现跨服排行榜
- 手游服务端框架之使用Guava构建缓存系统
- 手游服务端框架之GM金手指的设计
- 手游服务端框架之使用事件驱动模型解决业务高耦合
- 手游服务端框架之网关
- 手游服务端框架之后台管理工具
- 游戏服务端逻辑模块处理框架
- TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端)、UDP客户端
- 手游页游和端游的服务端的架构与区别
- mina 框架java服务端的搭建和通信。
- Spring MVC测试框架详解——服务端测试
- thrift框架搭建的php服务端/客户端代码
- 关于Remoting 中服务端逻辑操作方法与客户端分离
- Delphi 三层框架开发 服务端开发