diff --git a/README.md b/README.md index 7d06c88f..887e1c16 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ public void cronSchedulerPerSecond() { } ```` -#### 8. [storage](storage/src/test/java/com/zfoo/storage/ApplicationTest.java) excel和Java类自动映射框架,无需代码和任何工具只需要定义一个和excel对应的类,直接解析excel +#### 8. [storage](storage/src/test/java/com/zfoo/storage/ApplicationTest.java) Excel和Java类自动映射框架,只需要定义一个和Excel对应的类,直接解析Excel ``` @Resource diff --git a/doc/FAQ.md b/doc/FAQ.md index 1c4f1e62..aaa3c367 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -16,6 +16,14 @@ - 并不是所有编程语言都有interface的,你用了interface的话其它编程语言就没法用了,就无法跨平台了 - 基于packet的容易扩展,可以轻易跨平台 +--- + +#### 为什么选择mongodb + +- mongodb可以单机部署也可以分布式部署,是万能的数据库,而且速度足够快 +- mongodb,副本集部署(主从模式),可以数据同步,读写分离 +- mongodb,分片部署,故障转移(容灾) + #### TODO: - 参看一下akka的actor实现,进一步优化net的线程模型,统一异步回调的argument参数 diff --git a/doc/image/event/device01.png b/doc/image/event/device01.png new file mode 100644 index 00000000..6fea042b Binary files /dev/null and b/doc/image/event/device01.png differ diff --git a/doc/image/event/device02.png b/doc/image/event/device02.png new file mode 100644 index 00000000..20d5ac77 Binary files /dev/null and b/doc/image/event/device02.png differ diff --git a/doc/image/event/device03.png b/doc/image/event/device03.png new file mode 100644 index 00000000..c4e80a3d Binary files /dev/null and b/doc/image/event/device03.png differ diff --git a/doc/image/event/device04.png b/doc/image/event/device04.png new file mode 100644 index 00000000..4b4ae2ad Binary files /dev/null and b/doc/image/event/device04.png differ diff --git a/doc/image/event/device05.png b/doc/image/event/device05.png new file mode 100644 index 00000000..006739fa Binary files /dev/null and b/doc/image/event/device05.png differ diff --git a/doc/image/event/device06.png b/doc/image/event/device06.png new file mode 100644 index 00000000..e213a30e Binary files /dev/null and b/doc/image/event/device06.png differ diff --git a/doc/image/event/device07.png b/doc/image/event/device07.png new file mode 100644 index 00000000..5e4106d9 Binary files /dev/null and b/doc/image/event/device07.png differ diff --git a/doc/image/event/event01.png b/doc/image/event/event01.png new file mode 100644 index 00000000..eb6da0a8 Binary files /dev/null and b/doc/image/event/event01.png differ diff --git a/doc/image/event/event02.png b/doc/image/event/event02.png new file mode 100644 index 00000000..62300e92 Binary files /dev/null and b/doc/image/event/event02.png differ diff --git a/doc/image/monitor/monitor01.png b/doc/image/monitor/monitor01.png new file mode 100644 index 00000000..ea32d119 Binary files /dev/null and b/doc/image/monitor/monitor01.png differ diff --git a/doc/image/net/framework01.png b/doc/image/net/framework01.png new file mode 100644 index 00000000..f55b035b Binary files /dev/null and b/doc/image/net/framework01.png differ diff --git a/doc/image/net/framework02.png b/doc/image/net/framework02.png new file mode 100644 index 00000000..b3a5797a Binary files /dev/null and b/doc/image/net/framework02.png differ diff --git a/doc/image/net/framework03.png b/doc/image/net/framework03.png new file mode 100644 index 00000000..c3e79130 Binary files /dev/null and b/doc/image/net/framework03.png differ diff --git a/doc/image/net/framework04.png b/doc/image/net/framework04.png new file mode 100644 index 00000000..2930a744 Binary files /dev/null and b/doc/image/net/framework04.png differ diff --git a/doc/image/net/framework05.png b/doc/image/net/framework05.png new file mode 100644 index 00000000..01636d7c Binary files /dev/null and b/doc/image/net/framework05.png differ diff --git a/doc/image/net/framework06.png b/doc/image/net/framework06.png new file mode 100644 index 00000000..8aa9087c Binary files /dev/null and b/doc/image/net/framework06.png differ diff --git a/doc/image/net/framework07.png b/doc/image/net/framework07.png new file mode 100644 index 00000000..0950d49a Binary files /dev/null and b/doc/image/net/framework07.png differ diff --git a/doc/image/net/framework08.png b/doc/image/net/framework08.png new file mode 100644 index 00000000..e44df647 Binary files /dev/null and b/doc/image/net/framework08.png differ diff --git a/doc/image/net/framework09.png b/doc/image/net/framework09.png new file mode 100644 index 00000000..54120701 Binary files /dev/null and b/doc/image/net/framework09.png differ diff --git a/doc/image/net/rpc01.png b/doc/image/net/rpc01.png new file mode 100644 index 00000000..8f979580 Binary files /dev/null and b/doc/image/net/rpc01.png differ diff --git a/doc/image/scheduler/scheduler01.png b/doc/image/scheduler/scheduler01.png new file mode 100644 index 00000000..d04fbf2a Binary files /dev/null and b/doc/image/scheduler/scheduler01.png differ diff --git a/doc/image/storage/storage01.png b/doc/image/storage/storage01.png new file mode 100644 index 00000000..9eeb9a3b Binary files /dev/null and b/doc/image/storage/storage01.png differ diff --git a/doc/image/storage/storage02.png b/doc/image/storage/storage02.png new file mode 100644 index 00000000..1cdbc168 Binary files /dev/null and b/doc/image/storage/storage02.png differ diff --git a/event/README.md b/event/README.md new file mode 100644 index 00000000..fd3e84a1 --- /dev/null +++ b/event/README.md @@ -0,0 +1,44 @@ +### Ⅰ. 简介 + +- [event](https://github.com/zfoo-project/zfoo/blob/main/event/README.md) 事件总线解耦不同模块,提高代码的质量,核心思想是观察者设计模式 + +### Ⅱ. 硬件层面的事件总线 + +#### 1. 设备间的通信,直连的方式 + +CPU所代表的控制器和运算器,要和存储器,也就是我们的主内存,以及输入和输出设备进行通信。 计算机是用什么样的方式来完成,CPU和内存、以及外部输入输出设备的通信呢? + +比如我在键盘上输入一个快捷键,来控制我的音响播放音乐,计算机是用什么样的方式来完成,外部输入输出设备的通信呢? + +![Image text](../doc/image/event/device01.png) +![Image text](../doc/image/event/device02.png) + +#### 2. 通过总线降低复杂度 + +![Image text](../doc/image/event/device03.png) +![Image text](../doc/image/event/device04.png) +![Image text](../doc/image/event/device05.png) + +#### 3. 硬件总线的多重架构 + +![Image text](../doc/image/event/device06.png) +![Image text](../doc/image/event/device07.png) + +总线的设计思路,核心是为了减少多个模块之间交互的复杂性和耦合度。 + +在实际的硬件层面,总线其实就是一组连接电路的线路。因为不同设备之间的速度有差异,所以一台计算机里面往往会有多个总线。 + +实际上,总线这个设计思路在我们的软件开发过程中也经常会被用到。事件总线就是我们常见的一个设计模式,通常事件总线也会和订阅者发布者模式结合起来,成为大型系统的各个松耦合的模块之间交互的一种主要模式。 + +### Ⅲ. 软件层面的事件总线 + +![Image text](../doc/image/event/event01.png) +![Image text](../doc/image/event/event02.png) + +- 事件总线从设计模式上说叫观察者模式 +- 设计模式没有固定的写法,zfoo event用最简单的注解实现了事件总线 +- zfoo event使用Javassist字节码增强技术动态代理事件接收者,避免了反射,没有性能损耗 + +### Ⅳ. 参考文章 + +- [深入浅出计算机组成原理学习笔记:第四十二讲](https://www.cnblogs.com/luoahong/p/11359768.html) diff --git a/hotswap/README.md b/hotswap/README.md new file mode 100644 index 00000000..eba49ad8 --- /dev/null +++ b/hotswap/README.md @@ -0,0 +1,18 @@ +### Ⅰ. 简介 + +- [hotswap](https://github.com/zfoo-project/zfoo/blob/main/hotswap/README.md) 热更新代码,不需要停止服务器,不需要额外的任何配置,一行代码开启热更新 + +### Ⅱ. 单机热更新 + +- 直接传入需要热更新的class文件 + ``` + // 传入需要更新的class文件 + HotSwapUtils.hotswapClass(bytes); + ``` + +### Ⅲ. 分布式热更新 + +- 将需要热更新的class文件上传到zookeeper注册中心中,其它服务器监听需要更新的class文件, 中有分布式热更新实现 + +- [tank](https://github.com/zfoo-project/tank-game-server/blob/main/common/src/main/java/com/zfoo/tank/common/util/HotUtils.java) + 分布式热更新代码实现 diff --git a/monitor/README.md b/monitor/README.md new file mode 100644 index 00000000..651ff0f4 --- /dev/null +++ b/monitor/README.md @@ -0,0 +1,48 @@ +### Ⅰ. 简介 + +- [monitor](https://github.com/zfoo-project/zfoo/blob/main/storage/README.md) 轻量级cpu,内存,硬盘,网络监控 + +### Ⅱ. 使用 + +- uptime,仿Linux的uptime指令,可以用来监控cpu的负载 + ``` + UptimeVO uptime = OSUtils.uptime(); + ``` + +- df,仿Linux的df指令,可以用来监控硬盘容量 + ``` + List df = OSUtils.df(); + ``` + + +- free,仿Linux的free指令,可以用来监控内存占用 + ``` + MemoryVO free = OSUtils.free(); + ``` + + +- sar,仿Linux的sar指令,可以用来监控网络IO + ``` + List sar = OSUtils.sar(); + ``` + +### Ⅲ. 传统的服务器监控 + +- 传统的单机服务器监控,主要看cpu,内存,硬盘,网络 + ![Image text](../doc/image/monitor/monitor01.png) + +- 分布式服务器监控,主要是监控多台服务器,和单机监控差别不大,唯一不同就是服务器变多了 + ``` + 自己写一个分布式服务器监控非常简单,只需要每隔一秒钟或者一分钟去采集到当前服务器的主要数据,上传到数据库或者其他服务器上就行了。 + 通过这些被采集到的数据,去分析性能,去做监控警报,做折线图或者其他可视化图表。 + monitor通过静态类封装可以轻易采集到,然后实现自己的分布式监控程序,而且可以完全内嵌在Java程序中,不需要额外的部署 + ``` + +### Ⅲ. 现在的服务器监控 + +- 现在服务器是容器的时代,服务器监控也从对服务器的监控转变为对容器的监控 +- 容器监控几乎是容器自带的一个集成功能了,甚至云厂商还会免费提供监控程序和监控服务 + +### Ⅳ. 我们能做的服务器监控 + +- 自己定制的监控才是符合自己项目的监控,比如自己实现一个监控rpc接口被调用次数的这种监控 diff --git a/monitor/src/test/java/com/zfoo/monitor/ApplicationTest.java b/monitor/src/test/java/com/zfoo/monitor/ApplicationTest.java index d1b3fdcd..bf4fbf34 100644 --- a/monitor/src/test/java/com/zfoo/monitor/ApplicationTest.java +++ b/monitor/src/test/java/com/zfoo/monitor/ApplicationTest.java @@ -28,7 +28,7 @@ import oshi.SystemInfo; public class ApplicationTest { /** - * 仿Linux的uptime指令,可以用来监控cpu的负载 + * 仿Linux的uptime指令,可以用来监控cpu的负载,windows中无法统计,所以在windows返回的结果默认是-1 */ @Test public void uptimeTest() { diff --git a/net/README.md b/net/README.md index 3be1b4f3..ef44c242 100644 --- a/net/README.md +++ b/net/README.md @@ -1,5 +1,7 @@ ### Ⅰ. 简介 +- [storage](https://github.com/zfoo-project/zfoo/blob/main/net/README.md) 天生异步,Actor设计思想,无锁化设计,基于Spring的MVC式用法的万能RPC框架 + 1. 优雅的同步和异步请求,速度更快,支持 tcp udp websocket 2. 服务注册和发现,配置中心使用的是zookeeper,可扩展成其它注册中心 3. 自带高性能网关,自定义转发策略 @@ -9,8 +11,8 @@ ### Ⅱ. 相关模块 - net提供了两层api接口 - - IRouter为底层网络接口,直接通过Session连接发送消息 - - IConsumer为高层网络接口,是对IRouter的封装,消费者向服务提供者发送RPC消息 + - IRouter(low level api)为底层网络接口,直接通过Session连接发送消息 + - IConsumer(high level api)为高层网络接口,是对IRouter的封装,消费者向服务提供者发送RPC消息 ``` 极致的高性能,高可用性,高伸缩性(一般指增加机器),高扩展性(一般指代码层面的开闭原则) @@ -25,6 +27,10 @@ session,对netty的channel的封装 task,任务线程池,任务的分发 ``` +- 网络通信规范 + - 客户端对服务器的请求以Request结尾,返回以Response结尾 + - 服务器内部之间的调用以Ask结尾,返回以Answer结尾。 + #### 诞生背景 ``` @@ -40,6 +46,8 @@ Java生态的rpc框架非常多,但是很少有rpc框架能够契合到游戏 ### Ⅱ. 服务器架构图 +![Image text](../doc/image/net/rpc01.png) + 服务器架构图
### Ⅲ. 为什么快 @@ -50,16 +58,73 @@ Java生态的rpc框架非常多,但是很少有rpc框架能够契合到游戏 - rpc调用天生异步支持,并且保证rpc异步调用结束过后在同一条线程去执行,类似于actor的设计思想,特别适合对性能有极高需求的场景 - 使用MVC设计模式,规范开发,保证代码质量,高效执行 -### Ⅳ. 网络通信规范 +### Ⅳ. 服务器架构演进 -- 客户端对服务器的请求以Request结尾,返回以Response结尾 -- 服务器内部之间的调用以Ask结尾,返回以Answer结尾。 +- 单台服务器+数据库 + ![Image text](../doc/image/net/framework01.png) + +- 添加反向代理,类似网关 + ![Image text](../doc/image/net/framework02.png) + ``` + 更大规模的架构做准备的第一步是添加一个“反向代理” + 路由将请求转发到正确的端点 + 健康检查 + 身份验证确保实际允许用户访问服务器 + 防火墙确保用户只能访问我们允许他们使用的部分网络 + ``` + +- 引入负载均衡器 + ![Image text](../doc/image/net/framework03.png) + ``` + 负载均衡器的把请求分发到两台服务器上。用户 1 往左,用户 2 往右,用户 3 再往左。 + ``` + +- 扩展数据库 + ![Image text](../doc/image/net/framework04.png) + ``` + 集成的orm基于mongodb,可以支持分布式部署 + mongodb,副本集部署(主从模式),可以数据同步,读写分离, + mongodb,分片部署,故障转移(容灾) + ``` + +- 微服务,随着开发团队随着应用的发展而增长,越来越多的开发人员工作在同一台服务器上,发生冲突的可能性很大 + ![Image text](../doc/image/net/framework05.png) + ``` + 微服务可以单独扩展,更好地适应需求。开发团队之间相互独立,每个团队都负责自己的微服务生命周期 + 每个微服务都有自己的资源,比如数据库 + ``` + +- 缓存和内容分发网络(CDN) + ![Image text](../doc/image/net/framework06.png) + ``` + 网络应用的很大一部由静态资源构成,如图片、CSS 样式文件、Java 脚本以及一些针对特定产品提前渲染好的页面等等。 + 游戏应用也包含了大量的图片资源,3D建模的资源 + 缓存的加强版叫内容分发网络(Content Delivery Network),遍布全球的大量缓存。 + 这使得用户可以从物理上靠近他们的地方来获取网页内容,而不是每次都把数据从源头搬到用户那里。 + ``` + +- 消息队列 + ![Image text](../doc/image/net/framework07.png) + ``` + 流量消峰,有时需要处理大量比如双11抢购秒杀,让突然的大量请求排队去处理任务 + 应用解耦 + 消息分发 + ``` + +- 分片,分区,比如将用户分配到与其最近的服务器提供服务 + ![Image text](../doc/image/net/framework08.png) + ``` + 比如,两地三中心,容灾备份 + ``` + +- 对负载均衡器进行负载均衡 + ![Image text](../doc/image/net/framework09.png) ### Ⅴ. 教程 -- [单机服务器教程](src/test/java/com/zfoo/net/core/tcp/server/TcpServerTest.java) -- [RPC教程](src/test/java/com/zfoo/net/core/provider/ProviderTest.java) +- [单台服务器教程](src/test/java/com/zfoo/net/core/tcp/server/TcpServerTest.java) - [网关教程](src/test/java/com/zfoo/net/core/gateway/GatewayTest.java) +- [微服务RPC教程](src/test/java/com/zfoo/net/core/provider/ProviderTest.java) ### Ⅵ. 依赖的中间件zookeeper教程 @@ -71,3 +136,6 @@ Java生态的rpc框架非常多,但是很少有rpc框架能够契合到游戏 - [数据发布和订阅](src/test/java/com/zfoo/net/zookeeper/recipes/nodecache) - [zookeeper的分布式选举算法](src/test/java/com/zfoo/net/zookeeper/recipes/mastersel) +### Ⅶ. 参考文章 + +- [Scaling webapps for newbs](https://arcentry.com/blog/scaling-webapps-for-newbs-and-non-techies/) diff --git a/net/src/main/java/com/zfoo/net/packet/service/PacketService.java b/net/src/main/java/com/zfoo/net/packet/service/PacketService.java index 7a33e6f0..3edb6483 100644 --- a/net/src/main/java/com/zfoo/net/packet/service/PacketService.java +++ b/net/src/main/java/com/zfoo/net/packet/service/PacketService.java @@ -16,7 +16,7 @@ package com.zfoo.net.packet.service; import com.zfoo.net.NetContext; import com.zfoo.net.packet.model.DecodedPacketInfo; import com.zfoo.net.router.attachment.IAttachment; -import com.zfoo.net.router.route.PacketRoute; +import com.zfoo.net.router.route.PacketBus; import com.zfoo.protocol.IPacket; import com.zfoo.protocol.ProtocolManager; import com.zfoo.protocol.buffer.ByteBufUtils; @@ -121,7 +121,7 @@ public class PacketService implements IPacketService { var beanNames = applicationContext.getBeanDefinitionNames(); for (var beanName : beanNames) { var bean = applicationContext.getBean(beanName); - PacketRoute.registerPacketReceiverDefinition(bean); + PacketBus.registerPacketReceiverDefinition(bean); } } diff --git a/net/src/main/java/com/zfoo/net/router/Router.java b/net/src/main/java/com/zfoo/net/router/Router.java index 82b0e680..624775ff 100644 --- a/net/src/main/java/com/zfoo/net/router/Router.java +++ b/net/src/main/java/com/zfoo/net/router/Router.java @@ -29,7 +29,7 @@ import com.zfoo.net.router.attachment.SignalAttachment; import com.zfoo.net.router.exception.ErrorResponseException; import com.zfoo.net.router.exception.NetTimeOutException; import com.zfoo.net.router.exception.UnexpectedProtocolException; -import com.zfoo.net.router.route.PacketRoute; +import com.zfoo.net.router.route.PacketBus; import com.zfoo.net.router.route.SignalBridge; import com.zfoo.net.session.model.AttributeType; import com.zfoo.net.session.model.Session; @@ -303,7 +303,7 @@ public class Router implements IRouter { } // 调用PacketReceiver - PacketRoute.submit(session, packet, attachment); + PacketBus.submit(session, packet, attachment); } catch (Exception e) { logger.error(StringUtils.format("e[uid:{}][sid:{}]未知exception异常", session.getAttribute(AttributeType.UID), session.getSid(), e.getMessage()), e); } catch (Throwable t) { diff --git a/net/src/main/java/com/zfoo/net/router/route/PacketRoute.java b/net/src/main/java/com/zfoo/net/router/route/PacketBus.java similarity index 99% rename from net/src/main/java/com/zfoo/net/router/route/PacketRoute.java rename to net/src/main/java/com/zfoo/net/router/route/PacketBus.java index 1f272a65..01382e8c 100644 --- a/net/src/main/java/com/zfoo/net/router/route/PacketRoute.java +++ b/net/src/main/java/com/zfoo/net/router/route/PacketBus.java @@ -43,9 +43,9 @@ import java.lang.reflect.Modifier; * @author jaysunxiao * @version 3.0 */ -public abstract class PacketRoute { +public abstract class PacketBus { - private static final Logger logger = LoggerFactory.getLogger(PacketRoute.class); + private static final Logger logger = LoggerFactory.getLogger(PacketBus.class); /** * 客户端和服务端都有接受packet的方法,packetReceiverList对应的就是包的接收方法,将receiver注册到IProtocolRegistration diff --git a/orm/README.md b/orm/README.md index ef0642a4..4ca66907 100644 --- a/orm/README.md +++ b/orm/README.md @@ -1,20 +1,9 @@ ### Ⅰ. 简介 -- 基于MongoDB的orm框架,提供POJO对象和MongoDB数据库之间的映射 +- [orm](https://github.com/zfoo-project/zfoo/blob/main/orm/README.md) 基于MongoDB的orm框架,提供POJO对象和MongoDB数据库之间的映射 +- mongodb是分布式数据库,可以单机使用,也可以分布式使用 -### Ⅱ. 注意事项 - -- POJO对象的属性必须提供get和set方法,否则无法映射 -- 不支持泛型 -- 不支持循环引用的对象 -- 如果不想映射某属性,直接加上transient关键字 -- 目前支持基本数据属性(byte,short,int,long,float,double,boolean),字符串String,List,Set集合属性的映射,不支持Map -- 数据库主键能用整数尽量用整数,因为MongoDB默认的主键是一个字符串,比较占空间 -- 数据库使用自研的orm框架,比如一个实体类UserEntity,映射到数据库中的集合为user,首字母小写,去掉Entity -- 基于 [caffeine](https://github.com/ben-manes/caffeine) 的高性能数据缓存 -- 智能语法分析,错误的entity对象定义将无法启动程序并给出错误警告, - -### Ⅲ. 使用方法 +### Ⅱ. 直接使用方法(low level api) #### 1. IAccessor接口,为数据访问接口 @@ -44,53 +33,81 @@ OrmContext.getAccessor().load(id, class); #### 2. IQuery接口,为数据复杂查询接口 -#### 3. 缓存使用方法 +- 目前提供的方法有:queryFieldLike,queryAll,queryFieldEqual,queryFieldIn,pageQuery -- 例如有下列配置 +### Ⅲ. 缓存使用方法(high level api) + +- 通过下列配置指定数据库和缓存的策略列表 ``` - - + + - - - - - - - - - + + + + +database表示操作哪个数据库 +address表示数据库的地址,支持分片的配置 ``` -- 有下列注解 +- 通过注解引入映射 ``` @EntityCaches(cacheStrategy = "tenThousand", persister = @Persister("time30s")) public class UserEntity implements IEntity { } + +cacheStrategy表示一个缓存的策略,即将数据库中的数据先读入Orm中的EntityCaches缓存,如thousand这个策略表示,缓存数据库中1000条数据,10分钟过期 +persisters表示一个持久化的策略,如time30s这个策略表示,将EntityCaches中的缓存数据每30s写入到数据库中一次,即使中途宕机,也只损失30s秒的数据 ``` -- database表示操作哪个数据库 -- address表示数据库的地址,支持分片的配置 -- caches中的strategy表示一个缓存的策略,即将数据库中的数据先读入Orm中的EntityCaches缓存,如hundred这个策略表示,缓存数据库中1000条数据,10分钟过期 -- persisters中的strategy表示一个持久化的策略,如3s这个策略表示,将EntityCaches中的缓存数据每3s写入到数据库中一次,即使中途宕机,也只损失3秒的数据 -- EntityCaches这个注解表示将会被Orm管理,使用hundred策略,缓存的持久化策略为3s +- 缓存的使用方法 + 1. 通过注解 + ``` + @Component + public class UserManager { + + @EntityCachesInjection + public IEntityCaches userEntityCaches; + + } + ``` + 2. 通过类动态获取 + ``` + IEntityCaches userEntityCaches = OrmContext.getOrmManager().getEntityCaches(UserEntity.class); + ``` + +- 更新对象 + +``` +userEntityCaches.update(entity); +``` + +### Ⅳ. 注意事项 + +- POJO对象的属性必须提供get和set方法,否则无法映射 +- 如果不想映射某属性,直接加上transient关键字 +- 目前支持基本数据属性(byte,short,int,long,float,double,boolean),字符串String,List,Set集合属性的映射,不支持Map +- 数据库主键能用整数尽量用整数,因为MongoDB默认的主键是一个字符串,比较占空间 +- 数据库使用自研的orm框架,比如一个实体类UserEntity,映射到数据库中的集合为user,首字母小写,去掉Entity +- 基于 [caffeine](https://github.com/ben-manes/caffeine) 的高性能数据缓存 +- 智能语法分析,不支持泛型和循环引用的对象,错误的entity对象定义将无法启动程序并给出错误警告 ### Ⅳ. 教程 -- test下中包含了所有增删改查的教程,运行之前请先安装MongoDB \ No newline at end of file +- [test](https://github.com/zfoo-project/zfoo/tree/main/orm/src/test/java/com/zfoo/orm) 下中包含了所有增删改查的教程,运行之前请先安装MongoDB diff --git a/orm/src/test/java/com/zfoo/orm/entity/MailEnt.java b/orm/src/test/java/com/zfoo/orm/entity/MailEnt.java index 2d91b451..2bc1257b 100644 --- a/orm/src/test/java/com/zfoo/orm/entity/MailEnt.java +++ b/orm/src/test/java/com/zfoo/orm/entity/MailEnt.java @@ -24,7 +24,7 @@ import com.zfoo.orm.model.entity.IEntity; * @author jaysunxiao * @version 3.0 */ -@EntityCache(cacheStrategy = "tenThousand", persister = @Persister("time30s")) +@EntityCache(cacheStrategy = "thousand", persister = @Persister("time30s")) public class MailEnt implements IEntity { @Id diff --git a/orm/src/test/java/com/zfoo/orm/entity/UserEntity.java b/orm/src/test/java/com/zfoo/orm/entity/UserEntity.java index b9211de1..7a24413a 100644 --- a/orm/src/test/java/com/zfoo/orm/entity/UserEntity.java +++ b/orm/src/test/java/com/zfoo/orm/entity/UserEntity.java @@ -23,7 +23,7 @@ import java.util.List; * @author jaysunxiao * @version 3.0 */ -@EntityCache(cacheStrategy = "tenThousand", persister = @Persister("time30s")) +@EntityCache(cacheStrategy = "thousand", persister = @Persister("time30s")) public class UserEntity implements IEntity { @Id diff --git a/orm/src/test/resources/application.xml b/orm/src/test/resources/application.xml index c97194bf..b0772ed0 100644 --- a/orm/src/test/resources/application.xml +++ b/orm/src/test/resources/application.xml @@ -27,20 +27,15 @@ - - - - - diff --git a/protocol/README.md b/protocol/README.md index 3f83fa9a..0b1ab5be 100644 --- a/protocol/README.md +++ b/protocol/README.md @@ -1,10 +1,11 @@ ### Ⅰ. 简介 -- zfoo protocol是目前的Java二进制序列化和反序列化最快的框架,并且线程安全 +- [zfoo protocol](https://github.com/zfoo-project/zfoo/blob/main/protocol/README.md) + 是目前的Java二进制序列化和反序列化最快的框架,并且为序列化后字节最少的框架 - 协议目前原生支持Java Javascript C# Lua GDScript,协议理论上可以跨平台 - 使用Javassist字节码增强动态生成顺序执行的序列化和反序列化函数,顺序执行的函数可以轻易的被JIT编译以达到极致的性能 - 单线程环境,在没有任何JVM参数调优的情况下速度比Protobuf快20%,比Kryo快40%,[参见性能测试](src/test/java/com/zfoo/protocol/SpeedTest.java) -- 多线程环境,zfoo和Protobuf的性能不受任何影响,kryo因为线程不安全性能会有所损失,[参见性能测试](src/test/java/com/zfoo/protocol/SpeedTest.java) +- 线程安全,zfoo和Protobuf的性能不受任何影响,kryo因为线程不安全性能会有所损失,[参见性能测试](src/test/java/com/zfoo/protocol/SpeedTest.java) ### Ⅱ. 快速使用 @@ -62,6 +63,7 @@ cpu: i9900k - 修改字段名称过后无法解析,内部使用字段的名称按照字符串的自然顺序来依次读写的,所以修改名称会导致读写顺序变化导致出现异常 - 减少字段无法解析,没必要一定要删除一个不需要的字段,所以不考虑这种情况 - 增加字段无法解析,可以考虑通过版本号去控制,或者增加新的协议类去解决(组合大于继承,协议类应该对扩展开放,对修改关闭) + ``` 设计模式六大原则中的开闭原则是对扩展开放,对修改关闭。协议的设计涉及到功能应该也要遵守这个原则。 @@ -92,9 +94,15 @@ cpu: i9900k - List,如果发送的是[obj,obj,null,obj],接收到的是[obj,obj,null,obj],即引用类型序列化之前为null,序列化之后同样为null - 不支持的数据格式,因为zfoo会自动识别不支持的类型并且给出错误警告,所以用户不必太关心 - - int[][],二维以上数组,考虑到不是所有语言都支持多维数组 - - List[],Map[],Java语言本身就没有支持泛型类数组 - - List,Map,泛型里面套数组,这种写法看起来比较奇怪,实际使用的地方很少 - - 枚举类,考虑到很多其他语言不支持枚举类,可以用int或者string在代码层面做替换 - - 自定义泛型类XXXClass,泛型类在很多框架中都极易出现性能上和解析上的问题,而且并不是所有语言都支持 - - 循环引用,虽然底层支持循环引用,但是考虑到循环引用带来语义上难以理解,容易出现错误,所以就屏蔽了 + - int[][],二维以上数组,考虑到不是所有语言都支持多维数组 + - List[],Map[],Java语言本身就没有支持泛型类数组 + - List,Map,泛型里面套数组,这种写法看起来比较奇怪,实际使用的地方很少 + - 枚举类,考虑到很多其他语言不支持枚举类,可以用int或者string在代码层面做替换 + - 自定义泛型类XXXClass,泛型类在很多框架中都极易出现性能上和解析上的问题,而且并不是所有语言都支持 + - 循环引用,虽然底层支持循环引用,但是考虑到循环引用带来语义上难以理解,容易出现错误,所以就屏蔽了 + +### Ⅵ. 用途 + +- 通信协议 +- 存储大对象 +- 对象复制,深克隆 diff --git a/scheduler/README.md b/scheduler/README.md index 47df8b1b..99c94996 100644 --- a/scheduler/README.md +++ b/scheduler/README.md @@ -1,11 +1,17 @@ -### Ⅰ. 注意事项 +### Ⅰ. 简介 -- 轻量级的cron表达式实现 -- 每秒钟执行一次SchedulerManager.triggerPerSecond()方法,循环遍历可执行的scheduler -- SchedulerManager的executor只有一条线程,所以使用者要避免做耗时和阻塞的运算,如果有这样的需求可以抛到其它线程池 -- 最大亮点是前后调整本地机器时间都会触发任务调度,本地开发非常有用。java和spring自带的任务调度不支持调整机器时间 +- [scheduler](https://github.com/zfoo-project/zfoo/blob/main/scheduler/README.md) 时间任务调度总线,cron表达式的解析使用了spring自带的解释器模式 -### Ⅱ. Cron Expression Example +### Ⅱ. 时间任务调度 + +![Image text](../doc/image/scheduler/scheduler01.png) + +- 前后调整本地机器时间都会触发任务调度,本地开发非常有用 +- ScheduledExecutorService每秒钟执行一次triggerPerSecond()方法,循环遍历可执行的scheduler +- zfoo认为一个程序中不会有太多的时间任务掉地,所以ScheduledExecutorService只有一条线程,所以使用者要避免做耗时和阻塞的运算,如果有这样的需求可以抛到其它线程池 +- zfoo scheduler使用Javassist字节码增强技术动态代理时间调度任务,避免了反射,没有性能损耗 + +### Ⅲ. Cron Expression Example ``` 30 * * * * ? 每半分钟触发任务 diff --git a/storage/README.md b/storage/README.md new file mode 100644 index 00000000..1b463fe6 --- /dev/null +++ b/storage/README.md @@ -0,0 +1,56 @@ +### Ⅰ. 简介 + +- [storage](https://github.com/zfoo-project/zfoo/blob/main/storage/README.md) + Excel和Java类自动映射框架,只需要定义一个和Excel对应的类,直接解析Excel + +- 利用Java动态语言的反射特性,用最少的代码去解析Excel + +### Ⅱ. 自动映射 + +- Excel的第一行对应Java类属性,第二行和第三行不会起到注释的作用,其中第一列必须是Id属性 + +![Image text](../doc/image/storage/storage01.png) + +- Excel对应的Java类 + +![Image text](../doc/image/storage/storage02.png) + +- 解析过后有两种使用方式 + 1. 通过注解 + ``` + @Component + public class StudentManager { + + @ResInjection + private Storage studentResources; + + } + ``` + 2. 通过类动态获取 + ``` + Storage studentResources = (Storage) StorageContext.getStorageManager().getStorage(StudentResource.class); + ``` + +- 通过id找到对应的行 + +``` +var studentResource = studentResources.get(1000); +``` + +- 通过索引找对应的行,默认为可重复的索引,返回了一个列表list + +``` +var students = studentResources.getIndex("name", "james0"); +``` + +- 唯一索引通过Storage.getUniqueIndex()获取,需要把索引注解标注为@Index(unique = true) + +### Ⅲ. 热更新Excel + +- [tank](https://github.com/zfoo-project/tank-game-server/blob/main/common/src/main/java/com/zfoo/tank/common/util/HotUtils.java) + 分布式热更新Excel配置文件实现 + +### Ⅳ. 用途 + +- 财务分析,数据分析统计 +- 游戏中的数值配置