Abstract: The vast majority of digital farmers day and night by the need to suppress the liver out of the code, no matter how much chi chi belly, there can be no reconstruction, only rewrite.

This article is shared from huawei cloud community “also reconstruction? You can only shovel that code rewrite!” , author: Little Fuge.

One, foreword

We’re different. You’re the only one without a date! Yes, you are process oriented!

As I said, the vast majority of digital farmers are required to suppress the liver out of the code day and night, no matter how puffed up, there can be no refactoring, only rewrite. Why is that? Because the time cost of rewriting is far less than refactoring a crumpled piece of code. But there is no guarantee that the code you rewrite will be much better than it was before, with the risk of code accidents and little business value.

Although the code is for the machine to run, it is also for people to look at, and with each iteration, change and upgrade of the requirements, the r&d personnel need to develop and go online for many times for the same code, so it will involve the characteristics of maintainable, easy to expand and easy to hand over.

Those that don’t layer the code logic properly, don’t comment the code, don’t commit to the specification, don’t format it, don’t name it arbitrarily, and even write queryBatch as queryBitch can cause problems for subsequent code refactoring. So let’s take a look at what it takes to develop reconfigurable code.

Second, code optimization

1. Convention specifications

Feat. Feat. Type of docs feat. Feat. Type of docs. Git refactor is used to refactor code. To add a test or modify an existing test perf: Ci: A chore related to CI (Continuous Integration Services) : does not modify the rest of the SRC or test modifications, such as changes to the build process or accessibility tools. ${USER} * @date: ${DATE} */Copy the code
  • Branch: Specify a branch pull specification before development, such as date _ User _ Purpose, 210905_xfg_updateRuleLogic

  • Commit: author, type: desc For example, Frigero, fix: update rule Logic problems refer to the Commit Message specification

  • Note: Including class annotations, method annotations, property annotations, Editor -> File andCode Templates -> File Header AlibabaJava Coding Guidelines Unified standardized coding method.

2. Interface standards

When writing an RPC interface, the return result must contain explicit Code and Info description. Otherwise, it is difficult for the user to know whether the interface was successfully called or an exception, and in what case.

Define the Result

public class Result implements java.io.Serializable { private static final long serialVersionUID = 752386055478765987L; /** return result code */ private String code; /** Return result information */ private String info; public Result() { } public Result(String code, String info) { this.code = code; this.info = info; } public static Result buildSuccessResult() { Result result = new Result(); result.setCode(Constants.ResponseCode.SUCCESS.getCode()); result.setInfo(Constants.ResponseCode.SUCCESS.getInfo()); return result; } / /... get/set }Copy the code

Return result wrapper: inheritance

public class RuleResult extends Result { private String ruleId; private String ruleDesc; public RuleResult(String code, String info) { super(code, info); } / /... Public RuleResult execRule(DecisionMatter Request) {return new RuleResult(Constants.ResponseCode.SUCCESS.getCode(), Constants.ResponseCode.SUCCESS.getInfo()); }Copy the code

Return result wrapper: generic

public class ResultData<T> implements Serializable { private Result result; private T data; public ResultData(Result result, T data) { this.result = result; this.data = data; } / /... ResultData<Rule> execRule(DecisionMatter Request) {return new ResultData<Rule>(Result.buildSuccessResult(), new Rule()); }Copy the code

Either wrapper definition of the return result of an interface can standardize the return result. After packaging in this way, the user can use a uniform way to determine the Code and make the corresponding treatment.

3. Library table design

Three normal form: is the content of the standardization of the database, the so-called database three normal form is popularly said to be the design of the database table should comply with a set of specifications, if not comply with the design of the database will cause the database field redundancy, data query, insert and other problems.

Database not only has three normal forms (1NF/2NF/3NF), but also BCNF, 4NF, 5NF… In actual database design, however, it is sufficient to follow the first three paradigms. Further down you can create too many unnecessary constraints on your designed database.

0NF

  • Zeroth normal is when no normal form is used, the data stores a redundant number of table fields, and such a table structure is very difficult to maintain.

1NF

  • The first normal form is an improvement on the redundant fields in the zeroth normal form. The repeated fields are removed and a table structure with less redundant data is designed to be easy to store and read.

  • It is also stated in the first paradigm that all fields in a table should be atomic and indivisible. For example, you cannot store the department name and responsibility of a company’s employees table in one field. You need to make sure that each column remains atomic

2NF

  • If 1NF is met, all columns in the table must depend on the primary key. Ensure that each column is related to the primary key column and not to the primary key column. That is, a table can only describe one thing. You need to make sure that every column in the table is associated with a primary key.

3NF

  • There should be no dependencies, student id, name, to department, department to dormitory, you need to make sure that each column is directly related to the primary key column, not indirectly.

The three paradigms

The three paradigms are regular constraints for designing database table structures, but allow for local flexibility in actual development:

1. Sometimes, in order to facilitate the query, the snapshot information of the user will be redundant on the order table, such as some Settings information of the user when placing an order.

2. Single-column list data is summarized to a quantity value in the total table, which is convenient for query and avoids list summary operations.

3. Some fields can be redundant in the design of the table to avoid the cumbersome problem of the table caused by changing service development and careless consideration.

4. Algorithm logic

Usually in our actual business function logic development, in order to meet some high concurrency scenarios, it is impossible to lock the database table to reduce inventory, nor can we directly for a large number of circular training operations, usually need to consider how to decentralize 🤔 in such scenarios and reduce the time complexity.

SEC kill: decentralization

  • Background: This is a product activity seconds kill implementation scheme, the initial design is based on an activity ID to lock, seconds kill lock this ID, users purchase after release. However, when a large number of users buy the distributed exclusive lock in seconds, an exception occurs in the service logic processing, and the lock fails to be released. As a result, all users can no longer get the lock, which causes the problem of having goods but not being able to place orders.

  • Optimization: Optimize the exclusive race to piecewise static, using the active ID+ inventory number as the dynamic lock identifier. If the current user fails to lock, subsequent users can continue to lock without being affected. The failed lock will be compensated and restored by the worker, so overselling and unselling will be avoided eventually.

Algorithms: what not to do

@Test public void test_idx_hashMap() { Map<String, String> map = new HashMap<>(64); Map. put("alderney", "unimplemented service "); Map. put("luminance", "service not implemented "); Map. put("chorology", "unimplemented service "); Map. put("carline", "unimplemented service "); Map. put("fluorosis", "unimplemented service "); Map. put(" Angora ", "service not implemented "); Map. put("insititious", "unimplemented service "); Map. Put ("insincere", "implemented service "); long startTime = System.currentTimeMillis(); for (int i = 0; i < 100000000; i++) { map.get("insincere"); } system.out.println (" Time (initialCapacity) : "+ (system.currentTimemillis () -startTime)); }Copy the code
  • Background: The time complexity of HashMap data acquisition is O(1) -> O(logn) -> O(n), but after special operation, this time complexity can be pulled to O(n).

  • Action: This is a function that defines a HashMap that holds a business implementation key and invokes a service through the key. But the key here, only insincere is useful, the rest are unimplemented services. Do you see what the problem is?

This code at first glance no problem, see is the code under arsenic! The purpose of the HashMap is to make a list of all the keys in the HashMap, and to put useful keys at the end of the list to increase the get time!

First, new HashMap<>(64); Why are 64 lengths initialized by default? Because the default length is 8, when inserting elements, when the length of the linked list is 8, the expansion and tree judgment of the linked list will be carried out, and the original keys will be hashed, so all keys cannot form a linked list with high time complexity.

Idx = (sie-1) & (key.hashCode() ^(key.hashcode () >>> 16)); This will allow all keys to be hashed to the same location for collision. And the word insincere means; Insincere, insincere!

In the end, the first seven keys are invalid and do not work, except for the last key. This would allow many of these time-consuming collision lists to be built into a HashMap, with a load factor of 0.75, without letting the HashMap expand.

In fact, many algorithms, including hashing, inversion and load, can be used in many practical business scenarios, including crowd filtering, lottery logic, data routing and so on. The use of these functions can reduce time complexity, improve system performance, and reduce interface response time.

5. Separate responsibilities

In order to make the logical implementation of the program more extensible, we usually need to use design patterns to deal with the code implementation structure of each scenario. The application of design pattern in code development is mainly embodied in the definition of interface, the packaging of abstract classes and the implementation of inherited classes. By isolating the development of each functional area in this way, the requirements can be added more flexibly each time they are extended, without the code becoming more cluttered by requirements iterations.

case

public interface IRuleExec { void doRuleExec(String req); } public class RuleConfig { protected Map<String, String> configGroup = new ConcurrentHashMap<>(); static { // ... } } public class RuleDataSupport extends RuleConfig{ protected String queryRuleConfig(String ruleId){ return "xxx"; } } public abstract class AbstractRuleBase extends RuleDataSupport implements IRuleExec{ @Override public void doRuleExec(String req) { // 1. Query configuration String ruleConfig = super.queryRuleConfig("10001"); // 2. CheckRuleConfig (ruleConfig); } this.dologic (configgroup.get (ruleConfig)); } */ protected void doLogic(String req); private void checkRuleConfig(String ruleConfig) { // ... }} public class RuleExec extends AbstractRuleBase {@override protected void doLogic(String req) {Copy the code

The class diagram

  • Abstract class AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase: AbstractRuleBase Finally, there is the definition and invocation of abstract methods, and the business class RuleExec can implement its own logical functions on demand.

6. Be logical

Has your code ever had an online accident? Why the accident? Ten birds in a tree and one shot left how many? For example, is the gun silent, is the bird deaf, is there pregnant, is there a bird tied to the tree, is there a bird next to the tree, is the bird afraid of gunshots, is there a disabled bird, is the bird hunter blind?

In fact, your online accidents are basically around: database connections and slow queries, server loads and outages, logic exceptions, interface idempotency, data resistance, MQ consumption speed, RPC responsiveness, tool class usage errors, and so on.

Here is an example: users pay more points, resulting in bulk customer complaints.

  • Background: A large part of the research and development of this product may have participated in the development of the background, which is simply to meet the needs of users to use the point lottery. The process designed at the beginning of r&d is shown on the left of the figure. Users’ points are deducted through RPC interface, and lottery is carried out after successful deduction. However, the RPC service was unstable on that day. As a result, the RPC call succeeded, but a timeout failure was returned. However, the UUID of calling RPC interface is automatically generated each time and does not have idempotency of calling. Therefore, users pay more points.

  • Treatment: after the accident, the lottery process is modified and the lottery ticket is to be drawn. RPC interface is called by the lottery ticket ID to ensure the idempotency of the interface. When the RPC interface fails, the lottery is performed by the way of scheduled task compensation. After the process rectification, it is found that the compensation task occurs 1-3 times per week, which proves that there is indeed a problem of availability of RPC interface. At the same time, it also indicates that there has been a process problem for a long time, but there is no feedback because there are few complaints from users and customers.

7. Domain aggregation

Not abstract enough, not writable, not extensible enough, is it always your code, like a one-shot deal every time, completely written and bound, with no gaps for new requirements to expand into?

Why? Because a lot of the code written by r&d does not have the characteristics of domain aggregation, of course, this does not have to be in the DDD structure, even in the MVC layer, can also write a lot of good aggregation logic, function implementation and business invocation separated.

  • Based on the design idea of domain-driven design, the domain model is established by event storm, the logical and physical boundary of domain is divided reasonably, the domain object and service matrix and service architecture diagram are established, and the code structure model conforming to DDD hierarchical architecture idea is defined to ensure the consistency between business model and code model. Through the above design ideas, methods and processes, guide the team to complete the design and development of microservices according to DDD design ideas.

O Refuse mud ball small monomer, refuse pollution function and service, refuse one plus function schedule for one month

O Architecting highly available application services that are easily compliant with the high-speed iteration of the Internet

O Materialized, assembled and choreographed services to improve human efficiency

8. Service layering

If you want to make your systems engineering code can support of the business needs of the majority, and can settle can take, so you need to do basic development of the implementation of the code, pulled out the technology components, functional areas and business logic several layers, not to write the business logic of the frequent change in various functional areas, Functional areas should be made more independent and can be connected, choreographed and combined by business layers to achieve different business requirements. This way your functional areas can be settled down and it’s easier to expand with each requirement.

  • This is a simplified, hierarchical logical structure with aggregated domains, SDK components, middleware, and code choreography, and provides some common, condensed service governance capabilities. Through this layering and implementation of each level, the requirements can be more flexibly accepted.

9. Concurrency optimization

In the distributed scene development system, to use the distributed ability as far as possible, from the program design as far as possible to avoid some centralized, distributed things, database lock, because the use of these ways may be in some extreme cases, resulting in the overload of the system, resulting in accidents.

  • Therefore, it is often more necessary to do de-centralization, using MQ to eliminate peaks and reduce coupling so that the data can be ultimately consistent, but also more important to consider the use of Redis to reduce the amount of locking on the database.

  • The proper use of MQ, RPC, distributed tasks, Redis, database and table, and distributed transactions is the only way you can make your code support a larger business volume.

10. Source code ability

Have you read about HashMap’s zippered addressing data structure, hash hash and perturbation functions, how to dynamically switch data sources with Spring, how AOP is implemented and used, how MyBatis and Spring manage Bean objects, etc. Seems to be some interview essay, but in the actual development is actually can solve a lot of problems.

@Around("aopPoint() && @annotation(dbRouter)") public Object doRouter(ProceedingJoinPoint jp, DBRouter dbRouter) throws Throwable { String dbKey = dbRouter.key(); If (stringutils.isblank (dbKey)) throw new RuntimeException(" Annotation DBRouter key is null! ); String dbKeyAttr = getAttrValue(dbKey, jp.getargs ()); int size = dbRouterConfig.getDbCount() * dbRouterConfig.getTbCount(); // Perturbation function int idx = (sie-1) & (dbKeyattr.hashCode () ^ (dbKeyattr.hashCode () >>> 16)); / / library table index int dbIdx independence idx = / dbRouterConfig getTbCount () + 1; int tbIdx = idx - dbRouterConfig.getTbCount() * (dbIdx - 1); // Set ThreadLocal dbContextholder.setdbKey (string. format("%02d", dbIdx)); DBContextHolder.setTBKey(String.format("%02d", tbIdx)); Logger. info(" Database routing method: {} dbIdx: {} tbIdx: {}", getMethod(jp).getName(), dbIdx, tbIdx); Try {return jp.proceed(); } finally { DBContextHolder.clearDBKey(); DBContextHolder.clearTBKey(); }}Copy the code
  • This is a HashMap hash bucket array + linked list + red-black tree data structure via perturbation function (sie-1) & (key.hashcode () ^ (key.hashcode ()>>> 16)); Resolve serious data collision problems.

  • But in fact, such hash algorithm, addressing mode can be used in the design and implementation of database routing, as well as the whole array + linked list in fact, the way of library + table is similar.

  • First, we extract the number of library table products and use it as a HashMap.

  • When IDX calculates an index position on the total length, it also needs to convert this position into the library table to see the index of the total length because it falls into which library which table.

  • Finally, the index information of this calculation is stored in a ThreadLocal, which is used to pass the index information that can be extracted during the method call.

Third, summary

  • To be fair, it’s almost impossible to take a bunch of broken code and refactor it to clean it up. In detail, you need to change the code structure layer, attribute object integration, call logic encapsulation, but any step of the operation may have a risk to the original interface definition and call, and the external existing call your interface needs to upgrade as you change, you may want to wrap the layer, But this layer of packaging still requires a large time cost and almost no value of adaptation.

  • So in real development, if we can make this code refactoring possible, almost in real time, every time you add new functionality, new logic, fixing exceptions, you have to think about whether you can use code structure, implementation, design patterns, etc

  • When you are in the demand, think carefully to undertake such business demands, need to build what kind of data structure, algorithm logic, design pattern, domain aggregation, service arrangement, system architecture, etc., in order to build a more reasonable with easy maintenance, scalable system services. If you don’t feel that way, read Design Patterns and Handwriting Spring, which can help you improve your programming logic.

Click to follow, the first time to learn about Huawei cloud fresh technology ~