doc[zfoo]: 更新文档

This commit is contained in:
jaysunxiao committed 2021-10-24 18:28:00 +08:00
1 parent 1cd1779136
commit c345242a3d
40 files changed
+338 -70

No files matched your search

+1 -1
View File
@@ -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
+8
View File
@@ -16,6 +16,14 @@
- 并不是所有编程语言都有interface的,你用了interface的话其它编程语言就没法用了,就无法跨平台了
- 基于packet的容易扩展,可以轻易跨平台
---
#### 为什么选择mongodb
- mongodb可以单机部署也可以分布式部署,是万能的数据库,而且速度足够快
- mongodb,副本集部署(主从模式),可以数据同步,读写分离
- mongodb,分片部署,故障转移(容灾)
#### TODO:
- 参看一下akka的actor实现,进一步优化net的线程模型,统一异步回调的argument参数
Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

+44
View File
@@ -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)
+18
View File
@@ -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)
分布式热更新代码实现
+48
View File
@@ -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,内存,硬盘,网络
![Image text](../doc/image/monitor/monitor01.png)
- 分布式服务器监控,主要是监控多台服务器,和单机监控差别不大,唯一不同就是服务器变多了
```
自己写一个分布式服务器监控非常简单,只需要每隔一秒钟或者一分钟去采集到当前服务器的主要数据,上传到数据库或者其他服务器上就行了。
通过这些被采集到的数据,去分析性能,去做监控警报,做折线图或者其他可视化图表。
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() {
+75 -7
View File
@@ -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)
<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结尾。
- 单台服务器+数据库
![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/)
@@ -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
+51 -34
View File
@@ -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
-5
View File
@@ -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>
+16 -8
View File
@@ -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>,泛型类在很多框架中都极易出现性能上和解析上的问题,而且并不是所有语言都支持
- 循环引用,虽然底层支持循环引用,但是考虑到循环引用带来语义上难以理解,容易出现错误,所以就屏蔽了
### Ⅵ. 用途
- 通信协议
- 存储大对象
- 对象复制,深克隆
+12 -6
View File
@@ -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 * * * * ? 每半分钟触发任务
+56
View File
@@ -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<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配置文件实现
### Ⅳ. 用途
- 财务分析,数据分析统计
- 游戏中的数值配置