Technical Documentation: zfoo-project/zfoo
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
English | 简体中文
Ⅰ. Introduction of zfoo🚩
- Extremely fast, asynchronous, actor design, lock free, universal RPC framework, native GraalVM support
- High scalability, Single server deployment, microservice deployment, cluster deployment, gateway deployment
- Can be used as a game server framework or website server framework
- zfoo protocol supports C++ Rust Java/Kotlin/Scala JavaScript/TypeScript/ES C# Go Php Ruby Lua GDScript Python Dart Swift
Perfect work development process, complete online solution
- Spring projects, distributed projects, container projects, hot update code without
downtime hotswap - Excel json csv configuration is automatically mapped and parsed, Online hotswap
configuration storage - Automapping framework for MongoDB orm
- Event bus event
- Time task scheduling scheduler
- Cpu, memory, hard disk, network monitoring built into the program no code and extra tools
required monitor
Ⅱ. Who uses this project
- Projects with extremely high-performance requirements, such as website and game server frameworks, single server, global server, live chat, IM system, real-time push
- Projects such as saving, development, deployment, operation and maintenance costs
- As a backend infrastructure for Godot, Unity, Cocos,Webgl, H5, Network protocol supports tcp udp websocket http
- Keep it Simple and Stupid, simple configuration, lightweight code
Ⅲ. Maven dependency✨
- Environment requirement JDK 17+, support OpenJDK, Oracle JDK and native GraalVM
<dependency>
<groupId>com.zfoo</groupId>
<artifactId>boot</artifactId>
<version>4.1.4</version>
</dependency>- If you don't want to depend on all zfoo modules, you only need to choose to depend on one of them
<dependency>
<groupId>com.zfoo</groupId>
<artifactId>protocol</artifactId>
<version>4.1.4</version>
</dependency>Ⅳ. Tutorials
- zfoo sdk of csharp and lua for unity and godot
- zfoo sdk typescript javascript cocos web h5
- zfoo sdk gdscript for godot
- godot-bird bird and bird,powered by godot
- FAQ,There are standard demo display and instructions in the test folder of each project directory,
which can be run directly
Ⅴ. Usage⭐
1. [protocol](protocol/README.md) ultimate performance serialization and deserialization
// protocol registration
ProtocolManager.initProtocol(Set.of(ComplexObject.class, ObjectA.class, ObjectB.class));
// serialization
ProtocolManager.write(byteBuf, complexObject);
// deserialization
var packet = ProtocolManager.read(byteBuf);2. [net](net/README.md) ultimate performance RPC framework, supports tcp udp websocket http
// Service provider, only need to add an annotation to the method, the interface will be automatically registered
@PacketReceiver
public void atUserInfoAsk(Session session, UserInfoAsk ask) {
}
// Consumers, synchronously requesting remote service, will block the current thread
var ask = UserInfoAsk.valueOf(userId);
var answer = NetContext.getCosumer().syncAsk(ask, UserInfoAnswer.class, userId).packet();
// Consumers, asynchronously requesting remote service, and will still execute logic in the current thread after the asynchronous
NetContext.getCosumer().asyncAsk(ask, UserInfoAnswer.class, userId)
.whenComplete(answer -> {
// do something
);3. [hotswap](hotswap/src/test/java/com/zfoo/hotswap/ApplicationTest.java) hot update code, no need to stop the server, no additional configuration, just one line of code to start hot update
// Pass in the class file that needs to be updated
HotSwapUtils.hotswapClass(bytes);4. [orm](orm/README.md) automatic mapping framework based on mongodb
// You don't need to write sql and any configuration yourself, define a table in the database directly through annotation definitions
@EntityCache
public class UserEntity implements IEntity<Long> {
@Id
private long id;
private String name;
}
// update database data
entityCaches.update(userEntity);5. [event](event/src/test/java/com/zfoo/event/ApplicationTest.java) use the observer design pattern, decouples different modules and improves the quality of the code
// To receive an event, you only need to add an annotation to the method and the method will be automatically listen for the event
@EventReceiver
public void onMyNoticeEvent(MyNoticeEvent event) {
// do something
}
// fire an event
EventBus.post(MyNoticeEvent.valueOf("My event"));6. [scheduler](scheduler/README.md) scheduling Framework Based on Cron Expression
@Scheduler(cron = "0/1 * * * * ?")
public void cronSchedulerPerSecond() {
// do something
}7. [storage](storage/src/test/java/com/zfoo/storage/ApplicationTest.java) Excel to class automatic mapping framework, you only need to define a class corresponding to Excel, and directly parse Excel
@Storage
public class StudentResource {
@Id
private int id;
@Index
private String name;
private int age;
}Ⅵ. Commit specification👏
- People who like this project are welcome to maintain this project together, and pay attention to the following
specifications when submitting code - The code formats use the default formatting of IntelliJ Idea
- conventional-changelog-metahub
Ⅶ. License
zfoo use Apache License Version 2.0
Overview (`boot/README.md`)
zfoo spring boot support
- zfoo boot makes it easy to create Spring Boot application using zfoo by using auto-configure
Overview (`event/README.md`)
English | 简体中文
Ⅰ. Introduction
- event The event bus is used to decouple different
modules and improve the quality of the code. The core idea is the observer design pattern
Ⅱ. Event bus at the hardware level
1. Between devices communication
2. Reduced complexity with the bus
3. Multiple Architectures of Hardware Buses
The core of the design idea of the bus is to reduce the complexity and coupling degree of interaction between multiple
modules.
At the actual hardware level, a bus is actually a set of wires connecting circuits.
Because of the differences in speed between different devices, there are often multiple buses in a computer.
In fact, the design idea of the bus is often used in our software development process.
The event bus is a common design pattern. Usually, the event bus will also be combined with the subscriber-publisher
mode to
become a main mode of interaction between loosely coupled modules of a large-scale system.
Ⅲ. Event bus at software level
- The event bus is called the observer mode from the design mode
- There is no fixed way to write the design pattern, zfoo event implements the event bus with the simplest annotation
- zfoo event uses Javassist bytecode enhancement technology to dynamically proxy event receivers, avoiding reflection
and no performance loss
Overview (`hotswap/README.md`)
English | 简体中文
Ⅰ. Introduction
- hotswap Hot update code, no need to stop the
server, no additional configuration, one line of code to enable hot update
Ⅱ. Stand-alone hot update
- directly pass in the class file that needs hot updatetext
// pass in the class file that needs to be updated HotSwapUtils.hotswapClass(bytes);
Ⅲ. Distributed Hot Update
upload the class files that need to be updated to the zookeeper registration center, and other servers monitor the
class files that need to be updated, and update the code when they are monitoredtank
Distributed hot update code implementation
Overview (`monitor/README.md`)
English | 简体中文
Ⅰ. Introduction
- monitor Lightweight cpu, memory, hard disk,
network monitoring
Ⅱ. Use
uptime, imitating the uptime command of Linux, can be used to monitor the load of cpu
textUptimeVO uptime = OSUtils.uptime();df, imitating the Linux df command, can be used to monitor the hard disk capacity
textList<DiskFileSystemVO> df = OSUtils.df();free, imitating Linux's free command, can be used to monitor memory usage
textMemoryVO free = OSUtils.free();sar, the sar command imitating Linux, can be used to monitor network IO
textList<SarVO> sar = OSUtils.sar();
Ⅲ. Traditional Server Monitoring
Traditional stand-alone server monitoring mainly depends on cpu, memory, hard disk, network
Distributed server monitoring is mainly to monitor multiple servers, which is not much different from stand-alone
monitoring.textIt 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
- 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
Overview (`net/README.md`)
English | 简体中文
Ⅰ. Introduction
- net Asynchronous, actor design,
lock-free, universal RPC framework based on Spring's MVC-style usage
- Elegant synchronous and asynchronous requests, faster, supports tcp udp websocket
- Service registration and discovery, the configuration center uses zookeeper, which can be extended to other
registration centers - Built-in high-performance gateway, custom forwarding strategy
- Scalable services, load balancing, cluster monitoring, everything you expect
Ⅱ. 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
- IRouter(low level api) The underlying network interface, which sends messages directly through the Session
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,local configuration, zookeeper registration discovery, and request load balancing are all placed under this package
core,unified package of core package, server and client
router,message routing
handler,netty's handler defines some common handlers for clients and servers
protocol,registration of message classes, encoding and decoding of messages, bytecode enhancement, etc.
schema,analysis of spring's custom tags
session,encapsulation of netty's channel
task,task thread pool, task distribution- 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
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:
dubbo,The code is heavyweight, asynchronous calls are cumbersome, there is no gateway, and it is impossible to directly interact with the client;
grpc,The code is heavyweight, complex to use, complex to deploy, no gateway, difficult to customize;
akka,Using 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 very fast 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
Ⅲ. Why fast
- Use current best performin zfoo protocol 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
- Single server +
database,zfoo net Single server usage tutorial
- 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- Introducing a load
balancer, zfoo net Gateway Load Balancing Tutorial
The load balancer distributes the requests to the two servers. User 1 goes left, User 2 goes right, User 3 goes left again.- extended database
The integrated orm is based on mongodb, which can support distributed deployment
mongodb,Replica set deployment (master-slave mode), data synchronization, read-write separation,
mongodb,Fragmentation deployment, failover (disaster recovery)- microservice,Microservice RPC Tutorial
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- Caching and Content Delivery Network (CDN)
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.- message queue
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- Fragmentation, partitioning, such as assigning users to the closest server to provide services
Ⅴ. Dependent middleware zookeeper tutorial
- Addition, deletion, modification and inspection of ZK basics
- Curator additions, deletions, modifications
- Distributed auto-incrementing ID implemented by zookeeper
- Distributed Barrier
- Distributed locks
- Data publishing and subscribing
- Zookeeper's distributed election algorithm
Ⅵ. Reference article
Overview (`orm/README.md`)
English | 简体中文
Ⅰ. Introduction
- orm Based on MongoDB'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
- Orm can directly use the MongoDB official driver to operate the database
Ⅱ. Use
1. Direct use (low level api) to directly operate the database through the api provided by the official MongoDB driver
- Start by getting the Collection through Orm Manager
var collection = OrmContext.getOrmManager().getCollection(UserEntity.class)- Manipulate collections through collections
- insert,collection.insertOne(obj)
- delete,collection.deleteOne(obj)
- update,collection.updateOne(obj)
- query,collection.find(Filters.eq("_id", userId))
2. Indirect use (middle level API) to access data through the collection's simple wrapper IAccessor and IQuery interfaces
IAccessor is a data access interface
- Inserting data into the database uses the field value of the object's field which marked
@Idas the primary key
textOrmContext.getAccessor().insert(obj)- 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
textOrmContext.getAccessor().delete(obj);- Modify the data in the database
textOrmContext.getAccessor().update(obj);- Load data from the database
textOrmContext.getAccessor().load(id, class);- Inserting data into the database uses the field value of the object's field which marked
IQuery is data complex query interface
- The methods currently offered are: queryFieldLike,queryAll,queryFieldEqual,queryFieldIn,pageQuery
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">
<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 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> {
}
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 lostHow to use the cache
- By annotation
text@Component public class UserManager { @EntityCachesInjection public IEntityCaches<Long, UserEntity> userEntityCaches; }- Dynamically obtained through classes
textIEntityCaches<Long, UserEntity> userEntityCaches = OrmContext.getOrmManager().getEntityCaches(UserEntity.class);Update the object
userEntityCaches.update(entity);Ⅲ. Notes
- 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
- 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 contains all tutorials for
adding, deleting, and modifying, please install Mongo DB before running
Overview (`protocol/README.md`)
English | 简体中文
Ⅰ. Introduction
- A decentralized serialization library, secure, private, reliable, and compatible, allowing everyone to have a unique
set of protocols - 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 - Support to generate ProtoBuf's Proto file to Pojo protocol file, and the generated protocol can be converted to other
protocols and used by Java - The protocol is currently natively supported C++ Rust Java/Kotlin/Scala JavaScript/TypeScript/ES C# Go Php Ruby Lua
GDScript Python Dart Swift
Ⅱ. Quick use
- Environment requirement JDK 17+, support OpenJDK, Oracle JDK and native GraalVM
// 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
Test the environment
system:win10
cpu: i9900k
memory:64g- Single-threaded test with abscissa as the number of objects serialized and deserialized and time spent in milliseconds
on the ordinate
Ⅳ. Why fast
- 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 ByteBuf, support Zero Copy
- With primitive type collection, there is no boxing and unboxing, invalid GCs are avoided, and the performance is fast
enough - thread-safe and lock-free, without any performance loss in multi-threaded environment
- No reflection, no unsafe operations, support GraalVM
- 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 - Taking advantage of the principle of program locality, the serializer is placed in the short[] array, and the object
serialization and deserialization will only look for the serializer once
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
- 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
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
Default data format support eliminates the need for users to register
manually.Reference class definition- boolean,byte,short,int,long,float,double,String
- Boolean,Byte,Short,Integer,Long,Float,Double,String,If it is null during serialization, a default value will be
given - 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,You must specify a generic class if [1,1,null,1] is sent and [1,1,0,1] is received
- List
,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
- Native generic List, Set, Map, deserialization return type Hash Set, Array List, Hash Map, and null pointer
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[],Map[],The Java language itself does not support generic class arrays
- List,Map,Generics are set in arrays, which looks strange and has few actual uses
- char or Character, many other languages do not support char and can be replaced with string
- Enumerated classes, many other languages do not support enumerated classes and can be replaced with int or string
- Custom generic class XXX Class,
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
The protocol class must be a simple javabean, not inheriting from any other class, but can inherit an interface
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)
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 wThere are four ways to indicate that the protocol class
The first uses annotations: @Protocol(id = protocolId)
text@Protocol(id = 104) public class SimpleObject { public int c; public boolean g; }The second use: Register the agreement through Protocol Manager.initProtocolAuto() without writing the protocol
numbertextpublic class SimpleObject { public int c; public boolean g; }The third use: Register the protocol through ProtocolManager.initProtocol(xmlProtocols) in the protocol.xml file
text<protocols> <module id="1" name="common"> <protocol id="100" location="com.zfoo.protocol.packet.ComplexObject"/> <protocol id="101" location="com.zfoo.protocol.packet.NormalObject"/> <protocol id="102" location="com.zfoo.protocol.packet.ObjectA"/> <protocol id="103" location="com.zfoo.protocol.packet.ObjectB"/> <protocol id="104" location="com.zfoo.protocol.packet.SimpleObject"/> <protocol id="105" location="com.zfoo.protocol.packet.VeryBigObject"/> <protocol id="106" location="com.zfoo.protocol.packet.EmptyObject"/> </module> <module id="2" name="native"> <protocol location="com.zfoo.net.packet.common"/> <protocol location="com.zfoo.tank.common.protocol.common"/> </module> </protocols>The fourth use: generate a protocol with a protocol number by defining a proto file, so that the protocol can be
easily registeredtextsyntax = "proto3"; package test.message; message SimpleObject { int64 aa = 1; } // If the tag of a field exceeds 1000, the field is considered to be a compatible protocol field message OneMessage { // This is a comment on the field int64 id = 1; // This is equivalent to adding a @Compatible annotation to this field string name = 1001; }
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, Protocol
nesting is also still compatibleIn 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 serializationThe official environment does not necessarily have to delete an unwanted field in order to be version compatible and
avoid reducing fieldsAmong 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
Ⅷ. The difference between zfoo and Protobuf
- Protobuf can delete fields, but ZFOO does not support deleting fields, which improves performance by 1x and reduces
the size by 1x at the expense
In fact, the officially launched project almost did not encounter anyone who would delete the field,
and if this field was deleted, the server had to change the code not to reference the deleted field,
and the client had to change the code not to reference the deleted field, which doubled the workload.
If either of them forgets to modify the code, it will directly report an error,
so adding a discarded field @Deprecated comment to this field in the actual project can avoid a lot of unnecessary trouble.zfoo takes the intersection of type declarations in all languages, instead of protobuf taking the union, simplifying
the type implementation of protobuf- protobuftext
double float int32 int64 uint32 uint64 sint32 sint64 fixed32 fixed64 sfixed32 sfixed64 bool string bytes bytes - zfootext
float double byte int16 int32 int64 bool string
- protobuf
zfoo takes the intersection of the grammars of all languages, instead of the union of protobuf, and adds the grammar
implementation of protobuf- protobuftext
Collection nesting syntax is not supported - zfootext
Supports nested collection syntax
- protobuf
Simplified protobuf syntax, no enumeration, no oneof, no optional
Overview (`scheduler/README.md`)
English | 简体中文
Ⅰ. Introduction
- scheduler The time task scheduling bus, cron
expression parsing uses Spring's own interpreter pattern
Ⅱ. Time task scheduling
- Adjusting the local machine time before and after triggers task scheduling, and local development is very useful
- ScheduledExecutorService.triggerPerSecond() executed every second,loop 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
/* Detailed source-code truncated for AI context efficiency. */Overview (`storage/README.md`)
English | 简体中文
Ⅰ. Introduction
storage
Java pojo and excel/csv/json automatic mapping framework, only need to define a class corresponding to Excel, directly parse
Excel, json, csvTake advantage of the reflection nature of the Java dynamic language to parse Excel without any code
Arrays or collections support null safe, and empty arrays or collections resolve to arrays or collections of length 0
Empty strings are parsed to string of length 0, and empty object are parsed to null
Support Excel to export json, csv files
Ⅱ. Automatic mapping
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 attributeThe Java class for Excel
In addition, you can also use the record class provided by Java's new features
After parsing, there are two ways to use it
- By annotation
text@Component public class StudentManager { @ResInjection private IStorage<Integer, StudentResource> studentResources; }- Dynamically obtained through classes
textIStorage<Integer, StudentResource> studentResources = StorageContext.getStorageManager().getStorage(StudentResource.class);Find the corresponding row by id
var studentResource = studentResources.get(1000);Alternatively, it can be obtained through the following methods
var resource = StorageContext.get(StudentResource.class, 1001);- Find the corresponding row by index, default to a repeatable index, and return a list
var students = studentResources.getIndex(StudentResource::getName, "james0");If you are a record class, the usage is as follows
var students = studentStorage.getIndexes(StudentResource::name, "james0");- The unique index is obtained through Storage.getUniqueIndex(), and the index annotation needs to be marked as @Index(
unique = true)
StudentResource student = storage.getUniqueIndex(StudentResource::idCard, "110101200007281903");Ⅲ. Hot update Excel Json
- tank
Distributed hot update Excel Json csv configuration file implementation
Ⅳ. Use
- Financial analysis, data analysis statistics
- Numerical configuration in the game
--- METRICS ---
- Files Extracted: 10
- Estimated Token Budget: ~9620 tokens
- Canonical Reference: https://codewiki.google/github.com/zfoo-project/zfoo