doc[zfoo]: update document

This commit is contained in:
godotg
2022-12-02 20:39:13 +08:00
parent 2f75896907
commit 44004d5f93
14 changed files with 965 additions and 265 deletions
+2
View File
@@ -1,3 +1,5 @@
English | [简体中文](./README_CN.md)
### . Introduction
- [hotswap](https://github.com/zfoo-project/zfoo/blob/main/hotswap/README.md) Hot update code, no need to stop the
+2
View File
@@ -1,3 +1,5 @@
简体中文 | [English](./README.md)
### . 简介
- [hotswap](https://github.com/zfoo-project/zfoo/blob/main/hotswap/README.md) 热更新代码,不需要停止服务器,不需要额外的任何配置,一行代码开启热更新
+27 -18
View File
@@ -1,48 +1,57 @@
### . 简介
English | [简体中文](./README_CN.md)
- [monitor](https://github.com/zfoo-project/zfoo/blob/main/storage/README.md) 轻量级cpu,内存,硬盘,网络监控
### . Introduction
### Ⅱ. 使用
- [monitor](https://github.com/zfoo-project/zfoo/blob/main/storage/README.md) Lightweight cpu, memory, hard disk,
network monitoring
- uptime,仿Linux的uptime指令,可以用来监控cpu的负载
### Ⅱ. Use
- uptime, imitating the uptime command of Linux, can be used to monitor the load of cpu
```
UptimeVO uptime = OSUtils.uptime();
```
- df,仿Linuxdf指令,可以用来监控硬盘容量
- df, imitating the Linux df command, can be used to monitor the hard disk capacity
```
List<DiskFileSystemVO> df = OSUtils.df();
```
- free,仿Linuxfree指令,可以用来监控内存占用
- free, imitating Linux's free command, can be used to monitor memory usage
```
MemoryVO free = OSUtils.free();
```
- sar,仿Linux的sar指令,可以用来监控网络IO
- sar, the sar command imitating Linux, can be used to monitor network IO
```
List<SarVO> sar = OSUtils.sar();
```
### Ⅲ. 传统的服务器监控
### Ⅲ. Traditional Server Monitoring
- 传统的单机服务器监控,主要看cpu,内存,硬盘,网络
- Traditional stand-alone server monitoring mainly depends on cpu, memory, hard disk, network
![Image text](../doc/image/monitor/monitor01.png)
- 分布式服务器监控,主要是监控多台服务器,和单机监控差别不大,唯一不同就是服务器变多了
- Distributed server monitoring is mainly to monitor multiple servers, which is not much different from stand-alone
monitoring.
```
自己写一个分布式服务器监控非常简单,只需要每隔一秒钟或者一分钟去采集到当前服务器的主要数据,上传到数据库或者其他服务器上就行了。
通过这些被采集到的数据,去分析性能,去做监控警报,做折线图或者其他可视化图表。
monitor通过静态类封装可以轻易采集到,然后实现自己的分布式监控程序,而且可以完全内嵌在Java程序中,不需要额外的部署
It is very simple to write a distributed server monitoring by yourself.
You only need to collect the main data of the current server every second or minute, and upload it to the database or other servers.
Use these collected data to analyze performance, make monitoring alarms, make line charts or other visual charts.
Monitor can be easily collected through static class encapsulation, and then realize its own distributed monitoring program,
and can be completely embedded in Java programs without additional deployment
```
### Ⅲ. 现在的服务器监控
### Ⅲ. Modern Server Monitoring
- 现在服务器是容器的时代,服务器监控也从对服务器的监控转变为对容器的监控
- 容器监控几乎是容器自带的一个集成功能了,甚至云厂商还会免费提供监控程序和监控服务
- Now that the server is the era of containers, server monitoring has also changed from server monitoring to container
monitoring
- Container monitoring is almost an integrated function of the container itself, and even cloud vendors will provide
monitoring programs and monitoring services for free
### Ⅳ. 我们能做的服务器监控
### Ⅳ. Server Monitoring We Can Do
- 自己定制的监控才是符合自己项目的监控,比如自己实现一个监控rpc接口被调用次数的这种监控
- Self-customized monitoring is the monitoring that fits your own project, such as implementing a monitoring that
monitors the number of times the rpc interface is called by yourself
+50
View File
@@ -0,0 +1,50 @@
简体中文 | [English](./README.md)
### . 简介
- [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接口被调用次数的这种监控
+93 -82
View File
@@ -1,170 +1,181 @@
### . 简介
English | [简体中文](./README_CN.md)
- [net](https://github.com/zfoo-project/zfoo/blob/main/net/README.md) 天生异步,Actor设计思想,无锁化设计,基于Spring的MVC式用法的万能RPC框架
### . Introduction
1. 优雅的同步和异步请求,速度更快,支持 tcp udp websocket
2. 服务注册和发现,配置中心使用的是zookeeper,可扩展成其它注册中心
3. 自带高性能网关,自定义转发策略
4. 服务可伸缩,负载均衡,集群监控,应有尽有
4. 基于Java11,所有的依赖包都是最新的jar包
- [net](https://github.com/zfoo-project/zfoo/blob/main/net/README.md) Naturally asynchronous, Actor design idea,
lock-free design, universal RPC framework based on Spring's MVC-style usage
### Ⅱ. 相关模块
1. Elegant synchronous and asynchronous requests, faster, supports tcp udp websocket
2. Service registration and discovery, the configuration center uses zookeeper, which can be extended to other
registration centers
3. Built-in high-performance gateway, custom forwarding strategy
4. Scalable services, load balancing, cluster monitoring, everything you expect
- net提供了两层api接口
- IRouter(low level api)为底层网络接口,直接通过Session连接发送消息
- IConsumer(high level api)为高层网络接口,是对IRouter的封装,消费者向服务提供者发送RPC消息
### Ⅱ. Related modules
- net provides two layers of api interface
- IRouter(low level api) The underlying network interface, which sends messages directly through the Session
connection
- IConsumer(high level api) The high-level network interface is the encapsulation of I Router, and the consumer sends
RPC messages to the service provider
```
极致的高性能,高可用性,高伸缩性(一般指增加机器),高扩展性(一般指代码层面的开闭原则)
Extreme high performance, high availability, high scalability (generally refers to adding machines),
high scalability (generally refers to the principle of opening and closing at the code level)
config本地配置,zookeeper的注册发现,请求的负载均衡,都放在这个包下
core核心包,服务器,客户端的统一封装
router消息的路由
handlernettyhandler,定义了客户端,服务器的一些通用handler
protocol消息类的注册,消息的编解码,字节码增强等
schemaspring的自定义标签的解析
sessionnettychannel的封装
task任务线程池,任务的分发
configlocal configuration, zookeeper registration discovery, and request load balancing are all placed under this package
coreunified package of core package, server and client
routermessage routing
handlernetty's handler defines some common handlers for clients and servers
protocolregistration of message classes, encoding and decoding of messages, bytecode enhancement, etc.
schemaanalysis of spring's custom tags
sessionencapsulation of netty's channel
tasktask thread pool, task distribution
```
- 网络通信规范
- 客户端对服务器的请求以Request结尾,返回以Response结尾
- 服务器内部之间的调用以Ask结尾,返回以Answer结尾。
- Network communication specification
- The client's request to the server ends with Request, and the return ends with Response
- The call between the servers ends with Ask, and the return ends with Answer.
#### 诞生背景
#### Background
```
用Java做网络游戏涉及到比较复杂的交互场景的时候,远程请求非常多,这时候就需要一个高性能的rpc框架。
Java生态的rpc框架非常多,但是很少有rpc框架能够契合到游戏项目的rpc框架,下面列了一些:
dubbo,代码重量级,异步调用繁琐,没有网关,无法直接和客户端交互;
grpc,代码重量级,使用复杂,部署复杂,没有网关,很难自定义;
akka,用Scala语言,学习成本极高,直接放弃;
所以就写了一个万能的rpc框架,代码轻量级,可以方便的自定义,自带网关的,天生异步,工具链完整。
经过后续的不断优化,还做出了一个目前Java速度最快的序列化框架,然后速度就直接起飞了。
后来发现其实不仅仅能用来做游戏,只要有对性能有极致需求的rpc场景都能使用的上这个框架。
When using Java to make online games involves more complex interactive scenarios, there are a lot of remote requests.
At this time, a high-performance rpc framework is needed.
There are many rpc frameworks in the Java ecosystem, but few rpc frameworks can fit into the rpc framework of game projects.
Some of them are listed below
dubboThe code is heavyweight, asynchronous calls are cumbersome, there is no gateway, and it is impossible to directly interact with the client;
grpcThe code is heavyweight, complex to use, complex to deploy, no gateway, difficult to customize;
akkaUsing Scala language, the learning cost is extremely high, just give up;
So I wrote a universal rpc framework, the code is lightweight, can be easily customized, with its own gateway, inherently asynchronous, and the tool chain is complete.
After subsequent continuous optimization, a serialization framework with the fastest speed in Java was also made, and then the speed took off directly.
Later, I found out that it can be used not only for games, but also for rpc scenes with extreme performance requirements.
```
### Ⅱ. 服务器架构图
### Ⅱ. Server Architecture Diagram
![Image text](../doc/image/net/rpc01.png)
<img src="./../doc/image/general-game-architect.jpg" width="70%" height="70%" alt="服务器架构图"/><br/>
<img src="./../doc/image/general-game-architect.jpg" width="70%" height="70%" alt="Server Architecture Diagram"/><br/>
### Ⅲ. 为什么快
### Ⅲ. Why fast
----------
- 使用目前性能最好的 [zfoo protocol](protocol/README.md) 作为网关和RPC消息的序列化和反序列化协议
- 无锁化设计和优雅的线程池设计,用户的请求通过网关总能保证请求在同一台服务器的同一条线程去执行,所以就不需要用锁保证并发
- rpc调用天生异步支持,并且保证rpc异步调用结束过后在同一条线程去执行,类似于actor的设计思想,特别适合对性能有极高需求的场景
- 使用MVC设计模式,规范开发,保证代码质量,高效执行
- Use current best performin [zfoo protocol](protocol/README.md) as serialization and deserialization protocol for
gateway and RPC messages
- Lock-free design and elegant thread pool design, the user's request can always be guaranteed to be executed in the
same thread of the same server through the gateway, so there is no need to use locks to ensure concurrency
- The rpc call is naturally asynchronous, and it is guaranteed to be executed in the same thread after the rpc
asynchronous call ends, similar to the design idea of actor, especially suitable for scenarios with extremely high
performance requirements
- Use the MVC design pattern to standardize development, ensure code quality, and execute efficiently
### Ⅳ. 服务器架构演进
### Ⅳ. Server Architecture Evolution
- 单台服务器+数据库,[zfoo net 单台服务器使用教程](src/test/java/com/zfoo/net/core/tcp/server/TcpServerTest.java)
- Single server +
database[zfoo net Single server usage tutorial](src/test/java/com/zfoo/net/core/tcp/server/TcpServerTest.java)
![Image text](../doc/image/net/framework01.png)
----------
- 添加反向代理,类似网关
- Add a reverse proxy, similar to a gateway
```
更大规模的架构做准备的第一步是添加一个“反向代理”
路由将请求转发到正确的端点
健康检查
身份验证确保实际允许用户访问服务器
防火墙确保用户只能访问我们允许他们使用的部分网络
The first step in preparing for a larger scale architecture is to add a "reverse proxy"
Routing forwards the request to the correct endpoint
health examination
Authentication ensures that the user is actually allowed to access the server
Firewalls ensure users can only access the parts of the network we allow them to use
```
![Image text](../doc/image/net/framework02.png)
----------
- 引入负载均衡器, [zfoo net 网关负载均衡教程](src/test/java/com/zfoo/net/core/gateway/GatewayTest.java)
- Introducing a load
balancer [zfoo net Gateway Load Balancing Tutorial](src/test/java/com/zfoo/net/core/gateway/GatewayTest.java)
```
负载均衡器的把请求分发到两台服务器上。用户 1 往左,用户 2 往右,用户 3 再往左。
The load balancer distributes the requests to the two servers. User 1 goes left, User 2 goes right, User 3 goes left again.
```
![Image text](../doc/image/net/framework03.png)
----------
- 扩展数据库
- extended database
```
集成的orm基于mongodb,可以支持分布式部署
mongodb副本集部署(主从模式),可以数据同步,读写分离,
mongodb分片部署,故障转移(容灾)
The integrated orm is based on mongodb, which can support distributed deployment
mongodbReplica set deployment (master-slave mode), data synchronization, read-write separation,
mongodbFragmentation deployment, failover (disaster recovery)
```
![Image text](../doc/image/net/framework04.png)
----------
- 微服务,[微服务RPC教程](src/test/java/com/zfoo/net/core/provider/ProviderTest.java)
- microservice[Microservice RPC Tutorial](src/test/java/com/zfoo/net/core/provider/ProviderTest.java)
```
随着开发团队随着应用的发展而增长,越来越多的开发人员工作在同一台服务器上,发生冲突的可能性很大。
这个时候把通过微服务,把业务拆分出来是更好的选择。
As the development team grows with the application, more and more developers are working on the same server, and the potential for conflicts is high.
At this time, it is a better choice to split the business through microservices.
微服务可以单独扩展,更好地适应需求。开发团队之间相互独立,每个团队都负责自己的微服务生命周期
每个微服务都有自己的资源,比如数据库
Microservices can be scaled individually to better adapt to demand. Development teams are independent from each other, each responsible for their own microservice lifecycle
Each microservice has its own resources, such as a database
```
![Image text](../doc/image/net/framework05.png)
----------
- 缓存和内容分发网络(CDN)
- Caching and Content Delivery Network (CDN)
```
网络应用的很大一部由静态资源构成,如图片、CSS 样式文件、Java 脚本以及一些针对特定产品提前渲染好的页面等等。
游戏应用也包含了大量的图片资源,3D建模的资源
缓存的加强版叫内容分发网络(Content Delivery Network),遍布全球的大量缓存。
这使得用户可以从物理上靠近他们的地方来获取网页内容,而不是每次都把数据从源头搬到用户那里。
A large part of web applications consists of static resources, such as images, CSS style files, Java scripts, and some pre-rendered pages for specific products, etc.
Game applications also contain a large number of image resources, 3D modeling resources
An enhanced version of the cache is called the Content Delivery Network (Content Delivery Network), a large number of caches all over the world.
This allows users to fetch web content from places that are physically close to them, rather than moving data from the source to the user each time.
```
![Image text](../doc/image/net/framework06.png)
----------
- 消息队列
- message queue
```
流量消峰,有时需要处理大量比如双11抢购秒杀,让突然的大量请求排队去处理任务
应用解耦
消息分发
When the traffic peaks, sometimes it is necessary to process a large number of flash sales such as Double 11, so that a large number of sudden requests can be queued up to process tasks
application decoupling
message distribution
```
![Image text](../doc/image/net/framework07.png)
----------
- 分片,分区,比如将用户分配到与其最近的服务器提供服务
- Fragmentation, partitioning, such as assigning users to the closest server to provide services
```
比如,两地三中心,容灾备份
```
![Image text](../doc/image/net/framework08.png)
----------
- 对负载均衡器进行负载均衡
![Image text](../doc/image/net/framework09.png)
### . 依赖的中间件zookeeper教程
### . Dependent middleware zookeeper tutorial
- [zk基础的增删改查](src/test/java/com/zfoo/net/zookeeper/base)
- [curator的增删改查](src/test/java/com/zfoo/net/zookeeper/curator)
- [zookeeper实现的分布式自增ID](src/test/java/com/zfoo/net/zookeeper/recipes/atomicint)
- [分布式Barrier](src/test/java/com/zfoo/net/zookeeper/recipes/distributedbarrier)
- [分布式锁](src/test/java/com/zfoo/net/zookeeper/recipes/distributedbarrier)
- [数据发布和订阅](src/test/java/com/zfoo/net/zookeeper/recipes/nodecache)
- [zookeeper的分布式选举算法](src/test/java/com/zfoo/net/zookeeper/recipes/mastersel)
- [Addition, deletion, modification and inspection of ZK basics](src/test/java/com/zfoo/net/zookeeper/base)
- [Curator additions, deletions, modifications](src/test/java/com/zfoo/net/zookeeper/curator)
- [Distributed auto-incrementing ID implemented by zookeeper](src/test/java/com/zfoo/net/zookeeper/recipes/atomicint)
- [Distributed Barrier](src/test/java/com/zfoo/net/zookeeper/recipes/distributedbarrier)
- [Distributed locks](src/test/java/com/zfoo/net/zookeeper/recipes/distributedbarrier)
- [Data publishing and subscribing](src/test/java/com/zfoo/net/zookeeper/recipes/nodecache)
- [Zookeeper's distributed election algorithm](src/test/java/com/zfoo/net/zookeeper/recipes/mastersel)
### Ⅵ. 参考文章
### Ⅵ. Reference article
- [Scaling webapps for newbs](https://arcentry.com/blog/scaling-webapps-for-newbs-and-non-techies/)
+172
View File
@@ -0,0 +1,172 @@
简体中文 | [English](./README.md)
### . 简介
- [net](https://github.com/zfoo-project/zfoo/blob/main/net/README.md) 天生异步,Actor设计思想,无锁化设计,基于Spring的MVC式用法的万能RPC框架
1. 优雅的同步和异步请求,速度更快,支持 tcp udp websocket
2. 服务注册和发现,配置中心使用的是zookeeper,可扩展成其它注册中心
3. 自带高性能网关,自定义转发策略
4. 服务可伸缩,负载均衡,集群监控,应有尽有
4. 基于Java11,所有的依赖包都是最新的jar包
### Ⅱ. 相关模块
- net提供了两层api接口
- IRouter(low level api)为底层网络接口,直接通过Session连接发送消息
- IConsumer(high level api)为高层网络接口,是对IRouter的封装,消费者向服务提供者发送RPC消息
```
极致的高性能,高可用性,高伸缩性(一般指增加机器),高扩展性(一般指代码层面的开闭原则)
config,本地配置,zookeeper的注册发现,请求的负载均衡,都放在这个包下
core,核心包,服务器,客户端的统一封装
router,消息的路由
handlernetty的handler,定义了客户端,服务器的一些通用handler
protocol,消息类的注册,消息的编解码,字节码增强等
schemaspring的自定义标签的解析
session,对netty的channel的封装
task,任务线程池,任务的分发
```
- 网络通信规范
- 客户端对服务器的请求以Request结尾,返回以Response结尾
- 服务器内部之间的调用以Ask结尾,返回以Answer结尾。
#### 诞生背景
```
用Java做网络游戏涉及到比较复杂的交互场景的时候,远程请求非常多,这时候就需要一个高性能的rpc框架。
Java生态的rpc框架非常多,但是很少有rpc框架能够契合到游戏项目的rpc框架,下面列了一些:
dubbo,代码重量级,异步调用繁琐,没有网关,无法直接和客户端交互;
grpc,代码重量级,使用复杂,部署复杂,没有网关,很难自定义;
akka,用Scala语言,学习成本极高,直接放弃;
所以就写了一个万能的rpc框架,代码轻量级,可以方便的自定义,自带网关的,天生异步,工具链完整。
经过后续的不断优化,还做出了一个目前Java速度最快的序列化框架,然后速度就直接起飞了。
后来发现其实不仅仅能用来做游戏,只要有对性能有极致需求的rpc场景都能使用的上这个框架。
```
### Ⅱ. 服务器架构图
![Image text](../doc/image/net/rpc01.png)
<img src="./../doc/image/general-game-architect.jpg" width="70%" height="70%" alt="服务器架构图"/><br/>
### Ⅲ. 为什么快
----------
- 使用目前性能最好的 [zfoo protocol](protocol/README.md) 作为网关和RPC消息的序列化和反序列化协议
- 无锁化设计和优雅的线程池设计,用户的请求通过网关总能保证请求在同一台服务器的同一条线程去执行,所以就不需要用锁保证并发
- rpc调用天生异步支持,并且保证rpc异步调用结束过后在同一条线程去执行,类似于actor的设计思想,特别适合对性能有极高需求的场景
- 使用MVC设计模式,规范开发,保证代码质量,高效执行
### Ⅳ. 服务器架构演进
- 单台服务器+数据库,[zfoo net 单台服务器使用教程](src/test/java/com/zfoo/net/core/tcp/server/TcpServerTest.java)
![Image text](../doc/image/net/framework01.png)
----------
- 添加反向代理,类似网关
```
更大规模的架构做准备的第一步是添加一个“反向代理”
路由将请求转发到正确的端点
健康检查
身份验证确保实际允许用户访问服务器
防火墙确保用户只能访问我们允许他们使用的部分网络
```
![Image text](../doc/image/net/framework02.png)
----------
- 引入负载均衡器, [zfoo net 网关负载均衡教程](src/test/java/com/zfoo/net/core/gateway/GatewayTest.java)
```
负载均衡器的把请求分发到两台服务器上。用户 1 往左,用户 2 往右,用户 3 再往左。
```
![Image text](../doc/image/net/framework03.png)
----------
- 扩展数据库
```
集成的orm基于mongodb,可以支持分布式部署
mongodb,副本集部署(主从模式),可以数据同步,读写分离
mongodb,分片部署,故障转移(容灾)
```
![Image text](../doc/image/net/framework04.png)
----------
- 微服务,[微服务RPC教程](src/test/java/com/zfoo/net/core/provider/ProviderTest.java)
```
随着开发团队随着应用的发展而增长,越来越多的开发人员工作在同一台服务器上,发生冲突的可能性很大。
这个时候把通过微服务,把业务拆分出来是更好的选择。
微服务可以单独扩展,更好地适应需求。开发团队之间相互独立,每个团队都负责自己的微服务生命周期
每个微服务都有自己的资源,比如数据库
```
![Image text](../doc/image/net/framework05.png)
----------
- 缓存和内容分发网络(CDN)
```
网络应用的很大一部由静态资源构成,如图片、CSS 样式文件、Java 脚本以及一些针对特定产品提前渲染好的页面等等。
游戏应用也包含了大量的图片资源,3D建模的资源
缓存的加强版叫内容分发网络(Content Delivery Network),遍布全球的大量缓存。
这使得用户可以从物理上靠近他们的地方来获取网页内容,而不是每次都把数据从源头搬到用户那里。
```
![Image text](../doc/image/net/framework06.png)
----------
- 消息队列
```
流量消峰,有时需要处理大量比如双11抢购秒杀,让突然的大量请求排队去处理任务
应用解耦
消息分发
```
![Image text](../doc/image/net/framework07.png)
----------
- 分片,分区,比如将用户分配到与其最近的服务器提供服务
```
比如,两地三中心,容灾备份
```
![Image text](../doc/image/net/framework08.png)
----------
- 对负载均衡器进行负载均衡
![Image text](../doc/image/net/framework09.png)
### . 依赖的中间件zookeeper教程
- [zk基础的增删改查](src/test/java/com/zfoo/net/zookeeper/base)
- [curator的增删改查](src/test/java/com/zfoo/net/zookeeper/curator)
- [zookeeper实现的分布式自增ID](src/test/java/com/zfoo/net/zookeeper/recipes/atomicint)
- [分布式Barrier](src/test/java/com/zfoo/net/zookeeper/recipes/distributedbarrier)
- [分布式锁](src/test/java/com/zfoo/net/zookeeper/recipes/distributedbarrier)
- [数据发布和订阅](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/)
+55 -43
View File
@@ -1,51 +1,55 @@
### . 简介
English | [简体中文](./README_CN.md)
- [orm](https://github.com/zfoo-project/zfoo/blob/main/orm/README.md) 基于MongoDB的orm框架,提供POJO对象和MongoDB数据库之间的映射
- mongodb是分布式数据库,可以单机使用,也可以分布式使用
- 基于 [caffeine](https://github.com/ben-manes/caffeine) 的高性能数据库实体对象Entity缓存
### . Introduction
### Ⅱ. 使用
- [orm](https://github.com/zfoo-project/zfoo/blob/main/orm/README.md) Based on Mongo DB's orm framework, it provides a
mapping between POJO objects and Mongo DB databases
- MongoDB is a distributed database that can be used on a single machine or distributed in a single machine
- High-performance database entity object caching based on [caffeine](https://github.com/ben-manes/caffeine)
#### 1. 直接使用(low level api),通过MongoDB官方提供的底层Api操作数据库
### Ⅱ. Use
- 首先通过OrmManager获取Collection
#### 1. Direct use (low level API), through the low-level API provided by MongoDB to operate the database
- Start by getting the Collection through Orm Manager
```
var collection = OrmContext.getOrmManager().getCollection(UserEntity.class)
```
- 通过collection操作集合
- collection.insertOne(obj)
- collection.deleteOne(obj)
- collection.updateOne(obj)
- collection.find(Filters.eq("_id", userId))
- Manipulate collections through collections
- insertcollection.insertOne(obj)
- deletecollection.deleteOne(obj)
- updatecollection.updateOne(obj)
- querycollection.find(Filters.eq("_id", userId))
#### 2. 间接使用(middle level api),通过collection的简易包装类IAccessorIQuery接口访问数据
#### 2. Indirect use (middle level API) to access data through the collection's simple wrapper IAccessor and IQuery interfaces
- IAccessor接口,为数据访问接口
- 插入数据到数据库,会以对象的id()方法的返回值作为主键
- IAccessor is a data access interface
- Inserting data into the database uses the return value of the object's id() method as the primary key
```
OrmContext.getAccessor().insert(obj)
```
- 删除数据库中的数据,会以对象的id()方法的返回值作为查找关键字,删除以这个id()为主键的数据
- To delete data in the database, the return value of the id() method of the object will be used as the search
keyword, and the data with this id() as the main key will be deleted
```
OrmContext.getAccessor().delete(obj);
```
- 修改数据库中的数据
- Modify the data in the database
```
OrmContext.getAccessor().update(obj);
```
- 加载数据库中的数据
- Load data from the database
```
OrmContext.getAccessor().load(id, class);
```
- IQuery接口,为数据复杂查询接口
- 目前提供的方法有:queryFieldLikequeryAllqueryFieldEqualqueryFieldInpageQuery
- IQuery is data complex query interface
- The methods currently offered are: queryFieldLikequeryAllqueryFieldEqualqueryFieldInpageQuery
#### 3. 缓存使用(high level api),缓存Entity定时刷新缓存到数据库
#### 3. Cache usage (high level API), cache entity regular refresh cache to the database
- 通过下列配置指定数据库和缓存的策略列表
- Specify a list of policies for the database and cache through the following configuration
```
<orm:config id="config" entity-package="com.zfoo.orm.**.entity">
@@ -54,14 +58,14 @@ var collection = OrmContext.getOrmManager().getCollection(UserEntity.class)
<orm:address name="${mongodb.address.name}" url="${mongodb.address.url}"/>
</orm:host>
<!-- 缓存策略 -->
<!-- Caching policy -->
<orm:caches>
<orm:cache strategy="default" size="6000" expire-millisecond="600000"/>
<orm:cache strategy="thousand" size="1000" expire-millisecond="600000"/>
<orm:cache strategy="threeThousand" size="3000" expire-millisecond="600000"/>
</orm:caches>
<!-- 持久化策略 -->
<!-- Persistence policy -->
<orm:persisters>
<orm:persister strategy="default" type="time" config="600000"/>
<orm:persister strategy="cron30s" type="cron" config="0/30 * * * * ?"/>
@@ -69,23 +73,26 @@ var collection = OrmContext.getOrmManager().getCollection(UserEntity.class)
</orm:persisters>
</orm:config>
database表示操作哪个数据库
address表示数据库的地址,支持分片的配置
database indicates which database to operate
address indicates the address of the database and supports sharding configuration
```
- 通过注解引入映射
- mappings through annotations
```
@EntityCaches(cacheStrategy = @Cache("tenThousand"), persister = @Persister("time30s"))
public class UserEntity implements IEntity<Long> {
}
cacheStrategy表示一个缓存的策略,即将数据库中的数据先读入Orm中的EntityCaches缓存,如thousand这个策略表示,缓存数据库中1000条数据,10分钟过期
persisters表示一个持久化的策略,如time30s这个策略表示,将EntityCaches中的缓存数据每30s写入到数据库中一次,即使中途宕机,也只损失30s秒的数据
cache strategy represents a caching strategy, the data in the database is first read into the Entity Caches cache in Orm,
such as thousand, which means that 1000 pieces of data in the cache database expire in 10 minutes
persisters represents a persistence strategy, such as time 30 s, which means that the cached data in the Entity Caches is
written to the database every 30 seconds, and even if it goes down midway, only 30 s seconds of data is lost
```
- 缓存的使用方法
1. 通过注解
- How to use the cache
1. By annotation
```
@Component
public class UserManager {
@@ -95,27 +102,32 @@ persisters表示一个持久化的策略,如time30s这个策略表示,将Ent
}
```
2. 通过类动态获取
2. Dynamically obtained through classes
```
IEntityCaches<Long, UserEntity> userEntityCaches = OrmContext.getOrmManager().getEntityCaches(UserEntity.class);
```
- 更新对象
- Update the object
```
userEntityCaches.update(entity);
```
### Ⅲ. 注意事项
### Ⅲ. Notes
- 如果不想映射某属性,直接加上transient关键字
- 支持基本数据属性(byteshortintlongfloatdoubleboolean),字符串String,自定义对象,不支持泛型
- 数组支持一维数组,集合支持List,Set
- Map类型MongoDB官方限定了key只能为String
- 数据库主键能用整数尽量用整数,因为MongoDB默认的主键是一个字符串,比较占空间
- 数据库使用自研的orm框架,比如一个实体类UserEntity,映射到数据库中的集合为user,首字母小写,去掉Entity
- 智能语法分析,不支持泛型和循环引用的对象,错误的entity对象定义将无法启动程序并给出错误警告
- If you don't want to map an attribute, just add the transient keyword
- Supports basic data properties (byte, short, int, long, float, double, boolean), string String, custom objects, does
not support generics
- Arrays support one-dimensional arrays, and collections support List, Set
- Map type: Mongo DB officially restricts key to String
- Database primary keys can use integers as much as possible, because Mongo DB's default primary key is a string, which
takes up space
- The database uses a self-developed ORM framework, such as an entity class User Entity, which maps to the collection in
the database as user, with the first letter lowercase, and the Entity removed
- Intelligent parsing, does not support generic and circular referenced objects, and the wrong entity object definition
will fail to start the program with an error warning
### Ⅳ. 教程
### Ⅳ. Tutorial
- [test](https://github.com/zfoo-project/zfoo/tree/main/orm/src/test/java/com/zfoo/orm) 下中包含了所有增删改查的教程,运行之前请先安装MongoDB
- [test](https://github.com/zfoo-project/zfoo/tree/main/orm/src/test/java/com/zfoo/orm) contains all tutorials for
adding, deleting, and modifying, please install Mongo DB before running
+123
View File
@@ -0,0 +1,123 @@
简体中文 | [English](./README.md)
### . 简介
- [orm](https://github.com/zfoo-project/zfoo/blob/main/orm/README.md) 基于MongoDB的orm框架,提供POJO对象和MongoDB数据库之间的映射
- mongodb是分布式数据库,可以单机使用,也可以分布式使用
- 基于 [caffeine](https://github.com/ben-manes/caffeine) 的高性能数据库实体对象Entity缓存
### Ⅱ. 使用
#### 1. 直接使用(low level api),通过MongoDB官方提供的底层Api操作数据库
- 首先通过OrmManager获取Collection
```
var collection = OrmContext.getOrmManager().getCollection(UserEntity.class)
```
- 通过collection操作集合
- 增,collection.insertOne(obj)
- 删,collection.deleteOne(obj)
- 改,collection.updateOne(obj)
- 查,collection.find(Filters.eq("_id", userId))
#### 2. 间接使用(middle level api),通过collection的简易包装类IAccessor和IQuery接口访问数据
- IAccessor接口,为数据访问接口
- 插入数据到数据库,会以对象的id()方法的返回值作为主键
```
OrmContext.getAccessor().insert(obj)
```
- 删除数据库中的数据,会以对象的id()方法的返回值作为查找关键字,删除以这个id()为主键的数据
```
OrmContext.getAccessor().delete(obj);
```
- 修改数据库中的数据
```
OrmContext.getAccessor().update(obj);
```
- 加载数据库中的数据
```
OrmContext.getAccessor().load(id, class);
```
- IQuery接口,为数据复杂查询接口
- 目前提供的方法有:queryFieldLikequeryAllqueryFieldEqualqueryFieldInpageQuery
#### 3. 缓存使用(high level api),缓存Entity定时刷新缓存到数据库
- 通过下列配置指定数据库和缓存的策略列表
```
<orm:config id="config" entity-package="com.zfoo.orm.**.entity">
<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="default" size="6000" expire-millisecond="600000"/>
<orm:cache strategy="thousand" size="1000" expire-millisecond="600000"/>
<orm:cache strategy="threeThousand" size="3000" expire-millisecond="600000"/>
</orm:caches>
<!-- 持久化策略 -->
<orm:persisters>
<orm:persister strategy="default" type="time" config="600000"/>
<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 = @Cache("tenThousand"), persister = @Persister("time30s"))
public class UserEntity implements IEntity<Long> {
}
cacheStrategy表示一个缓存的策略,即将数据库中的数据先读入Orm中的EntityCaches缓存,如thousand这个策略表示,缓存数据库中1000条数据,10分钟过期
persisters表示一个持久化的策略,如time30s这个策略表示,将EntityCaches中的缓存数据每30s写入到数据库中一次,即使中途宕机,也只损失30s秒的数据
```
- 缓存的使用方法
1. 通过注解
```
@Component
public class UserManager {
@EntityCachesInjection
public IEntityCaches<Long, UserEntity> userEntityCaches;
}
```
2. 通过类动态获取
```
IEntityCaches<Long, UserEntity> userEntityCaches = OrmContext.getOrmManager().getEntityCaches(UserEntity.class);
```
- 更新对象
```
userEntityCaches.update(entity);
```
### Ⅲ. 注意事项
- 如果不想映射某属性,直接加上transient关键字
- 支持基本数据属性(byteshortintlongfloatdoubleboolean),字符串String,自定义对象,不支持泛型
- 数组支持一维数组,集合支持List,Set
- Map类型MongoDB官方限定了key只能为String
- 数据库主键能用整数尽量用整数,因为MongoDB默认的主键是一个字符串,比较占空间
- 数据库使用自研的orm框架,比如一个实体类UserEntity,映射到数据库中的集合为user,首字母小写,去掉Entity
- 智能语法分析,不支持泛型和循环引用的对象,错误的entity对象定义将无法启动程序并给出错误警告
### Ⅳ. 教程
- [test](https://github.com/zfoo-project/zfoo/tree/main/orm/src/test/java/com/zfoo/orm) 下中包含了所有增删改查的教程,运行之前请先安装MongoDB
+115 -73
View File
@@ -1,117 +1,149 @@
### . 简介
English | [简体中文](./README_CN.md)
### . Introduction
- [zfoo protocol](https://github.com/zfoo-project/zfoo/blob/main/protocol/README.md)
是目前的Java二进制序列化和反序列化速度最快的框架,并且为序列化字节最少的框架
- 协议目前原生支持 **C++ Java Javascript C# Go Lua GDScript**,可以轻易实现跨平台
- 协议可以自定义私有协议格式,让你的协议更加安全,支持增加字段和兼容前后版本协议
- 兼容protobuf,支持生成protobuf协议文件,提供从pojo到proto的生成方式
- proto文件到生成pojo的方式已经有不少jetbrains插件可以做到,可以在idea的plugin中搜索proto
It is currently the fastest framework for binary serialization and deserialization in Java, and has the fewest
serialization bytes
- The protocol is currently natively supported **C++ Java Javascript C# Go Lua GDScript**It's easy to do cross-platform
- The protocol can customize the private protocol format to make your protocol more secure, and supports adding fields
and being compatible with previous and subsequent protocols
- Compatible with Protobuf, it supports the generation of Protobuf protocol files, and provides a generation method from
POJO to PROTO
- There are already quite a few JetBrains plugins that can do it in the way to generate POJOs, and you can search for
Proto in the plugin of Idea
### Ⅱ. 快速使用
- 环境要求 **JDK 11+**,可以在 **OpenJDK****Oracle JDK** 无缝切换
- Environmental requirements **JDK 11+**
- protocol是独立项目,不依赖其它项目,可以直接打开,本地install到自己的本地maven仓库,即可单独使用
- Protocol is an independent project, does not depend on other projects, can be directly opened, locally installed to
its own local maven repository, can be used alone
```
// zfoo协议注册,只能初始化一次
// The zfoo protocol is registered and can only be initialized once
ProtocolManager.initProtocol(Set.of(ComplexObject.class, ObjectA.class, ObjectB.class));
// 序列化
// serialization
ProtocolManager.write(byteBuf, complexObject);
// 反序列化
// deserialization
var packet = ProtocolManager.read(buffer);
```
### Ⅲ. 性能测试
### Ⅲ. Performance testing
- 单线程环境,在没有任何JVM参数调优的情况下速度比Protobuf快50%,比Kryo快100%[参见性能测试](src/test/java/com/zfoo/protocol/SpeedTest.java)
- 线程安全,zfoo和Protobuf的性能不受任何影响,kryo因为线程不安全性能会有所损失,[参见性能测试](src/test/java/com/zfoo/protocol/SpeedTest.java)
- Single-threaded environment, 50% faster than Protobuf and 100% faster than Kryo without any JVM parameter
tuning,[参见性能测试](src/test/java/com/zfoo/protocol/SpeedTest.java)
- Thread safety, the performance of zfoo and Protobuf is not affected in any way, kryo will lose some performance
because of thread insecurity,[参见性能测试](src/test/java/com/zfoo/protocol/SpeedTest.java)
- 测试环境
- Test the environment
```
操作系统win10
systemwin10
cpu i9900k
内存:64g
```
- 单线程测试,横坐标为序列化和反序列化的对象数量,纵坐标为花费的时间(单位毫秒)
- Single-threaded test with abscissa as the number of objects serialized and deserialized and time spent in milliseconds
on the ordinate
![Image text](../doc/image/protocol/simple_object.png)
![Image text](../doc/image/protocol/normal_object.png)
![Image text](../doc/image/protocol/complex_object.png)
### Ⅳ. 为什么快
### Ⅳ. Why fast
- 使用Javassist字节码增强动态生成顺序执行的序列化和反序列化函数,顺序化的函数可以轻易的被JIT编译以达到极致的性能
- 原生集成netty的高性能ByteBuf
- 使用primitive type collection,没有装箱和拆箱,避免了无效GC,性能也足够快
- 天生线程安全并且无锁化;kryo强制要求每条线程都有自己的一个Kryo实例,这是一个比较重的设计,特别是线程比较多的场景
- 没有反射,没有unsafe操作;对比kryo中使用objenesis导致大量unsafe,而且在Java11中运行会出现警告
- 扁平化了方法栈的调用深度,数据结构嵌套没有任何性能损失,如List<Set<Map<>>>;对比kryo和protobuf数据结构嵌套会出现性能损失
- 无漏洞注入风险,只有初始化时会进行字节码增强,后期不会再进行任何字节码的操作
- Use Javassist bytecode to enhance the dynamic generation of serialization and deserialization functions for sequential
execution, and sequential functions can be easily JIT compiled to achieve extreme performance
- Natively integrated with netty's high-performance Byte Buf
- With primitive type collection, there is no boxing and unboxing, invalid GCs are avoided, and the performance is fast
enough
- Inherently thread-safe and lock-free; kryo forces each thread to have its own instance of Kryo, which is a heavy
design, especially in scenarios with many threads
- No reflections, no unsafe operation; The use of objenesis in Kryo resulted in a lot of unsafe, and warnings when
running in Java 11
- Flattening the call depth of the method stack, and there is no performance penalty for nesting data structures, such
as List<Set<Map<>>>; Comparing kryo and protobuf data structure nesting results in a performance penalty
- There is no risk of vulnerability injection, only bytecode enhancement will be performed during initialization, and no
bytecode operations will be performed in the later stage
```
数据压缩体积小,压缩体积比kryo和protobuf都要小;比kryo小是因为kryo需要写入每个对象的注册号
智能语法分析,错误的协议定义将无法启动程序并给出错误警告
提升开发效率,完全支持POJO方式开发,使用非常简单
The data compression volume is small, and the compression volume is smaller than that of Kryo and Protobuf; Smaller than kryo because kryo needs to write the registration number of each object
Intelligent syntax, incorrect protocol definitions will fail to start the program and give an error warning
Improve development efficiency, fully support POJO development, very easy to use
```
### . 为什么小
### . Why small
- 轻量级实现,核心序列化和反序列化代码一千行左右
- 优化了int和long的zigzagvarint编码的算法,避免了一些多余的方法调用和位操作
- 数据压缩体积小,压缩体积比kryo和protobuf都要小;比kryo小是因为kryo需要写入每个对象的注册号
- 智能语法分析,错误的协议定义将无法启动程序并给出错误警告
- 提升开发效率,完全支持POJO方式开发,使用非常简单
- Lightweight implementation, core serialization and deserialization code of about a thousand lines
- Optimized the zigzag and varint encoding algorithms for int and long, avoiding some redundant method calls and bit
operations
- The data compression volume is small, and the compression volume is smaller than that of Kryo and Protobuf; Smaller
than kryo because kryo needs to write the registration number of each object
- Intelligent syntax, incorrect protocol definitions will fail to start the program and give an error warning
- Improve development efficiency, fully support POJO development, very easy to use
```
目前的序列化过后对象的大小如下:
简单对象,zfoo包体大小8kryo包体大小5protobuf包体大小8
常规对象,zfoo包体大小430kryo包体大小483protobuf包体大小793
复杂对象,zfoo包体大小2216kryo包体大小2528protobuf包体大小5091
The current size of the serialized object is as follows:
Simple objects, zfoo package size 8, kryo package size 5, protobuf package size 8
Regular objects, ZFOO package size 430, KRYO package size 483, Protobuf package size 793
For complex objects, ZFOO package size 2216, KRYO package size 2528, and Protobuf package size 5091
```
### Ⅵ. 数据类型
### Ⅵ. Data type
- 默认的数据格式支持,无需用户手动注册,[参考类定义](src/test/java/com/zfoo/protocol/packet/ComplexObject.java)
- Default data format support eliminates the need for users to register
manually.[参考类定义](src/test/java/com/zfoo/protocol/packet/ComplexObject.java)
- booleanbyteshortintlongfloatdoublecharString
- BooleanByteShortIntegerLongFloatDoubleCharacter序列化的时候如果null,会给个默认值0(Character默认值为Character.MIN_VALUE
- int[]Integer[],如果是null,则解析后的为一个长度为0的数组
- 原生泛型List,Set,Map,反序列化返回类型为HashSetArrayListHashMap,并且空指针安全(返回大小为0的集合)
- List<Integer>,必须指定泛型类,如果发送的是[1,1,null,1],接收到的是[1,1,0,1]
- List<XXXClass>,如果发送的是[obj,obj,null,obj],接收到的是[obj,obj,null,obj],即引用类型序列化之前为null,序列化之后同样为null
- BooleanByteShortIntegerLongFloatDoubleCharacterIf it is null during serialization, a default value of 0
will be given (the default value of Character is Character.MIN VALUE)
- int[]Integer[]If it is null, it is parsed as an array of length 0
- Native generic List, Set, Map, deserialization return type Hash Set, Array List, Hash Map, and null pointer
safe (returns a collection of size 0)
- List<Integer>You must specify a generic class if [1,1,null,1] is sent and [1,1,0,1] is received
- List<XXXClass>If [obj,obj,null,obj] is sent, [obj,obj,null,obj] is received, that is, the reference type is
null before serialization, and it is also null after serialization
- 不支持的数据格式,因为zfoo会自动识别不支持的类型并且给出错误警告,所以用户不必太关心
- int[][],二维以上数组,考虑到不是所有语言都支持多维数组
- List<Integer>[]Map<Integer, Integer>[]Java语言本身就没有支持泛型类数组
- List<int[]>Map<Integer, Integer[]>,泛型里面套数组,这种写法看起来比较奇怪,实际使用的地方很少
- 枚举类,考虑到很多其他语言不支持枚举类,可以用int或者string在代码层面做替换
- 自定义泛型类XXXClass<T>,泛型类在很多框架中都极易出现性能上和解析上的问题,而且并不是所有语言都支持
- 循环引用,虽然底层支持循环引用,但是考虑到循环引用带来语义上难以理解,容易出现错误,所以就屏蔽了
- Unsupported data formats, because ZFOO automatically recognizes unsupported types and gives error warnings, so users
don't have to care too much
- int[][]Arrays above two dimensions, considering that not all languages support multidimensional arrays
- List<Integer>[]Map<Integer, Integer>[]The Java language itself does not support generic class arrays
- List<int[]>Map<Integer, Integer[]>Generics are set in arrays, which looks strange and has few actual uses
- Enumerated classes, considering that many other languages do not support enumerated classes, can be replaced at
the code level with int or string
- Custom generic class XXX Class, <T>generic classes are prone to performance and parsing problems in many
frameworks, and are not supported in all languages
- Circular references, although the underlying support circular references, but considering that circular references
bring semantically difficult to understand and prone to errors, so they are blocked
### Ⅶ. 协议规范
### Ⅶ. Protocol specifications
- 协议类必须是简单的javabean,不能继承任何其它的类,但是可以继承接口
- The protocol class must be a simple javabean, not inheriting from any other class, but can inherit an interface
- 为了防止代码里Object满天飞,避免协议层和po层混用对象造成一些潜在的并发问题,zfoo强制要求协议类必须实现IPacket接口
- In order to prevent objects in the code and avoid some potential concurrency problems caused by the mixing of objects
in the protocol layer and the po layer, zfoo mandates that protocol classes must implement the IPacket interface
```
现在IPacket的接口只是一个标识接口,继承IPacket的设计主要是为了让代码更优雅,容易理解一点,改为只继承Object也并没有很大工作量
Now the interface of IPacket is just an identification interface, inheriting the design of IPacket is mainly to make
the code more elegant and easier to understand, and it is not a lot of work to inherit only Object
继承IPacket的设计还有跨语言层面上的考虑,极大的简化了实现其它语言的序列化和反序列化难度,统一了其它语言的代码实现
The design of inheriting IPacket also has cross-language considerations, which greatly simplifies the difficulty
of serialization and deserialization in other languages and unifies the code implementation of other languages
```
- 协议号定义为short类型是为了减少包体积和内存大小,一个包可以减少2个byte,每个协议的在应用内存也可以降低6byteprotocols + IProtocolRegistration + protocolIdMap
- The protocol number is defined as a short type to reduce the packet size and memory size, a packet can be reduced by 2
bytes, and the application memory of each protocol can also be reduced by 6 byte(protocols + IProtocolRegistration +
protocolIdMap)
```
一个项目的协议体类很难超过3w,会有工具自动把你的协议号打包的紧凑一点,这样你的协议号就不会超过3w了
It is difficult for a project's protocol body class to exceed 3 w, and there will be tools that automatically package
your protocol number a little more compactly, so that your protocol number will not exceed 3 w
```
- 协议类必须标注协议号,有两种方式
- 第一种使用注解:@Protocol(id = protocolId)
- There are two ways to indicate that the protocol class must be labeled with the protocol number
- The first uses annotations: @Protocol(id = protocolId)
```
@Protocol(id = 104)
public class SimpleObject implements IPacket {
@@ -122,7 +154,8 @@ cpu i9900k
}
```
- 第二种使用静态常量+接口:这个协议号的值必须和IPacket接口返回的值一样,这样的方式可以微弱的提升一点性能
- The second uses static constants + interfaces: the value of this protocol number must be the same as the value
returned by the IPacket interface, which can slightly improve performance
```
public class SimpleObject implements IPacket {
@@ -140,7 +173,8 @@ cpu i9900k
}
```
- 第三种使用:通过ProtocolManager.initProtocolAuto()去注册协议就可以不写协议号
- The third use: Register the agreement through Protocol Manager.initProtocolAuto() without writing the protocol
number
```
public class SimpleObject implements IPacket {
@@ -151,21 +185,29 @@ cpu i9900k
}
```
- 如果为了版本兼容,增加字段,需要加上Compatible注解,order需要自然增大,这样就可以保证新老协议可以互相兼容
- 正式环境为了版本兼容,避免修改字段名称,内部默认使用字段的名称按照字符串的自然顺序来依次读写的(也可以自定义),所以会导致序列化出现异常
- 正式环境为了版本兼容,避免减少字段,没必要一定要删除一个不需要的字段,所以不考虑支持这种减少字段兼容协议的情况
- 设计模式六大原则中的开闭原则是对扩展开放,对修改关闭。协议的设计涉及到功能应该也要遵守这个原则,优先增加新的协议而不是修改现有协议
- If you add a field for version compatibility, you need to add a Compatible annotation, and the order needs to be
naturally increased, so as to ensure that the old and new protocols can be compatible with each other
- In order to be compatible with versions and avoid modifying field names, default uses field names to read and write
in the natural order of strings (can also be customized), so it will cause exceptions in serialization
- The official environment does not necessarily have to delete an unwanted field in order to be version compatible and
avoid reducing fields
- Among the six principles of design patterns, the principle of opening and closing is open to expansion and closed to
modification. The design of the protocol should also adhere to this principle when it comes to functionality,
prioritizing the addition of new protocols over modifying existing ones
### Ⅷ. 在zfoo中使用Protobuf
### Ⅷ. Use Protobuf in zfoo
- zfoo只提供了jprotobuf那种pojo的方式使用protobuf,但是可以通过pojo对象生成proto文件给客户端使用
- zfoo only provides protobuf in the way of pojo like jprotobuf, but it can generate proto files for clients to use
through pojo objects
- [jprotobuf](https://github.com/jhunters/jprotobuf) 针对Java程序开发一套简易类库,可以直接使用简单的pojo对象无需再去了解proto文件操作与语法
- [jprotobuf](https://github.com/jhunters/jprotobuf) you can directly use simple pojo objects without having to
understand proto file operations and syntax
- 通过pojo对象生成proto文件给客户端使用,[生成proto配置](src/test/resources/protobuf.xml)
- Generate a proto file for the client to use through the pojo
object, [Generate a proto configuration](src/test/resources/protobuf.xml)
- 也可以自定义自己的生成方式,
[使用代码自定义生成proto](https://github.com/zfoo-project/tank-game-server/tree/main/common/src/main/java/com/zfoo/tank/common/generate)
- You can also customize your own build method,
[Use code custom generation to proto](https://github.com/zfoo-project/tank-game-server/tree/main/common/src/main/java/com/zfoo/tank/common/generate)
+176
View File
@@ -0,0 +1,176 @@
简体中文 | [English](./README.md)
### . 简介
- [zfoo protocol](https://github.com/zfoo-project/zfoo/blob/main/protocol/README.md)
是目前的Java二进制序列化和反序列化速度最快的框架,并且为序列化字节最少的框架
- 协议目前原生支持 **C++ Java Javascript C# Go Lua GDScript**,可以轻易实现跨平台
- 协议可以自定义私有协议格式,让你的协议更加安全,支持增加字段和兼容前后版本协议
- 兼容protobuf,支持生成protobuf协议文件,提供从pojo到proto的生成方式
- proto文件到生成pojo的方式已经有不少jetbrains插件可以做到,可以在idea的plugin中搜索proto
### Ⅱ. 快速使用
- 环境要求 **JDK 11+**,可以在 **OpenJDK****Oracle JDK** 无缝切换
- protocol是独立项目,不依赖其它项目,可以直接打开,本地install到自己的本地maven仓库,即可单独使用
```
// zfoo协议注册,只能初始化一次
ProtocolManager.initProtocol(Set.of(ComplexObject.class, ObjectA.class, ObjectB.class));
// 序列化
ProtocolManager.write(byteBuf, complexObject);
// 反序列化
var packet = ProtocolManager.read(buffer);
```
### Ⅲ. 性能测试
-
单线程环境,在没有任何JVM参数调优的情况下速度比Protobuf快50%,比Kryo快100%[参见性能测试](src/test/java/com/zfoo/protocol/SpeedTest.java)
-
线程安全,zfoo和Protobuf的性能不受任何影响,kryo因为线程不安全性能会有所损失,[参见性能测试](src/test/java/com/zfoo/protocol/SpeedTest.java)
- 测试环境
```
操作系统:win10
cpu i9900k
内存:64g
```
- 单线程测试,横坐标为序列化和反序列化的对象数量,纵坐标为花费的时间(单位毫秒)
![Image text](../doc/image/protocol/simple_object.png)
![Image text](../doc/image/protocol/normal_object.png)
![Image text](../doc/image/protocol/complex_object.png)
### Ⅳ. 为什么快
- 使用Javassist字节码增强动态生成顺序执行的序列化和反序列化函数,顺序化的函数可以轻易的被JIT编译以达到极致的性能
- 原生集成netty的高性能ByteBuf
- 使用primitive type collection,没有装箱和拆箱,避免了无效GC,性能也足够快
- 天生线程安全并且无锁化;kryo强制要求每条线程都有自己的一个Kryo实例,这是一个比较重的设计,特别是线程比较多的场景
- 没有反射,没有unsafe操作;对比kryo中使用objenesis导致大量unsafe,而且在Java11中运行会出现警告
- 扁平化了方法栈的调用深度,数据结构嵌套没有任何性能损失,如List<Set<Map<>>>;对比kryo和protobuf数据结构嵌套会出现性能损失
- 无漏洞注入风险,只有初始化时会进行字节码增强,后期不会再进行任何字节码的操作
```
数据压缩体积小,压缩体积比kryo和protobuf都要小;比kryo小是因为kryo需要写入每个对象的注册号
智能语法分析,错误的协议定义将无法启动程序并给出错误警告
提升开发效率,完全支持POJO方式开发,使用非常简单
```
### . 为什么小
- 轻量级实现,核心序列化和反序列化代码一千行左右
- 优化了int和long的zigzag和varint编码的算法,避免了一些多余的方法调用和位操作
- 数据压缩体积小,压缩体积比kryo和protobuf都要小;比kryo小是因为kryo需要写入每个对象的注册号
- 智能语法分析,错误的协议定义将无法启动程序并给出错误警告
- 提升开发效率,完全支持POJO方式开发,使用非常简单
```
目前的序列化过后对象的大小如下:
简单对象,zfoo包体大小8kryo包体大小5protobuf包体大小8
常规对象,zfoo包体大小430kryo包体大小483protobuf包体大小793
复杂对象,zfoo包体大小2216kryo包体大小2528protobuf包体大小5091
```
### Ⅵ. 数据类型
- 默认的数据格式支持,无需用户手动注册,[参考类定义](src/test/java/com/zfoo/protocol/packet/ComplexObject.java)
- booleanbyteshortintlongfloatdoublecharString
- BooleanByteShortIntegerLongFloatDoubleCharacter,序列化的时候如果null,会给个默认值0(Character默认值为Character.MIN_VALUE
- int[]Integer[],如果是null,则解析后的为一个长度为0的数组
- 原生泛型List,Set,Map,反序列化返回类型为HashSetArrayListHashMap,并且空指针安全(返回大小为0的集合)
- List<Integer>,必须指定泛型类,如果发送的是[1,1,null,1],接收到的是[1,1,0,1]
- 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>,泛型类在很多框架中都极易出现性能上和解析上的问题,而且并不是所有语言都支持
- 循环引用,虽然底层支持循环引用,但是考虑到循环引用带来语义上难以理解,容易出现错误,所以就屏蔽了
### Ⅶ. 协议规范
- 协议类必须是简单的javabean,不能继承任何其它的类,但是可以继承接口
- 为了防止代码里Object满天飞,避免协议层和po层混用对象造成一些潜在的并发问题,zfoo强制要求协议类必须实现IPacket接口
```
现在IPacket的接口只是一个标识接口,继承IPacket的设计主要是为了让代码更优雅,容易理解一点,改为只继承Object也并没有很大工作量
继承IPacket的设计还有跨语言层面上的考虑,极大的简化了实现其它语言的序列化和反序列化难度,统一了其它语言的代码实现
```
- 协议号定义为short类型是为了减少包体积和内存大小,一个包可以减少2个byte,每个协议的在应用内存也可以降低6byte(protocols +
IProtocolRegistration + protocolIdMap
```
一个项目的协议体类很难超过3w,会有工具自动把你的协议号打包的紧凑一点,这样你的协议号就不会超过3w了
```
- 协议类必须标注协议号,有两种方式
- 第一种使用注解:@Protocol(id = protocolId)
```
@Protocol(id = 104)
public class SimpleObject implements IPacket {
public int c;
public boolean g;
}
```
- 第二种使用静态常量+接口:这个协议号的值必须和IPacket接口返回的值一样,这样的方式可以微弱的提升一点性能
```
public class SimpleObject implements IPacket {
public static final transient short PROTOCOL_ID = 104;
public int c;
public boolean g;
@Override
public short protocolId() {
return PROTOCOL_ID;
}
}
```
- 第三种使用:通过ProtocolManager.initProtocolAuto()去注册协议就可以不写协议号
```
public class SimpleObject implements IPacket {
public int c;
public boolean g;
}
```
- 如果为了版本兼容,增加字段,需要加上Compatible注解,order需要自然增大,这样就可以保证新老协议可以互相兼容
- 正式环境为了版本兼容,避免修改字段名称,内部默认使用字段的名称按照字符串的自然顺序来依次读写的(也可以自定义),所以会导致序列化出现异常
- 正式环境为了版本兼容,避免减少字段,没必要一定要删除一个不需要的字段,所以不考虑支持这种减少字段兼容协议的情况
- 设计模式六大原则中的开闭原则是对扩展开放,对修改关闭。协议的设计涉及到功能应该也要遵守这个原则,优先增加新的协议而不是修改现有协议
### Ⅷ. 在zfoo中使用Protobuf
- zfoo只提供了jprotobuf那种pojo的方式使用protobuf,但是可以通过pojo对象生成proto文件给客户端使用
- [jprotobuf](https://github.com/jhunters/jprotobuf) 针对Java程序开发一套简易类库,可以直接使用简单的pojo对象无需再去了解proto文件操作与语法
- 通过pojo对象生成proto文件给客户端使用,[生成proto配置](src/test/resources/protobuf.xml)
- 也可以自定义自己的生成方式,
[使用代码自定义生成proto](https://github.com/zfoo-project/tank-game-server/tree/main/common/src/main/java/com/zfoo/tank/common/generate)
+30 -31
View File
@@ -1,41 +1,40 @@
### . 简介
English | [简体中文](./README_CN.md)
- [scheduler](https://github.com/zfoo-project/zfoo/blob/main/scheduler/README.md) 时间任务调度总线,cron表达式的解析使用了spring自带的解释器模式
### . Introduction
### Ⅱ. 时间任务调度
- [scheduler](https://github.com/zfoo-project/zfoo/blob/main/scheduler/README.md) The time task scheduling bus, cron
expression parsing uses Spring's own interpreter pattern
### Ⅱ. Time task scheduling
![Image text](../doc/image/scheduler/scheduler01.png)
- 前后调整本地机器时间都会触发任务调度,本地开发非常有用
- ScheduledExecutorService每秒钟执行一次triggerPerSecond()方法,循环遍历可执行的scheduler
- zfoo认为一个程序中不会有太多的时间任务调度,所以ScheduledExecutorService只有一条线程,所以使用者要避免做耗时和阻塞的运算,如果有这样的需求可以抛到其它线程池
- zfoo scheduler使用Javassist字节码增强技术动态代理时间调度任务,避免了反射,没有性能损耗
- Adjusting the local machine time before and after triggers task scheduling, and local development is very useful
- ScheduledExecutorService.triggerPerSecond() executed every secondloop through the executable scheduler
- zfoo believes that a program does not have much time to schedule, so the Scheduled Executor Service has only one
thread, so users should avoid doing time-consuming and blocking operations
- zfoo scheduler uses Javassist bytecode enhancement technology to dynamically proxy time scheduling tasks, avoiding
reflections and no performance loss
### Ⅲ. Cron Expression Example
```
30 * * * * ? 每半分钟触发任务
30 10 * * * ? 每小时的10分30秒触发任务
30 10 1 * * ? 每天1点1030秒触发任务
30 10 1 20 * ? 每月20号1点1030秒触发任务
30 10 1 20 10 ? * 每年10月20号1点1030秒触发任务
30 10 1 20 10 ? 2018 2018年10月20号1点1030秒触发任务
30 10 1 ? 10 * 2018 2018年10月每天20号1点10分30秒触发任务
30 10 1 ? 10 SUN 2018 2018年10月每周日1点1030秒触发任务
15,30,45 * * * * ? 每分钟的15,30,45秒个触发一次
15-45 * * * * ? 每分钟的15秒到45秒内,每秒都触发一次
15/5 * * * * ? 每分钟的15秒开始触发,每隔5秒触发一次
15-30/5 * * * * ? 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
0 0/3 * * * ? 每小时的第0分0秒开始,没三分钟触发一次
0 15 10 ? * MON-FRI 星期一到星期五每天1015分0秒触发一次
0 15 10 L * ? 每个月的最后一天的1015分0秒触发任务
0 15 10 LW * ? 每个月最后一个工作日的1015分0秒触发任务
0 15 10 ? * 5L 每个月最后一个星期四的1015分0秒触发任务
0 15 10 ? * 5#3 每个月第三周的星期四的1015分0秒触发任务
说明:
*(星号):代表任何时刻都接受癿意思
,(逗号):代表分隔时段的意思
-(减号):代表一段时间范围内
/n(斜线):每隔n单位间隔
30 * * * * ? Task is triggered every half minute
30 10 * * * ? Trigger tasks every hour at 10 minutes and 30 seconds
30 10 1 * * ? Task is triggered every day at 1:10:30 seconds
30 10 1 20 * ? Task is triggered at 1:10:30 on the 20th of each month
30 10 1 20 10 ? * Task is triggered every year on October 20 at 1:10:30 seconds
30 10 1 20 10 ? 2018 Task is triggered on October 20, 2018 at 1:10:30 a.m
30 10 1 ? 10 * 2018 Task is triggered every day on the 20th at 1:10:30 seconds In October 2018
30 10 1 ? 10 SUN 2018 Task is triggered every Sunday in October 2018 at 1:10:30 seconds
15,30,45 * * * * ? Task is triggered every 15, 30, 45 seconds per minute
15-45 * * * * ? Task is triggered every second from 15 seconds to 45 seconds per minute
15/5 * * * * ? Triggers start at 15 seconds per minute and trigger every 5 seconds
15-30/5 * * * * ? Triggers start between 15 and 30 seconds per minute and trigger every 5 seconds
0 0/3 * * * ? It starts at 0:0 every hour and triggers every three minutes
0 15 10 ? * MON-FRI Triggered once a day from Monday to Friday at 10:15:00 seconds
0 15 10 L * ? Task is triggered at 10:15:00 on the last day of each month
0 15 10 LW * ? Task is triggered at 10:15:00 on the last working day of each month
0 15 10 ? * 5L Task is triggered at 10:15:00 on the last Thursday of every month
0 15 10 ? * 5#3 Task is triggered on the Thursday of the third week of every month at 10:15:00
```
+37
View File
@@ -0,0 +1,37 @@
简体中文 | [English](./README.md)
### . 简介
- [scheduler](https://github.com/zfoo-project/zfoo/blob/main/scheduler/README.md) 时间任务调度总线,cron表达式的解析使用了spring自带的解释器模式
### Ⅱ. 时间任务调度
![Image text](../doc/image/scheduler/scheduler01.png)
- 前后调整本地机器时间都会触发任务调度,本地开发非常有用
- ScheduledExecutorService每秒钟执行一次triggerPerSecond()方法,循环遍历可执行的scheduler
- zfoo认为一个程序中不会有太多的时间任务调度,所以ScheduledExecutorService只有一条线程,所以使用者要避免做耗时和阻塞的运算,如果有这样的需求可以抛到其它线程池
- zfoo scheduler使用Javassist字节码增强技术动态代理时间调度任务,避免了反射,没有性能损耗
### Ⅲ. Cron Expression Example
```
30 * * * * ? 每半分钟触发任务
30 10 * * * ? 每小时的10分30秒触发任务
30 10 1 * * ? 每天1点10分30秒触发任务
30 10 1 20 * ? 每月20号1点10分30秒触发任务
30 10 1 20 10 ? * 每年10月20号1点10分30秒触发任务
30 10 1 20 10 ? 2018 2018年10月20号1点10分30秒触发任务
30 10 1 ? 10 * 2018 2018年10月每天20号1点10分30秒触发任务
30 10 1 ? 10 SUN 2018 2018年10月每周日1点10分30秒触发任务
15,30,45 * * * * ? 每分钟的15,30,45秒个触发一次
15-45 * * * * ? 每分钟的15秒到45秒内,每秒都触发一次
15/5 * * * * ? 每分钟的15秒开始触发,每隔5秒触发一次
15-30/5 * * * * ? 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
0 0/3 * * * ? 每小时的第0分0秒开始,没三分钟触发一次
0 15 10 ? * MON-FRI 星期一到星期五每天10点15分0秒触发一次
0 15 10 L * ? 每个月的最后一天的10点15分0秒触发任务
0 15 10 LW * ? 每个月最后一个工作日的10点15分0秒触发任务
0 15 10 ? * 5L 每个月最后一个星期四的10点15分0秒触发任务
0 15 10 ? * 5#3 每个月第三周的星期四的10点15分0秒触发任务
```
+23 -18
View File
@@ -1,24 +1,28 @@
### . 简介
English | [简体中文](./README_CN.md)
### . Introduction
- [storage](https://github.com/zfoo-project/zfoo/blob/main/storage/README.md)
Excel和Java类自动映射框架,只需要定义一个和Excel对应的类,直接解析Exceljsoncsv
Excel and Java class automatic mapping framework, only need to define a class corresponding to Excel, directly parse
Excel, json, csv
- 利用Java动态语言的反射特性,无需任何代码就能解析Excel
- Take advantage of the reflection nature of the Java dynamic language to parse Excel without any code
- 支持Excel导出jsoncsv文件
- Support Excel to export json, csv files
### Ⅱ. 自动映射
### Ⅱ. Automatic mapping
- Excel的第一行对应Java类属性,第二行和第三行不会起到注释的作用,其中第一列必须是Id属性
- The first line of Excel corresponds to the Java class attribute, and the second and third lines do not serve as
comments, where the first column must be the Id attribute
![Image text](../doc/image/storage/storage01.png)
- Excel对应的Java类
- The Java class for Excel
![Image text](../doc/image/storage/storage02.png)
- 解析过后有两种使用方式
1. 通过注解
- After parsing, there are two ways to use it
1. By annotation
```
@Component
public class StudentManager {
@@ -28,31 +32,32 @@
}
```
2. 通过类动态获取
2. Dynamically obtained through classes
```
Storage<Integer, StudentResource> studentResources = (Storage<Integer, StudentResource>) StorageContext.getStorageManager().getStorage(StudentResource.class);
```
- 通过id找到对应的行
- Find the corresponding row by id
```
var studentResource = studentResources.get(1000);
```
- 通过索引找对应的行,默认为可重复的索引,返回了一个列表list
- Find the corresponding row by index, default to a repeatable index, and return a list
```
var students = studentResources.getIndex("name", "james0");
```
- 唯一索引通过Storage.getUniqueIndex()获取,需要把索引注解标注为@Index(unique = true)
- The unique index is obtained through Storage.getUniqueIndex(), and the index annotation needs to be marked as @Index(
unique = true)
### Ⅲ. 热更新Excel/Json
### Ⅲ. Hot update Excel Json
- [tank](https://github.com/zfoo-project/tank-game-server/blob/main/common/src/main/java/com/zfoo/tank/common/util/HotUtils.java)
分布式热更新Excel/Json/csv配置文件实现
Distributed hot update Excel Jsoncsv configuration file implementation
### Ⅳ. 用途
### Ⅳ. Use
- 财务分析,数据分析统计
- 游戏中的数值配置
- Financial analysis, data analysis statistics
- Numerical configuration in the game
+60
View File
@@ -0,0 +1,60 @@
简体中文 | [English](./README.md)
### . 简介
- [storage](https://github.com/zfoo-project/zfoo/blob/main/storage/README.md)
Excel和Java类自动映射框架,只需要定义一个和Excel对应的类,直接解析Exceljsoncsv
- 利用Java动态语言的反射特性,无需任何代码就能解析Excel
- 支持Excel导出jsoncsv文件
### Ⅱ. 自动映射
- 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/Json
- [tank](https://github.com/zfoo-project/tank-game-server/blob/main/common/src/main/java/com/zfoo/tank/common/util/HotUtils.java)
分布式热更新Excel/Json/csv配置文件实现
### Ⅳ. 用途
- 财务分析,数据分析统计
- 游戏中的数值配置