The previous article mainly discussed the problems encountered before designing the Phoenix framework and the ideas for designing the framework in the article 《Phoenix Framework: Designing a Business Concurrency Framework from 0 to 1 for the Xiaomi Mall Product Site》. This article mainly discusses how to design the framework.
PhoenixFramework is a framework that automatically constructs directed graphs, builds concurrent groups according to depth, and performs concurrent invocation of results.
Both the static and dynamic interfaces of the product site business need to call a large number of backend services to obtain data for business orchestration, and there are dependencies between each concurrent call. By using concurrent group design to break down dependencies and control concurrent calls, and using a unified Transfer layer for BO to DTO conversion, developers only need to define the Task and Transfer code logic for each call event and directly return the business data.
Glossary#
- PhoenixFramework: The name of the Phoenix (Phoenix) framework for this business concurrency framework.
- Task: Defines a call in business concurrency, which can be an HTTP, DUBBO, or Redis retrieval, or a MySQL read operation.
- Transfer: In business definition, it is the conversion logic of a sub-business module that converts BO data into DTO data.
Task and Trans Annotations#
How to Define a Task#
At the beginning of the framework design, we had two internal options. One was to implement Task by inheriting an abstract class. Task was defined by implementing the PhoenixTask class through inheritance. The other option was to use annotations to define each Task as a Task with strong constraints, and to define detailed description information in the annotations, providing developers with a clear design idea.
After internal discussions, we chose the excellent language feature of Java, which is to declare and define Task using annotations. This definition makes the code concise and clear, and is also conducive to collecting our definitions through Spring Bean collection tools.
/**
* PhoenixTask
* Task annotation
*
* @author debuginn
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PhoenixTask {
String taskName(); // Task name
String[] beforeTaskName() default {}; // Preceding tasks
String[] filterPlatform() default {}; // Filter channels, blacklist
String[] taskBoName(); // BO data generated by the task, used to check conflicts
}
The PhoenixTask
annotation is defined very simply:
taskName
is used to identify the task name and is enforced by enumeration for unique naming.beforeTaskName
is the preceding task. As mentioned earlier, each task is an event. Distinguishing the preceding task requires waiting for the return of the result during concurrent invocation, and then using it as the preceding parameter for this Task call.filterPlatform
is the filter channel, which is the functionality of the blacklist. If the request channel is declared as a blacklist in Task, it will be automatically blocked during concurrent execution.taskBoName
is the data that the task is converted into BO. It is used to obtain data through interface calls or middleware and convert it into data used by the Transfer layer. Data parameter verification is performed at the framework layer.
How to Define a Trans#
Trans is short for Transfer. Just like Task design, it is also defined using annotations:
/**
* PhoenixTrans
* Business orchestration annotation
*
* @author debuginn
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface PhoenixTrans {
String transName(); // Business orchestration block name
String apiName(); // Execute using concurrent API
String[] tasks() default {}; // Dependent task tasks
}
The PhoenixTrans
annotation is defined in a similarly simple way:
transName
is used to identify the name of the business orchestration block.apiName
is used to distinguish which concurrent API this Transfer business orchestration belongs to.tasks
defines the dependent Task tasks. A business orchestration may use BO data returned by n tasks for orchestration.
You may be confused here. Why is apiName not defined in Task but in Transfer? This is because in the design, in order for Task to be shared by n concurrent APIs, apiName is defined in Transfer. Then, by defining the dependent Task through tasks, you can infer which API the Task is currently used by.
How to Collect Task and Trans#
After customizing the PhoenixTask
and PhoenixTrans
annotations, collect the defined annotations by declaring an AnnotationProcessor
that inherits BeanPostProcessor
.
- First, collect the corresponding Task and Trans based on the annotation class.
- Divide different APIs based on different Trans and collect the Task dependencies used by different APIs.
- Use the Task used by Trans to filter dependencies.
- Group the Tasks based on their mutual dependencies.
This completes the design of the framework's layering and automatic construction. The main consideration in designing the framework is how to abstract the modules used in actual business into design, and at the same time, consider the scalability and strong constraints of the framework.
Conclusion#
This article mainly explains how I abstract the relationship between business and calls into Trans and Task. Next, I will discuss a series of articles on the core design of the concurrent thread pool, configuration thinking, monitoring design, and automatic construction algorithm of the concurrent framework.
If you are interested, I recommend following the official account or subscribing to this site. Welcome to interact and communicate. Let's become stronger together~