Java8 Lambda表达应用 -- 单线程游戏server+异步数据库操作
前段时间我们游戏server升级到开发环境Java8,这些天,我再次server的线程模型再次设计了一下,耗费Lambda表情。
LambdaJava代码。特别是丑陋不堪的匿名内部类,这篇文章主要就是想和大家分享这一点。
线程模型
首先简介一下我们游戏server的线程模型。大致例如以下图所看到的:
Netty线程池仅仅处理消息的收发,当Netty收到消息之后。会交给游戏逻辑线程处理。因为是单线程在处理游戏逻辑,所以每个消息必须非常快处理完。也就是说,不能有数据库等耗时操作。不然逻辑线程非常可能会被卡住。为了不卡住逻辑线程,数据库操作由单独的线程池来处理。
逻辑线程发起数据库操作后便马上返回继续处理其它消息。数据库线程池处理完成后。再通知逻辑线程,从而达到了异步数据库操作的效果。
GameAction
Netty部分的网络代码,在收到消息后。会依据消息找到相应的Action,然后运行。
详细代码省略,以下是简化后的GameAction的代码:
public abstract class GameAction {
/**
* 在逻辑线程里处理消息.
* @param gs
* @param req 请求消息
*/
public void execute(GameSession gs, Object req) {
GameLogicExecutor.execute(() -> {
doExecute(gs, req);
});
}
// 子类实现
public abstract void doExecute(GameSession gs, Object req);
}
execute()方法里,使用了Lambda表达式来实现Runnable接口。
GameLogicExecutor
GameLogicExecutor是游戏逻辑运行线程,代码例如以下所看到的:
import java.util.concurrent.Executor;
import java.util.concurrent.Executors; /**
* 游戏逻辑线程.
*/
public class GameLogicExecutor { // todo 限制队列长度
private static final Executor executor = Executors.newSingleThreadExecutor(); /**
* 把游戏逻辑放到队列.
* @param gameLogic
*/
public static void execute(Runnable gameLogic) {
executor.execute(gameLogic);
} }
GetPlayerListAction
以下看一个详细的GameAction实现,这个Action依据用户ID返回用户创建的玩家列表。代码例如以下:
import java.util.List;
public class GetPlayerListAction extends GameAction {
@Override
public void doExecute(GameSession gs, Object req) {
int userId = (Integer) req;
PlayerDao.getPlayerList(userId, (List<Player> players) -> {
gs.write(players);
});
}
}
如果请求參数是玩家ID。doExecute()方法并没有等待数据库操作。而是立即就返回了。传递给DAO的回调对象是个Lambda表达式,在回调方法里,玩家列表通过GameSession被写到client(这仅仅是演示,实际的响应消息可能是protobuf或JSON)。
PlayerDao
import java.util.ArrayList;
import java.util.List; public class PlayerDao { public static void getPlayerList(int userId, DbOpCallback<List<Player>> cb) {
DbOpExecutor.execute(() -> {
try {
List<Player> players = getPlayerList(userId);
cb.ok(players);
} catch (Exception e) {
cb.fail(e);
}
});
} // 耗时的数据库操作
private static List<Player> getPlayerList(int userId) {
return new ArrayList<>();
} }
getPlayerList()方法接收两个參数。第一个參数是用户ID,第二个參数是个callback,当数据库操作完成(成功或失败)时。会通知这个callback。DAO内部使用的可能是JDBC或MyBatis等,总之是耗时的操作,由数据库线程池运行。
DbOpExecutor
DbOpExecutor的代码比較简单。和GameLogicExecutor相似,例如以下所看到的:
import java.util.concurrent.Executor;
import java.util.concurrent.Executors; /**
* 数据库操作线程池.
*/
public class DbOpExecutor { // todo 依据cpu数等确定线程数
private static final Executor executor = Executors.newFixedThreadPool(4); /**
* 把数据库操作放进队列.
* @param dbOp
*/
public static void execute(Runnable dbOp) {
executor.execute(dbOp);
} }
DbOpCallback
最后看一下DbOpCallback代码:
/**
* 数据库操作回调.
* @param <T>
*/
@FunctionalInterface
public interface DbOpCallback<T> { /**
* 处理数据库返回结果.
* @param result
*/
void handleResult(T result); /**
* 数据库操作正常结束.
* @param result
*/
default void ok(T result) {
// 在游戏逻辑线程里处理结果
GameLogicExecutor.execute(() -> {
try {
handleResult(result);
} catch (Exception e) {
// todo 处理异常
}
});
} /**
* 数据库操作出现异常.
* @param e
*/
default void fail(Exception e) {
// todo 处理异常
} }
@FunctionalInterface说明这是一个函数式接口,简单的说,就是仅仅有一个抽象方法的接口。接口的default方法是Java8的新语法,详细请參考Java8相关方面的资料。ok()方法确保数据库操作的结果是在逻辑线程里处理。
结论
上面的代码。在Java8之前用匿名内部类也是能够写的,仅仅是相比Lambda表达式,更加冗长丑陋而已。另外要注意,上面的代码仅仅是简化后的演示样例代码,并不是真实代码。
假上面的代码设想到自己的项目,还需要注意的是异常处理。
版权声明:本文博主原创文章,博客,未经同意不得转载。
Java8 Lambda表达应用 -- 单线程游戏server+异步数据库操作的更多相关文章
- SQL Server远程数据库操作(备份、还原等)
· SQL Server远程数据库备份到本地: exp sauser/sapassword@192.168.8.233:1433/DBName file=d:/backup.dmp OWNER=sum ...
- SQL SERVER C#数据库操作类(连接、执行SQL)
using System; using System.Collections; using System.Collections.Specialized; using System.Data; usi ...
- [C#对sql操作]C#对sql server 2008数据库操作
using System.Data; using System.Data.SqlClient SqlConnection conn = new SqlConnection(System.Configu ...
- ADO.NET操作SQL Server:数据库操作类(已封装)
1.增.删.改通用方法 /// <summary> /// 增.删.改通用方法 /// </summary> /// <param name="commandT ...
- ADO.NET操作SQL Server:数据库操作类(未封装)
1.添加数据 /// <summary> /// 添加数据 /// </summary> /// <param name="newEntity"> ...
- 在tornado中使用异步mysql操作
在使用tornado框架进行开发的过程中,发现tornado的mysql数据库操作并不是一步的,造成了所有用户行为的堵塞.tornado本身是一个异步的框架,要求所有的操作都应该是异步的,但是数据库这 ...
- JAVA8给我带了什么——lambda表达
这此年来我一直从事.NET的开发.对于JAVA我内心深处还是很向往的.当然这并不是说我不喜欢.NET.只是觉得JAVA也许才是笔者最后的归处.MK公司是以.NET起家的.而笔者也因为兄弟的原因转行.N ...
- 【Java学习笔记之三十一】详解Java8 lambda表达式
Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前 ...
- java8 快速入门 lambda表达式 Java8 lambda表达式10个示例
本文由 ImportNew - lemeilleur 翻译自 javarevisited.欢迎加入翻译小组.转载请见文末要求. Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发 ...
随机推荐
- Google Map API V2密钥申请
之前用的都是v1,用的是MapView,好吧,仅仅能认命了.废话不再多说,開始android 的Google Maps Android API v2吧 之前參考了http://www.cnblogs. ...
- Spring常见问题解决办法汇总
解决The prefix 'context' for element 'context:component-scan' is not bound<beans xmlns="http:/ ...
- ORACLE 11G没有备份文件參数文件在异机通过rman备份恢复找回被误删的数据
背景: 同事误删除线上数据.所以须要从备份中找回数据恢复. 真实屋漏偏逢连夜雨.船迟又遇打头风.前两天备份的磁盘坏块,如今仅仅有rman全备的.bak文件,没有控制文件和參数文件,所 ...
- 基于 Android 的 3D 视频示例代码
笔者:Mark Liu 下载样本代码 简单介绍 在Android 中,创建一个可以播放视频剪辑的应用很easy:创建一个採用 3D 图形平面的游戏应用也很easy.可是,创建一个可以在 3D 图形对象 ...
- 重新想象 Windows 8 Store Apps (27) - 选取器: 联系人选取窗口, 自定义联系人选取窗口
原文:重新想象 Windows 8 Store Apps (27) - 选取器: 联系人选取窗口, 自定义联系人选取窗口 [源码下载] 重新想象 Windows 8 Store Apps (27) - ...
- hdu 4308 Saving Princess claire_ BFS
为了准备算法考试刷的,想明确一点即可,全部的传送门相当于一个点,当遇到一个传送门的时候,把全部的传送门都压入队列进行搜索 贴代码: #include <iostream> #include ...
- POJ--2923--Relocation--如压力DP
Relocation Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 2288 Accepted: 950 Descrip ...
- pig询问top k,每个返回hour和ad_network_id最大的两个记录(SUBSTRING,order,COUNT_STAR,limit)
pig里面有一个TOP功能.我不知道为什么用不了.有时间去看看pig源代码. SET job.name 'top_k'; SET job.priority HIGH; --REGISTER piggy ...
- Android - 支持不同的设备 - 支持不同的屏幕
Android整体上按照两大类来分类设备屏幕:尺寸和分辨率.app在设备上运行应该考虑大小和分辨率.同样的,你应该包含不同的资源来让app适应不同的屏幕大小和分辨率. 整体上有四种尺寸:小的,正常的, ...
- iPhone发展【一】从HelloWorld开始
转载请注明出处.原文网址:http://blog.csdn.net/m_changgong/article/details/8013553 作者:张燕广 从经典的HelloWorld開始踏入iPhon ...