Hello, I am crooked.
I've been thinking about a question recently:
For programmers, what counts as writing "technical" code?
Why do you think about thinking about this seemingly serious (pretend) and harmful (forced) question?
Because this is a question on Zhihu:
https://www.zhihu.com/question/37093538
When I saw this question for the first time, I quickly crossed it and didn't pay attention to it at all. But just looking at it like that, this question occasionally popped into my mind inadvertently.
Then after a period of time, this problem came up again when I brushed Zhihu at noon.
Coincidentally, at noon that day, I saw such an interview question:
The first time I saw this interview question, I was reminded of a warm-up function in the Dubbo service.
Combining the question of Zhihu, I thought at the time: The warm-up source code of the Dubbo service is, in my opinion, a "technical" code.
This piece of functional coding is really not complicated at all, mainly because it can reflect the "internal skills" of the coders for the JVM and RPC. They can realize that due to the compilation characteristics of the JVM, and Dubbo acts as an RPC framework in the architecture. role, so in order to maximize the stability of the service, you can do a certain amount of service warm-up at the coding level.
But after writing the relevant answer, from the comment area, it is basically all complaints, saying that the example I gave is contrary to the question.
For example, I intercepted the two comments with the most likes:
After reading these complaints, I think these complaints are justified. My example is really bad and very one-sided.
In order to better elicit this topic, I will first move and expand my answer at the time.
By the way, it can be regarded as an answer to the interview question I just said.
Service warm-up
The following method has only two lines, but this is the core code of the Dubbo service warm-up function:
org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance#calculateWarmupWeight
Take a look at where this method is called in the framework:
When we don't specify parameters, the input parameters warmup and weight have default values:
That is, in the case of using default parameters, the above method can be simplified to this:
static int calculateWarmupWeight(int uptime) {
//int ww = (int) ( uptime / ((float) 10 * 60 * 1000 / 100));
int ww = (int) ( uptime / 6000 );
return ww < 1 ? 1 : (Math.min(ww, 100));
}
Its input parameter uptime represents the service startup time in milliseconds. The return parameter represents the weight of the current service.
Based on this method, I will give you a picture first.
In the figure below, the x-axis is the startup time in seconds, and the y-axis is the corresponding weight:
As can be seen from the figure, from the start of the service, the weight will increase by one every 6 seconds, until 600 seconds, that is, 10 minutes later, the weight becomes 100.
For example, when the uptime is 60 seconds, the return value of this method is 10.
The return value of this method is 11 when the uptime is 66 seconds.
When the uptime is 120 seconds, the return value of this method is 20.
And so on...
After 600 seconds, that is, ten minutes and more than ten minutes, the weight is 100, which means that the warm-up is complete.
So what is this weight used for?
This has to be combined with load balancing.
Dubbo provides the following five load balancing strategies:
- Random LoadBalance : "Weighted Random" Strategy
- RoundRobin LoadBalance: "Weighted Round Robin" Strategy
- LeastActive LoadBalance: "Least Active Load Balance" strategy
- ShortestResponse LoadBalance: "Shortest Response Time" strategy
- ConsistentHash LoadBalance: "Consistent Hash" Strategy
In addition to the consistent hashing strategy, the other four strategies have to use the weight parameter:
Weight is a key factor used to determine which service the request is sent to.
I'll draw a diagram for you:
There are three services A, B, and C. The weights of A and B are both 100, and the C service has just been started.
As a service that has just been started, it is not suitable for accepting burst traffic, thinking that the code running on the server has not been fully compiled, and the code on the main link may not have entered the C2 stage of the compiler.
So it stands to reason that the C service needs a service warm-up process, that is, in the first 10 minutes of startup, there should be a process of gradually accepting more and more requests.
For example, in the simplest weighted random polling load balancing strategy, the key code is as follows:
org.apache.dubbo.rpc.cluster.loadbalance.RandomLoadBalance#doSelect
It doesn't matter if you don't understand, I'll draw a picture for you.
At the first minute when the C service starts, its weight is 10:
So totalWeight=210 in the code, so the following line of code is to randomly generate a number within 210:
int offset = ThreadLocalRandom.current().nextInt(totalWeight);
There are three servers in the diagram, so lenght=3 in the for loop.
What is the weights[] array?
Take a look at the code:
In each cycle, the weights of each server are aggregated and placed in weights[].
In the example above it is like this:
- weights[0]= 100 (the weight of the A server)
- weights[1]= 100 (the weight of the A server) + 100 (the weight of the B server) = 200
- weights[2]= 100 (the weight of the A server) + 100 (the weight of the B server) + 10 (the weight of the C server) = 210
When the random number offset is between 0-100, the A server processes the request. Between 100-200, the B server processes this request. Between 200-210, the C server processes this request:
That is to say: the C server has a certain probability to be selected to process this request, but the probability is not high.
How can the probability be high?
Power is important.
How is the weight so great?
As the startup time is longer, the weight also increases.
For example, 8 minutes after the service starts, it becomes like this, and the probability of the C server being selected is much higher:
Finally, after 10 minutes, the weights of the three servers are the same, and the traffic they undertake is almost the same.
The requests undertaken by the C server increase with the service startup time, until it reaches a peak after 10 minutes, which is a warm-up process.
The above is a preheating method, and similar to this preheating idea, you can also find similar source codes in other open source projects of the gateway class.
But preheating isn't just such an implementation.
For example, Alibaba has built an Alibaba Dragonwell based on OpenJDK, which is actually a JDK.
https://github.com/alibaba/dragonwell8
One of these features is preheating:
In addition to the warm-up point, I also mentioned the implementation of the least active load balancing strategy LeastActiveLoadBalance.java in Zhihu's answer:
Since the initial submission, it has not been modified several times in total.
You can also compare the initial version and the latest version, the core algorithm and core logic have basically not changed:
In addition to this strategy, several other strategies are almost similarly "stable".
from the comments
When I answered this question on Zhihu, I didn't write as much as the section above, but the core content is probably the above.
When I mentioned the warm-up in the answer, I wanted to express the seemingly inconspicuous two lines of code, but there are still many deep-seated reasons behind it. I think this has "technical content".
When it comes to the implementation of the load balancing strategy, it has not changed much for many years. I want to express that these important, low-level and basic codes have not been moved for many years after they are written, indicating that the code written at the beginning is very stable. of. To be able to write such stable code, I think this is also "technical".
Then take you to the comment section:
The comments are almost unanimously disapproving of this answer. But as I said earlier, when answering this question, I do feel that my answer is relatively close to the topic.
But after reading the comments I figured out why this is a bad answer, as the comments section says:
The example is not enough, but because the problem to be solved has not changed, so the solution is relatively stable.
First of all, this kind of code is too far from the code written by most programmers in their actual work. The source code of the framework is worth learning, but it is of little use for reference in actual development.
And the comment area also mentioned that most programmers have no chance to write such code that tests "technical ability".
This is indeed a fact. The development of a small number of middleware and the development of most business logic are two groups of programmers with completely different thinking modes.
Then I took a look at the high praise answers under this topic:
In fact, Gao Zan replied with this sentence:
When an excellent programmer receives a task to write "Destroy the Earth", he will not simply write a destroyEarth() method; he will write a destroyPlanet() method with earth as a parameter pass in.
This is an example that is closer to our actual work.
For this example, let me change to a more general requirement, such as the requirement to allow you to access a WeChat payment:
You might define a class like this:
public class WechatPayService {
public void wechatPayment(){
//微信支付相关逻辑
}
}
When you want to use it, just inject WechatPayService where you need to use it, there is nothing wrong with it.
But a demand that comes along with it is to allow you to access Alipay to pay.
Of course, you naturally made a similar class:
public class AliPayService {
public void aliPayment(){
//支付宝支付相关逻辑
}
}
But you wrote and found: Hey, what's the matter, I feel that the logic of Alipay and WeChat have many similarities, and the key steps of development feel exactly the same?
So you define an interface and use the strategy pattern to specifically handle "payment" related requirements:
public interface IPayService {
/**
* 支付抽象接口
*/
public void pay();
}
In my opinion, this is a very conventional development solution. When I even got the requirement of "WeChat Payment", I knew very well that I should use the strategy mode to meet this requirement, in order to facilitate future development.
However, I also have a familiar process with this "savvy road", and I didn't know how to write it as soon as I started, as soon as I entered the industry and worked.
After I was working, I read a lot of code in actual projects, and saw that the project was in use. I thought it was very practical and the project structure was very clear. Only in other similar needs, I deliberately imitated, learned, understood, and applied. , polishing, and slowly integrated into my coding habits. Because I am too familiar with it, I gradually think that this is something that has no technical content.
Until later, I once took an intern to do a project. The project has a function of leaderboard. The leaderboard needs to support various dimensions. When the front-end request is made, it will tell me which leaderboard currently needs to be displayed.
In the stages of requirement analysis, system design and code implementation, I naturally thought of the strategy pattern mentioned earlier.
Later, the intern students saw this logic and said to me: The way to realize this requirement is really good. If I were to write it, I would never think of such a landing plan.
But I think this is just a general solution.
I give this example to express the meaning that for the "technical content", everyone's understanding of each stage is completely different.
As far as I am concerned, standing at the time point when I am writing this article, I think that the code with technical content is that others are willing to use it after seeing it, willing to imitate it, and willing to tell the people who come after it: this thing is really good, You can also use it.
It can be as small as a method class with only a few lines in a project, or as large as a complete technical solution to a problem in the industry.
In addition to this example, I would like to cite another example that I encountered shortly after I started working.
The requirements are also very simple, that is, the addition, deletion, modification and query operation of a table, that is, the crud without technical content that we often complain about.
However, I was shocked when I saw the code submitted by others.
For example, for a new operation, all the logic is in a controller. There is no so-called service layer and dao layer. A shuttle directly injects the mapper into the controller. In one method, everything from data verification to database interaction is included.
Does the function work?
can be used.
But is this code "technical"?
I think it can be said that it has no technical content. With the current buzzwords, I even think that this is the programmer's "rotten".
I want to continue developing new features based on this piece of code, what can I do?
There's nothing I can do, the original code really doesn't want to be moved.
I can only guarantee that on this pile of "shit mountain", my newly written code is clean and clear, and I will not continue to throw garbage in it.
Then I read a book called "Clean Code" and there was a rule in it called "Scout Rules".
There is a saying in the military regulations: keep the camp cleaner than when you came.
The analogy to the code is actually a very small thing, such as just changing a variable name, splitting a function that is a bit too long, eliminating a little bit of repetitive code, cleaning up a nested if statement...
This is the easiest way to make a project's code get better over time, and continuous improvement is an inherent part of professionalism.
I think I've implemented this "rule" pretty well. When I see some things that I didn't write, but I think there can be a better way of writing, and the changes are very simple and don't affect the core functions, I will Take the initiative to change it.
What I can guarantee is that after this code went through me, I didn't make it more confusing.
Split a piece of messy code into clarity, and later people are willing to continue to write down according to your structure, or continue to improve.
Are you saying this is writing "technical" code?
I don't think so.
However, I think this should be an ability that must be possessed before pursuing to write "technical" code. And it is a basic ability that is more important than writing "technical" code.
extend
The above is just a little bit of my personal opinion, but I would like to extend something else.
For example, before writing the article, I also asked this question on other sites:
https://segmentfault.com/q/1010000042111980
Everyone has different opinions and gave different answers from various angles.
This also confirms the point I said earlier:
For the "technical content", everyone and each stage have a completely different understanding.
I am pasting the replies everyone gave me, hoping it will help you too:
For another example, I recently saw a video like this on Zhihu:
https://www.zhihu.com/zvideo/1542577108190068737?page=ogv
The protagonist, Huang Xuan, said something like this:
This is already a programmer of another dimension, an answer to another dimension of "what is technical code".
I'm nowhere near that high, but I like this answer:
It will continue to be passed down and become the next generation of software, or the cornerstone of the next generation of human civilization. I feel like being able to get involved in something like this, for me, might be a programmer romance.
So what about you, what's your answer to this question?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。