描述
- 在游戏服务器中,数据回写是指将内存中的数据回写更新到数据库(MongoDB),触发回写的情况通常有两种:
-
定时回写:每个一段时间将将数据刷新到数据库中
-
停服回写:在关闭游戏服务器时回写所有数据
设计方案
- 设计方案如下图,涉及到的接口和类概况:
- GameServer:游戏服务器
- PlayerService:玩家数据业务
- Player:玩家信息 Bean
- IFlushTimer:定时回写接口,Bean 类需实现该接口的两个方法
- delay():返回定时回写时间
- run():执行定时回写操作
- 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;
}
}
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 .....");
}
}
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();
}
}
网友评论