In-depth analysis of SpringMVC core principle: from the handwritten simple version of the MVC framework (SmartMvc) : github.com/silently952…
IDEA multithreaded file download plugin: github.com/silently952…
Public number: Beta learning JAVA
Abstract
The purpose of caching is to improve the system access speed, make the data closer to the user, and is often used to improve performance. Caching is also ubiquitous in life, such as logistics systems, they basically have warehouses everywhere, if the local warehouse has data, then the speed of delivery will be very fast; CPU read data also uses the cache, register -> cache -> memory -> hard disk/network; The Maven repository we use most often also has local and remote repositories. At present, there are more and more applications of cache, such as browser cache, reverse proxy layer cache, application layer cache, database query cache, and distributed centralized cache.
This article will start with browser caches and Nginx caches.
Browser cache
Browser caching means that when we visit a website or Http service, the server can set the Http response headers. If you set the cache-related headers, the browser will cache the data. The next time the data is accessed, it can be obtained directly from the browser cache or just need to check the validity of the cache in the server, which can reduce the cost of network time between the browser and the server and save bandwidth.
Htpp related knowledge, welcome to visit the “interview” Http protocol
Cache-Control
This command is used to control the working mechanism of the cache. The command has a few parameters. The common parameters are as follows:
- No-cache: indicates that the resource does not need to be cached
- Max-age (s): indicates the maximum validity period of the cache. If max-age is 0, no cache is required
Expires
Controls the expiration date of a resource. When a browser receives Expires, the browser will use the local cache to send a request to the server after the expiration date. If the server also specifies cache-control max-age in the response header, the browser takes precedence over max-age. If the server doesn’t want the browser to cache the resource, you can set Expires to the same value as the header field Date
Last-Modified / If-Modified-Since
Last-Modified
Last-modified Indicates when the resource was Last Modified. Used with if-modified-since, the cache is validated by time. We’re going to use this later in the game.
If-Modified-Since
If the if-modified-since date in the request header is earlier than the update date of the requested resource, the service will process it and return the latest resource. If if-modified-since the requested resource has not been updated Since the specified date, the service does not process the request and returns a response of 304 Mot Modified, indicating that the cached file is valid and can continue to be used.
Practical examples
Test code for caching using SpringMVC:
@ResponseBody @RequestMapping("/http/cache") public ResponseEntity<String> cache(@RequestHeader(value = "If-Modified-Since", required = false) String ifModifiedSinceStr) throws ParseException { DateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); Date ifModifiedSince = dateFormat.parse(ifModifiedSinceStr); long lastModifiedDate = getLastModifiedDate(ifModifiedSince); Long now = system.currentTimemillis (); int maxAge = 30; If (objects.nonNULL (ifModifiedSince) && ifModifiedSince. GetTime () == lastModifiedDate) { HttpHeaders headers = new HttpHeaders(); headers.add("Date", dateFormat.format(new Date(now))); // Set headers. Add ("Expires", dateformat.format (new Date(now + maxAge * 1000))); Add (" cache-control ", "max-age=" + maxAge); return new ResponseEntity<>(headers, HttpStatus.NOT_MODIFIED); } // The document has been modified. headers.add("Date", dateFormat.format(new Date(now))); // set headers. Add (" last-modified ", dateformat.format (new Date(lastModifiedDate))); // Set headers. Add ("Expires", dateformat.format (new Date(now + maxAge * 1000))); Add (" cache-control ", "max-age=" + maxAge); String responseBody = JSON.toJSONString(ImmutableMap.of("website", "https://silently9527.cn")); return new ResponseEntity<>(responseBody, headers, HttpStatus.OK); } // Get the last update time of the document, easy to test, change every 15 seconds; Private Long getLastModifiedDate(Date ifModifiedSince) {long Now = System.CurrentTimemillis (); if (Objects.isNull(ifModifiedSince)) { return now; } long seconds = (now - ifModifiedSince.getTime()) / 1000; if (seconds > 15) { return now; } return ifModifiedSince.getTime(); }Copy the code
- When first visited
http://localhost:8080/http/cache
We can see the following response header:
We mentioned earlier that cache-control takes precedence over Expires, so in a real world project you can use it both, or just cache-Control. The value of Expires is usually the current system time + cache expiration time
- When we visit again in 15 seconds
http://localhost:8080/http/cache
You should see the following request header:
If-modified-since is the last-modified header sent to the server. The browser checks with the server to see If the content has changed. Since the daemon has not changed the content for 15 seconds, we get the following response header
The response status code is 304, indicating that the server tells the browser that your cache is valid and can continue to be used.
If-None-Match / ETag
If-None-Match
If-none-match is the ETag value returned by the server. The server rejects the request only when the ETag value of the requested resource is inconsistent with if-none-match.
ETag
The response header field ETag tells the customer service side the identity of the response entity, which is a way to uniquely identify the resource as a string. The server can specify an ETag value for each resource. When the resource is updated, the value of ETag is also updated. The usual algorithm for generating ETag values uses MD5.
- Strong ETag value: Changes the value of the entity no matter how subtle the change
- Weak ETag value: Used only to indicate whether the resource is the same or not. The ETag will be changed only if the resource sends a fundamental change. Using weak ETag values needs to be added earlier
W/
ETag: W/"etag-xxxx"
Copy the code
A weak ETag value is usually recommended because most of the time we turn on gzip compression at the proxy layer. Weak ETag validates compressed and uncompressed entities, while a strong ETag value requires that the bytes of the response entity must be exactly the same.
Practical examples
@ResponseBody @RequestMapping("/http/etag") public ResponseEntity<String> etag(@RequestHeader(value = "If-None-Match", required = false) String ifNoneMatch) throws ParseException { long now = System.currentTimeMillis(); int maxAge = 30; String responseBody = json.tojsonString (immutablemap.of ("website", "https://silently9527.cn")); String etag = "W/\"" + MD5Encoder.encode(responseBody.getBytes()) + "\""; If (etag.equals(ifNoneMatch)) {return ResponseEntity<>(httpstatus.not_modified); } DateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); HttpHeaders headers = new HttpHeaders(); headers.add("ETag", etag); headers.add("Date", dateFormat.format(new Date(now))); Add (" cache-control ", "max-age=" + maxAge); return new ResponseEntity<>(responseBody, headers, HttpStatus.OK); }Copy the code
ETag is used to send to the server verifies the content change, request to http://localhost:8080/http/etag for the first time, access to the response header information:
Within 30 seconds, we refresh the page again and see the following request header:
If-none-match = if-none-match = if-none-match = if-none-match = if-none-match = if-none-match = if-none-match = if-none-match = if-none-match
How to choose between ETag and Last-Modified?
From the above two examples, we can see that ETag requires the server to query the content that needs to be responded to, and then calculate the ETag value, and then compare it with if-none-match in the browser request header to determine whether the data needs to be returned. For the server, it only saves bandwidth. The information that the server should have called the back-end service query is still not saved; By comparing the time of last-Modified, if the content is not updated, the server does not need to call the back-end service to query the response data, which not only saves the bandwidth of the server but also reduces the pressure of the back-end service.
After comparison, it is concluded that ETag is generally applicable to static resources such as images/JS/CSS in order to reduce the pressure of back-end services, while data such as user details that need to call back-end services are suitable for last-Modified processing.
Nginx
We usually use Nginx as a reverse proxy server. We can tune Nginx through caching and caching. In this article, we will talk about Nginx tuning from these two aspects
The buffer
By default, Nginx will fetch data from the upstream server as quickly as possible before returning a response to the client. Nginx will cache as much data as possible from the upstream server locally and then return it to the client all at once. If data returned from the upstream server needs to be written to disk each time, Nginx performance will definitely degrade; So we need to optimize Nginx cache based on the actual situation.
proxy_buffer_size
: Sets the size of the Nginx buffer used to store the header of the upstream response.proxy_buffering
: enables proxy content buffering. When this function is disabled, the proxy sends a synchronized message to the client as soon as it receives a return from the upstream server.proxy_max_temp_file_size
Is set to 0; By setting theproxy_buffering
To on.proxy_max_temp_file_size
A value of 0 ensures that the proxy process does not apply to disks, just to buffers; After the openingproxy_buffers
andproxy_busy_buffers_size
It’s the parameters that matterproxy_buffers
: Sets the number and size of the cache that responds to the upstream server. When one buffer is full, the next buffer will be requested to open until the number of buffers reaches the maximum valueproxy_busy_buffers_size
:proxy_busy_buffers_size
It’s not a space of its own. It’sproxy_buffers
andproxy_buffer_size
Part of. Nginx will start sending data to the client without fully reading the back-end response, so it will set aside a portion of the busy state for sending data to the clientproxy_buffers
It then proceeds to fetch data from the back end.proxy_busy_buffer_size
The busy parameter sets the size of the buffer in the busy state.
1) If the size of the complete data is smaller than busy_buffer, the data is transferred to the client immediately after the transfer is complete;
2) If the size of the complete data is not smaller than busy_buffer, the busy_buffer will be sent to the client immediately after it is filled.
This is typically set to twice the buffers of proxy_buffers.
The Nginx proxy buffer is set for each request. To set the buffer size to the optimal state, measure the average number of requests and response sizes that pass through the reverse proxy server. The default value of the proxy_buffers directive is 8 4KB or 8 8KB (depending on the operating system). If our server is 1G and only Nginx is running, it is conservatively estimated that Nginx can use 768MB of memory excluding the memory usage of the operating system
- Each active connection uses buffer memory: 8 4KB = 8 * 4 * 1024 = 32768 bytes
- Available memory size 768 MB: 768 x 1024 x 1024 = 805306368 bytes
- So the number of connections Nginx can handle simultaneously: 805306368/32768 = 24576
By our rough estimate, a 1-gigabyte server running Nginx alone can handle approximately 24,576 connections simultaneously.
Let’s say we measure and find that the average data size that the reverse proxy server responds to is 900KB, and the default of eight 4KB buffers is not enough, so we can adjust the size
http {
proxy_buffers 30 32k;
}
Copy the code
After setting this, the fastest response can be achieved per request, but the number of connections processed at the same time is reduced, (768 * 1024 * 1024)/(30 * 32 * 1024)=819 active connections;
If the number of concurrent requests in our system is not too high, we can reduce the number of proxy_buffers buffers and set proxy_busy_buffers_size to a slightly larger size to increase the buffer sent to the client. To ensure that Nginx can write all the data read from the upstream server into the buffer during transmission.
http {
proxy_buffers 10 32k;
proxy_busy_buffers_size 64k;
}
Copy the code
The cache
In addition to buffering upstream server responses to quickly return to the client, Nignx can also cache responses, as we can see in the figure above
- 1A: A request arrives at Nginx and tries to fetch it from the cache
- 1B: Cache does not exist to go directly to the upstream server to fetch data
- 1C: The upstream server returns a response, and Nginx puts the response into the cache
- 1D: Returns the response to the client
- 2A: Another request reaches Nginx and is looked up in the cache
- 2B: If there is corresponding data in the cache, it is returned directly without going to the upstream server to obtain the data
Nginx caches
proxy_cache_path
: The directory to place cached responses and shares.levels
Levels =1:2 indicates two levels of directories, and a maximum of three levels. 1 indicates that the level-1 directory uses one hexadecimal for its name, and 2 indicates that the level-2 directory uses two hexadecimal for its name. If all files are stored in the same directory, file access is slow due to a large number of files.keys_zone
Set the cache name and shared memory size,inactive
The maximum lifetime of not being accessed after being placed in the cache,max_size
Set the maximum size of the cacheproxy_cache
: Defines which cache the response should be stored in (keys_zone
The name of the setting)proxy_cache_key
: Sets the Key used by the cache. The default Key is a complete ACCESS URL. You can set the Key based on the actual situationproxy_cache_lock
: When multiple clients access the URL at the same time, if this configuration is enabled, only one client will fetch the response from the upstream server and put it into the cache. The other clients will wait to fetch the response from the cache.proxy_cache_lock_timeout
: to enable theproxy_cache_lock
After that, if the first request exceedsproxy_cache_lock_timeout
The default time is 5s, so all waiting requests will go to the upstream server to fetch data at the same time, which may cause backend stress.proxy_cache_min_uses
: Sets the number of times a resource is requested before it is cachedproxy_cache_use_stale
: Returns expired data to the client in case of an error in accessing the upstream server. This is an option when cached content is not sensitive to expiration timeproxy_cache_valid
: Sets the cache time for different response status codes. If setproxy_cache_valid 5s
, all status codes will be cached.
Set the maximum cache lifetime to 6 hours, the cache size to 1 gb, and the cache validity period to 1 day as follows:
http { proxy_cache_path /export/cache/proxy_cache keys_zone=CACHE:10m levels=1:2 inactive=6h max_size=1g; server { location / { proxy_cache CACHE; Proxy_cache_valid 1d; 1d proxy_pass: http://upstream; }}}Copy the code
If a set-cookie header is Set in the current response, the current response will not be cached. This can be achieved by ignoring the header using proxy_ignore_HEADERS
proxy_ignore_headers Set-Cookie
Copy the code
If we do, we need to include the value in the cookie as part of the proxy_cache_key to prevent different data from the same URL response from overwriting the cached data and returning the wrong data to the client
proxy_cache_key "$host$request_uri$cookie_user"
Copy the code
Note that this is still a problem because adding cookies to the cached key can cause multiple copies of the public resource to be cached, resulting in wasted space. To solve this problem we can allocate different resources separately, for example:
server { proxy_ignore_headers Set-Cookie; location /img { proxy_cache_key "$host$request_uri"; proxy_pass http://upstream; } location / { proxy_cache_key "$host$request_uri$cookie_user"; proxy_pass http://upstream; }}Copy the code
Clear the cache
Although we set the cache to speed up the response, sometimes we will encounter the cache error request, usually we need to open a back for ourselves, so that we can find the problem and manually clear the cache in time. Nginx could consider using the ngx_cache_purge module for cache cleansing.
Location ~ /purge/.* {allow 127.0.0.1; deny all; proxy_cache_purge cache_one $host$1$is_args$args }Copy the code
This method restricts access; Proxy_cache_purge module, cache_one specified key_zone, $host$1$is_args$args specified parameter to generate cache keys
storage
If you have large static files, which are not likely to be modified, then you don’t need to set a cache expiration date for them and let Nginx store them directly. If the upstream server modifies these files, a separate program can be provided to remove the corresponding static files.
http { proxy_temp_path /var/www/tmp; server { root /var/www/data; location /img { error_page 404 = @store } location @store { internal; proxy_store on; proxy_store_access group:r all:r; proxy_pass http://upstream; }}}Copy the code
The request first looks for the file in /img, and then upstream if it doesn’t exist. The internal directive is used to specify that only internal calls from the local Nginx are allowed; external calls directly return a 404 Not Found status. Proxy_store indicates that files returned from upstream servers need to be stored in /var/www/data; Proxy_store_access Sets the access permission
conclusion
Cache-Control
andExpires
Set the validity period of the resource cache- use
Last-Modified / If-Modified-Since
Determine whether the cache is valid - use
If-None-Match / ETag
Determine whether the cache is valid - Tune Nginx by configuring the Nginx buffer size
- Use Nginx caching to speed up request responses
How to speed up the request response, this article mainly focuses on Http caching and Nignx reverse proxy two aspects of caching, you think this is the end of it? No! In the next article, we’ll talk about caching from an application perspective
To the last point of attention, do not get lost
There may be more or less deficiencies and mistakes in the article, suggestions or opinions are welcome to comment and exchange.
Finally, white piao is not good, creation is not easy, I hope friends can like the comments pay attention to three even, because these are all the power source I share 🙏
Public number: Beta learning JAVA
🏆 technology project issue 8 chat | magical function and problems of cache