图片

Background introduction

Is there a lot of if-else written in the business? Are you fed up with these if-else changing frequently?

Have a lot of abstractions been done in the business, and new business scenarios are still not used?

Have you researched various rule engines and found that it is either too heavy or too troublesome to access or maintain, and finally found that it is better to hard code?

Next, I will introduce a brand new open source rule engine-ice, with a simple example, from the lowest level of arrangement ideas, to illustrate the difference between ice and other rule engines; to describe how ice uses a new design idea to fit the solution. The properties of coupling and reuse give you the greatest freedom of arrangement.

Application Scenarios of the Rule Engine

Rule engines are used in many business scenarios, such as:

Affiliate Marketing : Composed of a variety of conditions, processes, and rewards, the timeline is complex, the code reuse rate is not high, and adjustments are frequent.

Risk control rules : Combining a variety of conditions and returning to the decision, the conditions are large and complex, and change frequently.

Data analysis : Produce the desired data through the rules arranged by the analysts themselves, with thousands of people and thousands of faces.

The above scenarios often have some common pain points:

Flexible business (frequent changes, obvious timeliness, complex test logic)

Pursuit of flexibility and fancy: Products and operations have been exploring new ways of playing, resulting in many abstracted modules often unable to withstand two iterations.

Going online today will be adjusted again: due to some occasional situations, such as low online user participation, timely adjustment of user participation thresholds, etc. (Of course, all situations can be considered in place before development, but a lot of work is done for small probability events, and the cost too high).

R&D and testing are exhausted: R&D hard-coding, testing and verifying complex and repetitive logic, become more and more tired over time.

Timeline (multiple timelines are intertwined and confused)

The R&D arrangement is wrong: the general marketing type involves a lot of timelines, but at present, when testing an activity with different time node attributes that will be launched in the future, it is often hard-coded by R&D scheduling time and testing. When it happens and disrupts the timeline, you need to rearrange the time (if you haven't experienced it, you don't need to understand it too much, it will be discussed later).

Test Parallel Kong Rong Pear: When the timeline conflicts and there are multiple tests running concurrently at the conflicting positions, the tests often coordinate the test sequence by themselves. When one side has problems, the progress of subsequent tests is often uncontrollable.

other problems

Dependency is unsustainable: The test environment is an unstable environment. Once a problem occurs in the dependency, it will inevitably affect the progress. How can we achieve a simple and efficient mock?

Fixing data is a pain in the ass: When online issues arise, how can affected customers be compensated quickly and efficiently?

Design ideas of open source rule engine ice

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 10 points will not be superimposed)

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

图片

As shown in the figure above, 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 cost, 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 Drool s 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.

Plans never keep up with 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, that's it, go!

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?

Let's see how ice does it?

Introduce relationship nodes

The relationship node is to control the flow of business.

AND

Among all the 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, and if all are false, it will be 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 one node is false, then false, if there is neither true nor false, it returns none, and all child nodes are terminated after execution.

NONE

All child nodes are executed, and whatever the child node returns, returns 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).

Introduce leaf nodes

Leaf nodes are actually processed nodes.

Flow

Some condition and rule nodes, such as ScoreFlow in the example.

Result

Nodes with some result properties, such as AmountResult and PointResult in the example.

None

Some actions that do not interfere with the process, such as assembly work, are described below as TimeChangeNone.

With the above nodes, how do we assemble it?

图片

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

 [ScoreFlow-100:false]→[AND:false]→[ScoreFlow-50:true]→[PointResult:true]→[AND:true]→[ANY:true]

At this time, it can be seen that the time that needs to be stripped before can be integrated into each node, and the time configuration is returned to the node. If the execution time is not reached, for example, 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.

Flexible and quick response to changes

For ①, you can 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 AmountResul 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 activity started in 10.1 must be developed and launched before 10.1. For example, how can I test an activity started in 10.1 in 9.15? In ice, just a little modification:

图片

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.

properties of ice

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 superimposing to superimposing, it is only limited to the logic of that ANY node, just change it to the logic I want. 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 here for participating in the 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 instance in the render activity, change one, and the other will be updated, avoiding the problem of changing the time everywhere.

In the same way, if there is a problem online, such as the sendAmount interface is hung up, 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 mocking, just add the data that needs to be mocked in the Pack, and you can run it.

Introduce the front 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 forward node is not false. , the semantics are consistent with the two nodes connected by AND.

Talk is cheap. Show me the code…

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

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

Welcome to use and experience the open source rule/process engine ice. If you encounter problems, please submit an issue to communicate. You can also add the author's WeChat: lwaitmoonl, note "ice", and enter the exchange group.

Introduction to the Dev for Dev column

Dev for Dev (Developer for Developer) is a developer interactive innovation practice activity jointly initiated by Agora and the RTC developer community. Through various forms of technology sharing, communication and collision, and project co-construction from the perspective of engineers, the power of developers is gathered, the most valuable technical content and projects are mined and delivered, and the creativity of technology is fully released.


RTE开发者社区
658 声望971 粉丝

RTE 开发者社区是聚焦实时互动领域的中立开发者社区。不止于纯粹的技术交流,我们相信开发者具备更加丰盈的个体价值。行业发展变革、开发者职涯发展、技术创业创新资源,我们将陪跑开发者,共享、共建、共成长。