doc[zfoo]: 更新文档
No files matched your search
@@ -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
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
- 并不是所有编程语言都有interface的,你用了interface的话其它编程语言就没法用了,就无法跨平台了
|
||||
- 基于packet的容易扩展,可以轻易跨平台
|
||||
|
||||
---
|
||||
|
||||
#### 为什么选择mongodb
|
||||
|
||||
- mongodb可以单机部署也可以分布式部署,是万能的数据库,而且速度足够快
|
||||
- mongodb,副本集部署(主从模式),可以数据同步,读写分离
|
||||
- mongodb,分片部署,故障转移(容灾)
|
||||
|
||||
#### TODO:
|
||||
|
||||
- 参看一下akka的actor实现,进一步优化net的线程模型,统一异步回调的argument参数
|
||||
|
||||
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 312 KiB |
|
After Width: | Height: | Size: 209 KiB |
|
After Width: | Height: | Size: 241 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 241 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 88 KiB |
@@ -0,0 +1,44 @@
|
||||
### Ⅰ. 简介
|
||||
|
||||
- [event](https://github.com/zfoo-project/zfoo/blob/main/event/README.md) 事件总线解耦不同模块,提高代码的质量,核心思想是观察者设计模式
|
||||
|
||||
### Ⅱ. 硬件层面的事件总线
|
||||
|
||||
#### 1. 设备间的通信,直连的方式
|
||||
|
||||
CPU所代表的控制器和运算器,要和存储器,也就是我们的主内存,以及输入和输出设备进行通信。 计算机是用什么样的方式来完成,CPU和内存、以及外部输入输出设备的通信呢?
|
||||
|
||||
比如我在键盘上输入一个快捷键,来控制我的音响播放音乐,计算机是用什么样的方式来完成,外部输入输出设备的通信呢?
|
||||
|
||||

|
||||

|
||||
|
||||
#### 2. 通过总线降低复杂度
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
#### 3. 硬件总线的多重架构
|
||||
|
||||

|
||||

|
||||
|
||||
总线的设计思路,核心是为了减少多个模块之间交互的复杂性和耦合度。
|
||||
|
||||
在实际的硬件层面,总线其实就是一组连接电路的线路。因为不同设备之间的速度有差异,所以一台计算机里面往往会有多个总线。
|
||||
|
||||
实际上,总线这个设计思路在我们的软件开发过程中也经常会被用到。事件总线就是我们常见的一个设计模式,通常事件总线也会和订阅者发布者模式结合起来,成为大型系统的各个松耦合的模块之间交互的一种主要模式。
|
||||
|
||||
### Ⅲ. 软件层面的事件总线
|
||||
|
||||

|
||||

|
||||
|
||||
- 事件总线从设计模式上说叫观察者模式
|
||||
- 设计模式没有固定的写法,zfoo event用最简单的注解实现了事件总线
|
||||
- zfoo event使用Javassist字节码增强技术动态代理事件接收者,避免了反射,没有性能损耗
|
||||
|
||||
### Ⅳ. 参考文章
|
||||
|
||||
- [深入浅出计算机组成原理学习笔记:第四十二讲](https://www.cnblogs.com/luoahong/p/11359768.html)
|
||||
@@ -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)
|
||||
分布式热更新代码实现
|
||||
@@ -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<DiskFileSystemVO> df = OSUtils.df();
|
||||
```
|
||||
|
||||
|
||||
- free,仿Linux的free指令,可以用来监控内存占用
|
||||
```
|
||||
MemoryVO free = OSUtils.free();
|
||||
```
|
||||
|
||||
|
||||
- sar,仿Linux的sar指令,可以用来监控网络IO
|
||||
```
|
||||
List<SarVO> sar = OSUtils.sar();
|
||||
```
|
||||
|
||||
### Ⅲ. 传统的服务器监控
|
||||
|
||||
- 传统的单机服务器监控,主要看cpu,内存,硬盘,网络
|
||||

|
||||
|
||||
- 分布式服务器监控,主要是监控多台服务器,和单机监控差别不大,唯一不同就是服务器变多了
|
||||
```
|
||||
自己写一个分布式服务器监控非常简单,只需要每隔一秒钟或者一分钟去采集到当前服务器的主要数据,上传到数据库或者其他服务器上就行了。
|
||||
通过这些被采集到的数据,去分析性能,去做监控警报,做折线图或者其他可视化图表。
|
||||
monitor通过静态类封装可以轻易采集到,然后实现自己的分布式监控程序,而且可以完全内嵌在Java程序中,不需要额外的部署
|
||||
```
|
||||
|
||||
### Ⅲ. 现在的服务器监控
|
||||
|
||||
- 现在服务器是容器的时代,服务器监控也从对服务器的监控转变为对容器的监控
|
||||
- 容器监控几乎是容器自带的一个集成功能了,甚至云厂商还会免费提供监控程序和监控服务
|
||||
|
||||
### Ⅳ. 我们能做的服务器监控
|
||||
|
||||
- 自己定制的监控才是符合自己项目的监控,比如自己实现一个监控rpc接口被调用次数的这种监控
|
||||
@@ -28,7 +28,7 @@ import oshi.SystemInfo;
|
||||
public class ApplicationTest {
|
||||
|
||||
/**
|
||||
* 仿Linux的uptime指令,可以用来监控cpu的负载
|
||||
* 仿Linux的uptime指令,可以用来监控cpu的负载,windows中无法统计,所以在windows返回的结果默认是-1
|
||||
*/
|
||||
@Test
|
||||
public void uptimeTest() {
|
||||
|
||||
@@ -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框架能够契合到游戏
|
||||
|
||||
### Ⅱ. 服务器架构图
|
||||
|
||||

|
||||
|
||||
<img src="./../doc/image/general-game-architect.jpg" width="70%" height="70%" alt="服务器架构图"/><br/>
|
||||
|
||||
### Ⅲ. 为什么快
|
||||
@@ -50,16 +58,73 @@ Java生态的rpc框架非常多,但是很少有rpc框架能够契合到游戏
|
||||
- rpc调用天生异步支持,并且保证rpc异步调用结束过后在同一条线程去执行,类似于actor的设计思想,特别适合对性能有极高需求的场景
|
||||
- 使用MVC设计模式,规范开发,保证代码质量,高效执行
|
||||
|
||||
### Ⅳ. 网络通信规范
|
||||
### Ⅳ. 服务器架构演进
|
||||
|
||||
- 客户端对服务器的请求以Request结尾,返回以Response结尾
|
||||
- 服务器内部之间的调用以Ask结尾,返回以Answer结尾。
|
||||
- 单台服务器+数据库
|
||||

|
||||
|
||||
- 添加反向代理,类似网关
|
||||

|
||||
```
|
||||
更大规模的架构做准备的第一步是添加一个“反向代理”
|
||||
路由将请求转发到正确的端点
|
||||
健康检查
|
||||
身份验证确保实际允许用户访问服务器
|
||||
防火墙确保用户只能访问我们允许他们使用的部分网络
|
||||
```
|
||||
|
||||
- 引入负载均衡器
|
||||

|
||||
```
|
||||
负载均衡器的把请求分发到两台服务器上。用户 1 往左,用户 2 往右,用户 3 再往左。
|
||||
```
|
||||
|
||||
- 扩展数据库
|
||||

|
||||
```
|
||||
集成的orm基于mongodb,可以支持分布式部署
|
||||
mongodb,副本集部署(主从模式),可以数据同步,读写分离,
|
||||
mongodb,分片部署,故障转移(容灾)
|
||||
```
|
||||
|
||||
- 微服务,随着开发团队随着应用的发展而增长,越来越多的开发人员工作在同一台服务器上,发生冲突的可能性很大
|
||||

|
||||
```
|
||||
微服务可以单独扩展,更好地适应需求。开发团队之间相互独立,每个团队都负责自己的微服务生命周期
|
||||
每个微服务都有自己的资源,比如数据库
|
||||
```
|
||||
|
||||
- 缓存和内容分发网络(CDN)
|
||||

|
||||
```
|
||||
网络应用的很大一部由静态资源构成,如图片、CSS 样式文件、Java 脚本以及一些针对特定产品提前渲染好的页面等等。
|
||||
游戏应用也包含了大量的图片资源,3D建模的资源
|
||||
缓存的加强版叫内容分发网络(Content Delivery Network),遍布全球的大量缓存。
|
||||
这使得用户可以从物理上靠近他们的地方来获取网页内容,而不是每次都把数据从源头搬到用户那里。
|
||||
```
|
||||
|
||||
- 消息队列
|
||||

|
||||
```
|
||||
流量消峰,有时需要处理大量比如双11抢购秒杀,让突然的大量请求排队去处理任务
|
||||
应用解耦
|
||||
消息分发
|
||||
```
|
||||
|
||||
- 分片,分区,比如将用户分配到与其最近的服务器提供服务
|
||||

|
||||
```
|
||||
比如,两地三中心,容灾备份
|
||||
```
|
||||
|
||||
- 对负载均衡器进行负载均衡
|
||||

|
||||
|
||||
### Ⅴ. 教程
|
||||
|
||||
- [单机服务器教程](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/)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
- 通过下列配置指定数据库和缓存的策略列表
|
||||
|
||||
```
|
||||
<orm:config id="config" entity-package="com.zfoo.orm.**.entity">
|
||||
|
||||
<orm:host database="test" user="" password="">
|
||||
<orm:address name="server0" url="127.0.0.1:27017"/>
|
||||
<orm:host database="${mongodb.database}" user="${mongodb.user}" password="${mongodb.password}">
|
||||
<orm:address name="${mongodb.address.name}" url="${mongodb.address.url}"/>
|
||||
</orm:host>
|
||||
|
||||
<!-- 缓存策略 -->
|
||||
<orm:caches>
|
||||
<orm:cache strategy="ten" size="10" expire-millisecond="600000"/>
|
||||
<orm:cache strategy="hundred" size="100" expire-millisecond="600000"/>
|
||||
<orm:cache strategy="thousand" size="1000" expire-millisecond="600000"/>
|
||||
<orm:cache strategy="threeThousand" size="3000" expire-millisecond="600000"/>
|
||||
<orm:cache strategy="tenThousand" size="10000" expire-millisecond="600000"/>
|
||||
</orm:caches>
|
||||
|
||||
<!-- 持久化策略 -->
|
||||
<orm:persisters>
|
||||
<orm:persister strategy="cronDefault" type="cron" config="0,30 * * * * ?"/>
|
||||
<orm:persister strategy="cron3s" type="cron" config="0/3 * * * * ?"/>
|
||||
<orm:persister strategy="cron15s" type="cron" config="0/15 * * * * ?"/>
|
||||
<orm:persister strategy="cron30s" type="cron" config="0/30 * * * * ?"/>
|
||||
<orm:persister strategy="cron1m" type="cron" config="0 0/30 * * * ?"/>
|
||||
<orm:persister strategy="time30s" type="time" config="30000"/>
|
||||
</orm:persisters>
|
||||
<orm:persister strategy="cronDefault" type="cron" config="0,30 * * * * ?"/>
|
||||
<orm:persister strategy="cron30s" type="cron" config="0/30 * * * * ?"/>
|
||||
<orm:persister strategy="time30s" type="time" config="30000"/>
|
||||
</orm:persisters>
|
||||
</orm:config>
|
||||
|
||||
database表示操作哪个数据库
|
||||
address表示数据库的地址,支持分片的配置
|
||||
```
|
||||
|
||||
- 有下列注解
|
||||
- 通过注解引入映射
|
||||
|
||||
```
|
||||
@EntityCaches(cacheStrategy = "tenThousand", persister = @Persister("time30s"))
|
||||
public class UserEntity implements IEntity<Long> {
|
||||
}
|
||||
|
||||
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<Long, UserEntity> userEntityCaches;
|
||||
|
||||
}
|
||||
```
|
||||
2. 通过类动态获取
|
||||
```
|
||||
IEntityCaches<Long, UserEntity> 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
|
||||
- [test](https://github.com/zfoo-project/zfoo/tree/main/orm/src/test/java/com/zfoo/orm) 下中包含了所有增删改查的教程,运行之前请先安装MongoDB
|
||||
@@ -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<String> {
|
||||
|
||||
@Id
|
||||
|
||||
@@ -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<Long> {
|
||||
|
||||
@Id
|
||||
|
||||
@@ -27,20 +27,15 @@
|
||||
|
||||
<!-- 缓存策略 -->
|
||||
<orm:caches>
|
||||
<orm:cache strategy="ten" size="10" expire-millisecond="600000"/>
|
||||
<orm:cache strategy="hundred" size="100" expire-millisecond="600000"/>
|
||||
<orm:cache strategy="thousand" size="1000" expire-millisecond="600000"/>
|
||||
<orm:cache strategy="threeThousand" size="3000" expire-millisecond="600000"/>
|
||||
<orm:cache strategy="tenThousand" size="10000" expire-millisecond="600000"/>
|
||||
</orm:caches>
|
||||
|
||||
<!-- 持久化策略 -->
|
||||
<orm:persisters>
|
||||
<orm:persister strategy="cronDefault" type="cron" config="0,30 * * * * ?"/>
|
||||
<orm:persister strategy="cron3s" type="cron" config="0/3 * * * * ?"/>
|
||||
<orm:persister strategy="cron15s" type="cron" config="0/15 * * * * ?"/>
|
||||
<orm:persister strategy="cron30s" type="cron" config="0/30 * * * * ?"/>
|
||||
<orm:persister strategy="cron1m" type="cron" config="0 0/30 * * * ?"/>
|
||||
<orm:persister strategy="time30s" type="time" config="30000"/>
|
||||
</orm:persisters>
|
||||
</orm:config>
|
||||
|
||||
@@ -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<XXXClass>,如果发送的是[obj,obj,null,obj],接收到的是[obj,obj,null,obj],即引用类型序列化之前为null,序列化之后同样为null
|
||||
|
||||
- 不支持的数据格式,因为zfoo会自动识别不支持的类型并且给出错误警告,所以用户不必太关心
|
||||
- int[][],二维以上数组,考虑到不是所有语言都支持多维数组
|
||||
- List<Integer>[],Map<Integer, Integer>[],Java语言本身就没有支持泛型类数组
|
||||
- List<int[]>,Map<Integer, Integer[]>,泛型里面套数组,这种写法看起来比较奇怪,实际使用的地方很少
|
||||
- 枚举类,考虑到很多其他语言不支持枚举类,可以用int或者string在代码层面做替换
|
||||
- 自定义泛型类XXXClass<T>,泛型类在很多框架中都极易出现性能上和解析上的问题,而且并不是所有语言都支持
|
||||
- 循环引用,虽然底层支持循环引用,但是考虑到循环引用带来语义上难以理解,容易出现错误,所以就屏蔽了
|
||||
- int[][],二维以上数组,考虑到不是所有语言都支持多维数组
|
||||
- List<Integer>[],Map<Integer, Integer>[],Java语言本身就没有支持泛型类数组
|
||||
- List<int[]>,Map<Integer, Integer[]>,泛型里面套数组,这种写法看起来比较奇怪,实际使用的地方很少
|
||||
- 枚举类,考虑到很多其他语言不支持枚举类,可以用int或者string在代码层面做替换
|
||||
- 自定义泛型类XXXClass<T>,泛型类在很多框架中都极易出现性能上和解析上的问题,而且并不是所有语言都支持
|
||||
- 循环引用,虽然底层支持循环引用,但是考虑到循环引用带来语义上难以理解,容易出现错误,所以就屏蔽了
|
||||
|
||||
### Ⅵ. 用途
|
||||
|
||||
- 通信协议
|
||||
- 存储大对象
|
||||
- 对象复制,深克隆
|
||||
@@ -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
|
||||
### Ⅱ. 时间任务调度
|
||||
|
||||

|
||||
|
||||
- 前后调整本地机器时间都会触发任务调度,本地开发非常有用
|
||||
- ScheduledExecutorService每秒钟执行一次triggerPerSecond()方法,循环遍历可执行的scheduler
|
||||
- zfoo认为一个程序中不会有太多的时间任务掉地,所以ScheduledExecutorService只有一条线程,所以使用者要避免做耗时和阻塞的运算,如果有这样的需求可以抛到其它线程池
|
||||
- zfoo scheduler使用Javassist字节码增强技术动态代理时间调度任务,避免了反射,没有性能损耗
|
||||
|
||||
### Ⅲ. Cron Expression Example
|
||||
|
||||
```
|
||||
30 * * * * ? 每半分钟触发任务
|
||||
|
||||
@@ -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属性
|
||||
|
||||

|
||||
|
||||
- Excel对应的Java类
|
||||
|
||||

|
||||
|
||||
- 解析过后有两种使用方式
|
||||
1. 通过注解
|
||||
```
|
||||
@Component
|
||||
public class StudentManager {
|
||||
|
||||
@ResInjection
|
||||
private Storage<Integer, StudentResource> studentResources;
|
||||
|
||||
}
|
||||
```
|
||||
2. 通过类动态获取
|
||||
```
|
||||
Storage<Integer, StudentResource> studentResources = (Storage<Integer, StudentResource>) 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配置文件实现
|
||||
|
||||
### Ⅳ. 用途
|
||||
|
||||
- 财务分析,数据分析统计
|
||||
- 游戏中的数值配置
|
||||