preface

Redis (a) how to achieve a fixed size cache?

Java implements the principle of Redis expire from zero script

Java from zero handwritten implementation of Redis (three) memory data how to restart not lost?

In this section, let’s learn how to implement a removeListener for removing listeners like the Guava-Cache and a slowListener for monitoring slow logs like the Redis SlowListener.

Delete listener

instructions

We delete data transparently to the user in two scenarios:

(1) After the size is full, carry out data elimination.

(2) clears data when expire expires.

These two features are supposed to be invisible to the user, but users can add and remove listeners to get information about the changes if they care.

Implementation approach

To implement deleted listening, we need to find the location of the deletion and then call the listener.

EVICT removes the scene

Each time the data is put, it verifies whether the maximum size limit is reached, and if it is, EVICT elimination is performed.

The expire expires scene

After the user specifies the expire time, the user returns to the background to perform the refresh asynchronously.

There are also lazy deletion scenarios.

The interface definition

For the sake of uniformity, we define the same interface for all deletes:

** @author binbin.hou * @since 0.0.6 * @param <K> key * @param <V> value */ public interface ** @author binbin.hou * @since 0.0.6 * @param <K> key * @param <V> value */ IcacheremoveListener <K,V> {/** * listen * @param context * @since 0.0.6 */ void listen(final) ICacheRemoveListenerContext<K,V> context); }

Built-in implementation

The built-in implementation of the system is as follows:

public class CacheRemoveListener<K,V> implements ICacheRemoveListener<K,V> { private static final Log log = LogFactory.getLog(CacheRemoveListener.class); @Override public void listen(ICacheRemoveListenerContext<K, V> context) { log.debug("Remove key: {}, value: {}, type: {}", context.key(), context.value(), context.type()); }}

This listener is enabled by default and cannot be turned off at this time.

The custom

Users can customize the implementation according to their own needs:

public class MyRemoveListener<K,V> implements ICacheRemoveListener<K,V> { @Override public void Listen (ICacheRemoveListenerContext < K, V > context) {System. Out. Println (" hateful, delete prompt 】 【 I deleted!" + context.key()); }}

test

ICache<String, String> cache = CacheBs.<String,String>newInstance()
        .size(1)
        .addRemoveListener(new MyRemoveListener<String, String>())
        .build();

cache.put("1", "1");
cache.put("2", "2");

We specify a cache size of 1 and set our custom delete listener.

The delete listener here can add more than one.

The log

The test log is as follows:

[the DEBUG] [the 2020-09-30 19:32:54. 617] [the main] [C.G.H.C.C.S.L.R.C acheRemoveListener. Listen] - Remove the key: 2, value: 2, type: Damn it, I've been deleted! 2

Slow operation listener

instructions

The log information related to slow operation is stored in Redis, which mainly consists of two parameters:

(1) slowlog-log-slower than a presets threshold, which is in milliseconds (1 second =1000000 microseconds). Defaults are 10000

(2) If the slowlog-max-len is slowlog-len, what is the maximum number of slowlog records to store

However, Redis is stored directly in memory and has a length limit.

According to the actual working experience, if we can add the monitoring of the slow log, and then have the corresponding storage or alarm, it will be more convenient to analyze the problem and give quick feedback.

So we introduce something like a delete listener.

Implementation approach

We process all cache operations and record the corresponding operation elapsed time.

If it takes time to operate on a user-set time threshold, the slow operation listener is called.

The interface definition

In order to keep the interface flexible, each implementation can define its own slow operation threshold, which facilitates hierarchical processing.

For example, over 100ms, the user can choose to output the WARN log; More than 1S, may affect the business, can be directly connected to the alarm system.

Public interface icacheslowListener {/** * Listen * @param context * @since 0.0.6 */ void listen(final) ICacheSlowListenerContext context); /** * SlowerThanMills (); /** * SlowerThanMills (); }

Custom listeners

Implement the interface ICachesLowListener

Each listener here can specify its own slow log threshold for hierarchical processing.

public class MySlowListener implements ICacheSlowListener { @Override public void listen(ICacheSlowListenerContext Context) {System.out.println(" [Slow Log] Name: "+ Context.methodName ()); } @Override public long slowerThanMills() { return 0; }}

use

ICache<String, String> cache = CacheBs.<String,String>newInstance()
        .addSlowListener(new MySlowListener())
        .build();

cache.put("1", "2");
cache.get("1");
  • The test results
[the DEBUG] [the 2020-09-30 17:40:11. 547] [the main] [C.G.H.C.C.S.I.C.C acheInterceptorCost. Before] - Cost start method: Put [DEBUG] [the 2020-09-30 17:40:11. 551] [the main] [C.G.H.C.C.S.I.C.C acheInterceptorCost. After] - Cost end, method: Put, cost: 10ms [slow log] name: Put [DEBUG] [the 2020-09-30 17:40:11. 554] [the main] [C.G.H.C.C.S.I.C.C acheInterceptorCost. Before] - Cost start method: Get [the DEBUG] [the 2020-09-30 17:40:11. 554] [the main] [C.G.H.C.C.S.I.C.C acheInterceptorCost. After] - Cost end, method: Get, cost: 1ms 【 slow log 】name: get

In practice, we can store slow log data for later analysis.

It can also be directly connected to the alarm system for timely feedback of problems.

summary

Listeners are relatively simple to implement, but they are more powerful to the user.

The article mainly tells about the train of thought, the realization part because of the space limitation, did not post all out.

Open source address:
https://github.com/houbb/cache

If you found this article helpful, please feel free to join us in the comments collection

Your encouragement is my greatest motivation