描述
在游戏服务器中,数据回写是指将内存中的数据回写更新到数据库(MongoDB),触发回写的情况通常有两种:
定时回写:每个一段时间将将数据刷新到数据库中
停服回写:在关闭游戏服务器时回写所有数据
设计方案
设计方案如下图,涉及到的接口和类概况:
delay():返回定时回写时间
run():执行定时回写操作
GameServer:游戏服务器
PlayerService:玩家数据业务
Player:玩家信息 Bean
IFlushTimer:定时回写接口,Bean 类需实现该接口的两个方法
DBFlushTimer:定时回写工具类,通过 ScheduledExecutorService 实现定时回写
数据回写
代码案例
定义 IFlushTimer 接口,继承自 Runnable
public interface IFlushTimer extends Runnable { int delay();
}Bean 类实现 IFlushTimer 接口的 delay()、run() 方法
public class Player implements IFlushTimer { private int id; private String name; private ScheduledFuture<?> db_sf = null; public Player(int id, String name) { this.id = id; this.name = name;
} @Override
public int delay() { return 5;
} @Override
public void run() { this.flush();
} /**
* 启动定时回写任务
*/
public void startup() {
db_sf = DBFlushTimer.add(this);
} /**
* 模拟数据库回写
*/
private void flush() {
System.out.println("id:" + this.id + "回写中..."); // 模拟回写操作延迟
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
} /**
* 清除定时回写并回写数据
*/
public void clear() { synchronized (db_sf) {
db_sf.cancel(false);
db_sf = null;
}
runClose();
} /**
* 回写数据并执行其他操作
*/
public void runClose() { this.flush();
} public int getId() { return id;
} public void setId(int id) { this.id = id;
} public String getName() { return name;
} public void setName(String name) { this.name = name;
}
}编写定时回写工具类 DBFlushTimer,需要注意的是这里使用的是 scheduleWithFixedDelay(...) 方法,ScheduledExecutorService 有两个方法:
scheduleAtFixedRate(...):固定每隔多少秒执行一次任务
scheduleWithFixedDelay(...):当前任务结束的时才开始结算间隔时间,如 0 秒开始执行第一次任务,任务耗时 5 秒,任务间隔时间 3 秒,那么第二次任务执行的时间是在第 8 秒开始
public class DBFlushTimer { private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); public static ScheduledFuture add(IFlushTimer timer){
ScheduledFuture scheduledFuture = scheduler.scheduleWithFixedDelay(timer,(long)timer.delay(),(long)timer.delay(),TimeUnit.SECONDS); return scheduledFuture;
}
}编写业务类 PlayerService
public class PlayerService {
public static final ConcurrentHashMap<Integer, Player> playerId2Player = new ConcurrentHashMap<Integer, Player>(); /**
* 开服初始化,模拟从数据库加载玩家信息
*/
public static void init() {
Player p1 = new Player(1, "p1");
Player p2 = new Player(2, "p2");
PlayerService.playerId2Player.put(p1.getId(), p1);
PlayerService.playerId2Player.put(p2.getId(), p2); // 启动定时回写
p1.startup();
p2.startup();
} /**
* 停服回写所有数据
*/
public static void flushToDB(){
System.out.println("flush player data to db ....."); for (Player player : playerId2Player.values()){
player.clear();
}
System.out.println("flush tong data end .....");
}
}编写 GameServer
public class GameServer { public static void main(String[] args)throws Exception {
init();
TimeUnit.SECONDS.sleep(12);
stop();
} public static void init(){
System.out.println("init game server .....");
PlayerService.init();
} // 关服
public static void stop(){
System.out.println("stop game server .....");
PlayerService.flushToDB();
}
}
作者:林塬
链接:https://www.jianshu.com/p/42e77dd614a9