22
头图

foreword

Background introduction

The rule/process engine must be familiar to everyone. Drools, Esper, Activiti, Flowable, etc. are familiar to everyone. Many big manufacturers are also keen to study their own rule engines, which are used to solve complex rules and process problems in flexible scenarios. If you want to change the configuration, you can generate/validate new rules, and get rid of the hard-coded bitter sea. After all, changing the configuration and arranging rules/processes on an existing basis is much cheaper than hard coding, but using the existing rule engine on the market to arrange, firstly, the access cost and learning cost are not low, and secondly With the passage of time, the rules have become more and more complex and some scenarios are not applicable, which makes people complain even more.

"Design Ideas"

In order to facilitate understanding, the design idea will be accompanied by a simple recharge example.

"Example"

Company X will carry out a seven-day recharge activity during the National Day holiday. The contents of the activity are as follows:

Event time: (10.1-10.7)

Activities:

Recharge 100 yuan and get 5 yuan balance (10.1-10.7)

Recharge 50 yuan and get 10 points (10.5-10.7)

Remarks for the event: no superimposed gift (recharge 100 yuan can only get 5 yuan balance, and will not be superimposed to give 10 points)

Simply dismantling, to complete this activity, we need to develop the following modules:

In the figure, it is found that the key to be issued is to be issued. Where does this key come from:

As shown in the figure, when the user recharges successfully, a parameter package pack (like Activiti/Drools' Fact) corresponding to the recharge scenario will be generated. The package will contain the recharge user's uid, recharge amount spend, recharge time requestTime and other information. We can get the value in the package through the defined key (similar to map.get(key)).

There is nothing wrong with how the module is designed. The key point is how to arrange the following to achieve freedom of configuration. Next, through the existing nodes above, we will explain the advantages and disadvantages of different rule engines in the core arrangement, and compare how ice does it.

"Flowchart Implementation"

Class Activiti, Flowable implementation

Flowchart implementation should be the most common arrangement method we think of~ It looks very concise and easy to understand. Through special design, such as removing some unnecessary lines, the UI can be made more concise. But because of the time attribute, time is actually a rule condition, and after adding it becomes:

It looks fine too.

"Execution Tree Implementation"

Class Drools implementation (When X Then Y)

This looks fine too, try it with the timeline:

It's still relatively concise, at least compared to the flowchart format, and I would be more willing to modify this.

"Change"

The advantage of the above two solutions is that some scattered configurations can be well managed in combination with the business, and minor modifications to the configuration can be done at your fingertips, but the real business scenarios may still have to hammer you. With flexibility changes, everything is different.

"ideal"

It won't change, don't worry, just like that, go online.

"Reality"

①Recharge 100 yuan and change it to 80, 10 points to 20 points, and time to change to 10.8 and end it (smile, after all, I spent so much time working on the rules engine, and finally realized the value!)

②Users are not very motivated to participate, so let’s get rid of the non-overlay and send them all.

③You can't send too much with a balance of 5 yuan. Let's set up an inventory of 100. By the way, if the inventory is not enough to charge 100 yuan, you still have to send 10 points.

The above changes do not seem unrealistic. After all, the real online changes are much more outrageous than this. The main disadvantage of the flow chart and execution tree implementations is that they can affect the whole body. It is easy to make mistakes if it is not considered in place, and this is just a simple example. The actual content of activities is much more complicated than this, and there are also many timelines. Considering this, plus the cost of using the learning framework, Often the gain outweighs the gain, and in the end it turns out that it's better to hardcode it.

what to do?

"How is ice made?"

" "Introduction of relationship nodes" "

In order to control the flow of business, the relationship node

【AND】

Among all child nodes, one returns false, and the node will also be false, all true is true, and the execution is terminated at the place where false is executed, similar to Java's &&

【ANY】

Among all child nodes, if one returns true, the node will also be true, all false are false, and the execution will be terminated when the execution reaches true, similar to Java's ||

【ALL】 

All child nodes will be executed. If any one returns true, the node is also true. If there is no true, if a node is false, it will return false. If there is no true and no false, it will return none. All child nodes are terminated after execution.

【NONE】

All child nodes will execute, no matter what the child node returns, it will return none

【TRUE】

All child nodes will be executed, no matter what the child node returns, it will return true, and no child node will return true (other nodes without children return none)

"Introducing Leaf Nodes"

The leaf node is the real processing node [Flow] Some condition and rule nodes, such as ScoreFlow in the example [Result] Some result nodes, such as AmountResult, PointResult in the example [None] Some actions that do not interfere with the process, such as assembly work Wait, as TimeChangeNone, which will be introduced below, has the above nodes, how do we assemble it?

As shown in the figure, using the tree structure (the traditional tree is mirrored and rotated), the execution order is similar to in-order traversal. It is executed from the root, which is a relationship node, and the child nodes are executed from top to bottom. If the user's recharge amount is 70 Element, the execution process:

At this time, it can be seen that the time that needs to be stripped out before can be integrated into each node, and the time configuration is returned to the node. If the execution time is not reached, such as the node that issued the points will take effect after 10.5 days, then before 10.5 , it can be understood that this node does not exist.

"Changes and Problem Resolution"

For ① directly modify the node configuration

For ②, you can directly change the ANY of the root node to ALL (the logic of superimposed and non-superimposed transmission is on this node, and the logic belonging to this node should be solved by this node)

For ③ due to insufficient inventory, it is equivalent to not issuing to the user, then AmountResult returns false, and the process will continue to be executed downwards without any changes.

One more thorny question, when the timeline is complex, what to do with test work and test concurrency? An event that started in 10.1 must be developed and launched before 10.1. For example, how can I test an event that started in 10.1 in 9.15? In ice, it just needs to be modified slightly:

As shown in the figure, a node TimeChangeNone (changing the requestTime in the package) is introduced, which is responsible for changing the time. The execution of the subsequent nodes depends on the time in the package. TimeChangeNone is similar to a plug-in for changing time. If the test is parallel, then You can add a time change plug-in to the business that each person is responsible for for multiple tests.

"Characteristics"

Why dismantle it like this? Why does this solve these changes and problems?

In fact, the tree structure is used for decoupling. When the flow chart and execution tree are implemented, when changing the logic, it is inevitable to look forward and backward, but ice does not need it. The business logic of ice is all on this node, and each node can represent a single Logic, for example, if I change the logic of stacking to stacking, it is only limited to the logic of that ANY node. Just change it to the logic I want. As for the child nodes, don't pay special attention. It depends on the flow of packages, and the subsequent process executed by each node does not need to be specified by itself.

Because the execution process after executing it is no longer under its control, it can be reused:

As shown in the figure, TimeChangeNone is used in the participation activity. If there is still an H5 page that needs to be presented, and different presentations are also related to time, what should I do? Just use the same TimeChangeNone instance in the rendering activity, change one, and the other will be updated, avoiding the problem of changing the time everywhere.

Similarly, if there is a problem on the line, such as the sendAmount interface hangs, because the error will not return false to continue execution, but provide optional strategies, such as the Pack and the node to which it is executed, and wait until the interface is repaired. , and then continue to throw it into ice and run it again (since the placement time is the time when the problem occurs, there is no need to worry about the problem that the repair after the event ends will not take effect). Similarly, if it is a non-critical business such as the avatar service, it still hangs. I hope to run, but there is no avatar, so you can choose to skip the error and continue to execute. The rules for placing orders here are not described in detail. The same principle can also be used for mocks. Just add the data that needs to be mocked in the Pack, and you can run it.

"Introducing Pre-Node"

In the above logic, we can see that some AND nodes are closely bound. In order to simplify the view and configuration, the concept of forward node is added. This node will be executed if and only when the execution result of the current node is not false. , the semantics are consistent with the two nodes connected by AND.

professional documentation

code

Talk is cheap. Show me the code… 

github: https://github.com/zjn-zjn/ice 

gitee: https://gitee.com/waitmoon/ice 

doc: http://waitmoon.com/docs/#/


程序员海军
981 声望7k 粉丝