The optimization method listed in this blog post is aimed at comparing traditional projects , but wants to improve the throughput of the system. Now the fashionable technology is still not separated from the front and back ends. Use nginx as a static resource server to proxy our static resources.

Who restricted the Throughput?

When we stress test a traditional project, the container found that the system’s Throughput was limited by the database (mysql), although the code does not seem to be wrong, the logic is not wrong, but too many requests are Was hit the database, the database has opened a large number of IO operations, such a large load will even make the overall load of the Linux system suddenly soar, but in contrast to the throughput of our system, huh, huh…

Looking to the cache

Since mysql’s compression capability limits our system, we cache the data and do everything possible to reduce the number of direct contacts between the user and the database, so that the throughput of our system can be the number of requests from the processor at the same time. Naturally will go up

There are a lot of caching technologies on the market, and the two cache databases are Memcache and Redis.

The difference between Redis and Memcahe

  • Redis not only supports key-value key-value pair data, but also supports data structures such as list, set, and hash.
  • Redis supports data backup, ie cluster backup in master-slaver mode
  • Redis supports data persistence, it can save in-memory data on disk, support RDB and AOF two persistent forms

Pressure measurement on Redis

Copy# 挨个测试redis中的命令
# 每个数据包大小是3字节
# 100个并发, 发起10万次请求
redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 100000

[root@139 ~]# redis-benchmark -h 127.0.0.1 -p 9997 -c 100 -n 100000
====== PING_INLINE ======
  100000 requests completed in 1.04 seconds
  100 parallel clients
  3 bytes payload
  keep alive: 1

98.68% <= 1 milliseconds // 百分之98.68的请求在1毫秒内完成了
99.98% <= 2 milliseconds 
100.00% <= 2 milliseconds
96525.09 requests per second  // 每秒完成的请求数在9万六左右


-d  指定数据包的大小,看下面redis的性能还是很强大的
-q  简化输出的参数
[root@139 ~]# redis-benchmark -h 127.0.0.1 -p 9997 -q -d 100 -c 100 -n 100000
PING_INLINE: 98619.32 requests per second
PING_BULK: 95877.28 requests per second
SET: 96153.85 requests per second
GET: 95147.48 requests per second
INCR: 95238.10 requests per second
LPUSH: 95328.88 requests per second
RPUSH: 95877.28 requests per second
LPOP: 95328.88 requests per second
RPOP: 97276.27 requests per second
SADD: 96339.12 requests per second
HSET: 98231.83 requests per second
SPOP: 94607.38 requests per second
LPUSH (needed to benchmark LRANGE): 92165.90 requests per second
LRANGE_100 (first 100 elements): 97181.73 requests per second
LRANGE_300 (first 300 elements): 96153.85 requests per second
LRANGE_500 (first 450 elements): 94428.70 requests per second
LRANGE_600 (first 600 elements): 95969.28 requests per second
MSET (10 keys): 98231.83 requests per second

只测试 指定的命令
-t 跟多个命令参数
[root@139 ~]# redis-benchmark -p 9997 -t set,get -q -n 100000 -c 100 
SET: 97276.27 requests per second
GET: 98135.42 requests per second

From the above stress test, you can see that the performance of Redis is absolute strength, quite powerful, and is not an order of magnitude compared with mysql, so the conclusion is very obvious, if we add a layer of redis to the cache in the user and mysql key The throughput of the system will naturally go up

So in order to improve the system’s ability to withstand pressure, we gradually transfer the pressure from mysql to redis

Page Cache Technology

Before we talk about page caching, let’s talk about the life cycle of a request in a traditional project: Probably from the browser to the server, the server queries the database to get the result, and then passes the result data to the template engine. Render data into html page

To improve the speed of this process, we can do this, page caching, as the name suggests, is to cache the html page into the cache database.

An example is as follows:

In the beginning, we will first try to get the rendered html source response from the cache to the client. The format of the response is @ResponseBody和producescontrolled by the properties in the cache , telling the browser that it will return it to the html text.

Advantages: The pressure of the user’s request is transferred from mysql to redis. This strength is not a problem for redis single machine.

Disadvantages: Obviously, expanding the request to the page level, data consistency will inevitably be affected, which is a point that has to be considered when using the page cache.

Feature 1: Strictly control the cache time, don’t forget to add the expiration time…

Feature 2: It turns out that thymeleaf automatically completes the rendering of the data. Now, obviously, we are manually rendering the data.

How_to_improve_the_throughput_of_web_applications_0.png

Copy    @RequestMapping(value = "/to_list",produces = "text/html;charset=UTF-8")
    @ResponseBody
    public String toLogin(Model model, User user, HttpServletResponse response, HttpServletRequest request) {

        // 先从redis缓存中获取数据
        String html = redisService.get(GoodsKey.goodsList, "", String.class);
        if (html != null)
            return html;

        // 查询商品列表
        List<GoodsVo> goodsList = goodsService.getGoodsList();
        model.addAttribute("goodsList", goodsList);

        // 使用Thymeleaf模板引擎手动渲染数据
        WebContext springWebContext = new WebContext(request,response,request.getServletContext(),request.getLocale(),model.asMap());
        String goods_list = thymeleafViewResolver.getTemplateEngine().process("goods_list", springWebContext);

        // 存入redis
        if (goods_list!=null){
            redisService.set(GoodsKey.goodsList,"",goods_list);
        }
        return goods_list;
    }

Since I have said it here, I will continue to say how I can play it…

You see, the api source code that has been rendered by manually controlling the api of the template engine, what is already rendered? What is uttered is that I wrote on the front end: th ${user}, such a placeholder , has now been replaced by thymeleaf into Zhang San… (speaking enough)

After getting the source code that has been rendered, we can write this file to a certain directory of the system through IO operation. I don’t know if you have found it. When you visit Jingdong Taobao to browse a product page, you will find the url. Is similar to this www.jjdd.com/aguydg/ahdioa/1235345.html

This suffix 123145.html has a high probability that Jingdong uses static page technology. This is wise. It is not bad to use such a huge number of product information suffixes with numbers, and the speed is not fast?

How to achieve this effect?

It is said above, through IO to write the data of these source code to a directory under Linux, the file name is the last number in the above URL, and these xxx.html are proxyed by Nginx as a static resource server, and the user accesses again. If you go to this static page, you will not touch the database, and nginx also supports zero copy, and the number of 50,000 is not a problem…
Also, the suffix array is best not to be scribbled, it is better to use the product id directly, after all, Yes, click on the item to get the id, then enter the static page.

Object Cache Technology

Caching objects in java, such as persisting user information into redis, each time the user queries their own information, first queries from redis, and sometimes returns directly, if not, then queries the database, thus also implementing the user and database. Adding a layer of cache between them can also greatly improve the throughput of the system.

How do you usually play?

How_to_improve_the_throughput_of_web_applications_1.png

The user’s request attempts to obtain the object information from redis before querying the database. If the redis does not exist, it will go to the database for query. After the result is queried, the result will be saved into redis.

Copy// todo 使用redis做缓存,减少和数据库的接触次数
public Label findById(Long labelId) {

    // 先尝试从缓存中查询当前对象
    Label label = (Label) redisTemplate.opsForValue().get("label_id" + labelId);

    if (label==null){
        Optional<Label> byId = labelRepository.findById(labelId);
        if (!byId.isPresent()) {
            // todo 异常
        }
        label = byId.get();

        // 将查出的结果存进缓存中
        redisTemplate.opsForValue().set("label_id"+label.getId(),label);
    }
    return label;
}

When the user updates the data, the database is updated first, and then the cache of the response in redis is deleted/updated.

Copypublic void update(Long labelId, Label label) {
    label.setId(labelId);
    Label save = labelRepository.save(label);

    // todo 数据库修改成功后, 将缓存删除
    redisTemplate.delete("label_id"+save.getId());
    }

When the user deletes the data, first delete the data in the database, and then delete the cache in redis.

Copypublic void delete(Long labelId) {
    labelRepository.deleteById(labelId);

    // todo 数据库修改成功后, 将缓存删除
    redisTemplate.delete("label_id"+labelId);
}

Imitate Vue to implement page staticization

Everyone is saying that the page is static. Is it really so amazing? Actually, it is not so magical. To put it bluntly, the data on the traditional web page is rendered by the template engine (such as JSP or template like thymeleaf). Engine), the rendering of the data in the static webpage is done by js, and the static resources of this webpage and project such as js, css are placed under a directory, the status is the same as the ordinary static resources, and The advantage is that the browser gives the benefits , because the browser is cached for static resources, if you are good at observing, you will find that sometimes a page is repeatedly requested, the page is displayed normally, but the status code is 304… request Still will reach the server, but the server will tell the browser that the page it wants to visit has not changed, so the browser will find the local cache.

Nowadays the most popular play static pages among the most popular technique is Angular.js and Vue.js, also really easy to use, a few months ago I wrote notes about vue, interested students can go and see
click to see my Vue notes

In this blog, it is just not used VUE, but the idea of ​​implementing static pages and vue is not bad, the same is to achieve static page through js code

  • First of all, how to write the backend code?

Separation of the front and back ends, naturally json interaction, the back end is @ResponseBodyreturned to the front-end json object through control, and it is recommended that you also have a whole VO object, use this VO object to package all kinds of data together, and return it to the front end once. , it seems that the backend is really simple, that is, returning a json object

  • How to write the front end?

The first thing is to move the html file from the template folder to the static folder, and let the html file and js/css file be called brothers.

Then give this xxx.html the name, why do you want to change the name of the directory? Because SpringBoot is more than the encoding, what is the file under what directory, and the default configuration information of Thymeleaf part is as follows

Copy@ConfigurationProperties(prefix = "spring.thymeleaf")
public class ThymeleafProperties {

    private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;

    public static final String DEFAULT_PREFIX = "classpath:/templates/";

    public static final String DEFAULT_SUFFIX = ".html";

No way, these configuration information by default think that the templates in the class path are xxx.html files.

The second thing is to remove the namespace like thymeleaf introduced in the html tag, the static page does not need

Copy<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>商品列表</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <!-- jquery -->
    <!--<script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>-->
    <script type="text/javascript" th:src="js/jquery.min.js"></script>

The third thing is to write an ajax. When the page is loaded, it will trigger the request to the background, get the data, and operate the nodes to complete the data rendering through jQuery.

After three steps to finish the work, it is not very complicated to see, but our page has become a static page. From then on, she will be cached by the browser. As long as the page does not change, the browser will always use its own cache. The amount of data transmitted on the network is much more powerful, and the system RT is absolutely ascending several orders of magnitude.

Optimization of static resources

Let’s talk about the common static resource optimization techniques on the market:

  • Compression and optimization of js/css, reducing traffic
  • Multiple js/css combined to reduce the number of connections
  • Tengine technology
  • webPack, when using vue development, it will package a set of vue dependencies into a js html file, isn’t it cool?
  • CDN acceleration technology, many cloud service providers have provided, and the price is not expensive, it is also a good choice to put large static resources on the CDN for acceleration.
  • Use Nginx to do static resource proxy, and Nginx supports zero copy. It also supports data file compression and transmission on the network. Its own concurrency is also very powerful. When it is static resource server, it is definitely the first choice. Believe me, you will Fall in love with it

In addition, when multiple users concurrently modify the inventory, the inventory is actually changed to a negative number. The database engine used by itself is innodb. There is a row-level lock. We only need to modify our sql to add the condition and stock_number > 0.

In order to prevent the same user from sending two requests and occasionally killing multiple items, we go to the miaosha_user table to create a unique index, establish a unique index for the userId, and not allow the same userId to appear twice, thus avoiding the above. Happening

Captcha Technology

  • benefit

There are many advantages to let users enter the verification code. In addition to verifying the user’s identity information, the most obvious benefit is to spread the pressure on the system. If the front end does not add the image verification code, the system needs to carry 10,000 concurrently within 1 s. , but with the addition of a picture verification code, you can spread the 10,000 concurrents to less than 10 seconds, or even more

  • The overall idea:The image verification code is just an image, so the front end wants to display it. It definitely needs an img tag. What is a bad place to think about it? It is the path of this picture, src=啥, what can we do? Directly through this path to the src to generate the imge’s Controller, each time the page is refreshed, it will initiate a request to this path, the Controller will generate an image, and the output stream will be generated by HttpServletResponse. The image is sent to the browser, plus he is the img tag, is this not ok?

Baidu’s technology on how to generate image verification code is really a lot. I don’t paste the code. Interested students are Baidu, and the code is piece by piece.

  • How to achieve click refresh to complete the refresh operation?

Because this image is a static resource, if you do not disable the cache, the image will be cached. To replace the verification code every time you click on the image, refer to the following js implementation, add a timestamp.

Copy   function refreshImageCode() {
        $("#verifyCodeImg").attr("src","/path/verifyCode?goodsId="+$("#goodsId").val()+"&timestamp="+new Date());
    }

Interface Current Limiting Technology

  • What is interface current limiting?

For example, if we want to limit the number of times a user can access the A method in A Controller in less than 30 times in a minute, this is actually a requirement for interface current limiting, which can effectively prevent malicious access by users.

  • How to achieve interface current limit?

In fact, this thing is not difficult to achieve with the cache, for example, I use the above example: Do you want to limit the flow of a method? We add the following logic in a method

The pseudo code is as follows:

Copypublic void a(User user){
    // check user
    // limit
   Integer count = redis.get(user.getId());
    if(count!=null&&count>15)
      return ; // 
    if(count==null){
        redis.set(user.getId(),30,1); // visit once,valid key time is 30s
    }else{
        redis.incr(user.getId());
    }
}
  • How to prevent the logic of the current limit from infecting the business code?

We can use the interceptor technology, if we rewrite the interceptor preHandler()method, it will callback before executing the method in the Controller, and then with the custom annotation technology, the latter is simply for…

Example:

Copy@Component
public class AccessIntercepter extends HandlerInterceptorAdapter {
    // check before execute
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod){
            HandlerMethod hd = (HandlerMethod) handler;
            LimitAccess methodAnnotation = hd.getMethodAnnotation(LimitAccess.class);
            if (methodAnnotation==null)
                return true;

            // parse
            int maxCount = methodAnnotation.maxCount();
            boolean needLogin = methodAnnotation.needLogin();
            int second = methodAnnotation.second();
            // todo
        }
        return true;
    }
}

Conclusion: I have recently passed the exam week, this Saturday, next Wednesday, the test operations… I hope I can spend it safely…

I am a blogger giving me a daydream, welcome to praise support

Orignal link:https://www.cnblogs.com/ZhuChangwu/p/11872124.html