This is the 6th day of my participation in the August More Text Challenge

Traffic limiting is used to prevent malicious request traffic, malicious attacks, or traffic exceeding the system peak value. There are various traffic limiting schemes distributed at different layers. In principle, traffic is restricted to the application layer. However, server-side traffic limiting is also indispensable, and here is a simple version of server-side traffic limiting that I implemented myself that can be used to generate practices. Code address: Based on RateLimiter and Redis+Lua implementation of the flow limiting component

Token bucket algorithm

The token bucket algorithm is a bucket that holds fixed capacity tokens and adds tokens to the bucket at a fixed rate. The token bucket algorithm can be described by the following concepts:

  • Tokens are placed into the token bucket at a constant rate. Like 10 per second;
  • A bucket stores a maximum of B tokens. When the bucket is full, newly added tokens are discarded or rejected.
  • When a packet of n bytes arrives, n tokens are removed from the bucket and the packet is sent to the network;
  • If there are less than n tokens in the bucket, the token is not removed and the packet is curbed (either discarded or buffered).

The diagram below:

The token algorithm controls the rate of output according to the rate of token release, that is, the rate of to network in the figure above. To network can be understood as a handler of a message, executing a certain service or calling an RPC.

Updated in real time

This component uses Nacos as the configuration center to dynamically update the flow control configuration.

Configuration parameters

/** * THROUGHPUT: Number of allowed requests per second */
private Double permitsPerSecond;

/** * warmup time: the time from low throughput to high throughput */
private Long warmupPeriod;

/** * Whether to wait */ simultaneously
private Boolean sync;

/** * Timeout duration */
private Long timeout;

/** * Validation mode Mandatory * standalone and global [standalone, global] */
private String model;

* For example, if multiple verification rules are set at the same time, you can select global or single-server verification first. */
private Integer sort = 0;

/** * Parameter name Used to obtain parameter values */ based on parameter names during dynamic flow control
private List<String> paramNames;
Copy the code

implementation

The entire code structure is as follows:

We’ll focus on the annotation, Controller, and Handler packages below.

annotation

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {

    /** * Configure center key */
    String remoteKey(a);
}
Copy the code

The annotations are simple, just a remote key that gets the remote NACOS remote configuration.

controller

Below this package are the flow control controller implementation and factory classes.

/** ** flow controller */
public interface RateLimitController {

    /** * Execute flow control *@return* /
    boolean check(a);
}
Copy the code

StandAloneRateLimitController and GlobalRateLimitController implements this interface, the corresponding single flow control and global flow control.

public interface ControllerFactory {

    /** * Create different flow controllers for different configurations *@param key remote key
     * @paramRateLimitConfig Configuration information *@return* /
    RateLimitController getInstance(String key, RateLimitConfig rateLimitConfig);
}
Copy the code

The factory class can create different flow controllers for different configurations. The default implementation is provided to support both, or you can extend the implementation yourself.

handler

public interface RateLimitHandler {

    /** * Execute flow control *@param key key for nacos
     * @paramRateLimitConfigs Flow control configuration that supports multiple *@paramParams request parameters that support dynamic flow control based on one or more parameters *@return* /
    boolean doCheck(String key, List<RateLimitConfig> rateLimitConfigs, Map<String, Object> params);
}
Copy the code

The flow control processing class is also relatively simple, which is to get the specific flow control controller instance according to the factory class and flow control configuration, and then execute flow control. There are two points to note here, one is dynamic flow control and the other is possible OOM issues.

Dynamic flow control

Flow control Dynamic flow control can be implemented based on a specific dimension, for example, the user or IP address. You can configure a parameter list in the paramNames property, and dynamically join keys based on the obtained parameter values in the background to implement dynamic flow control.

OOM issues

Flow control for dynamic parameters in the interface. It is not known how many dynamic parameters there will be in advance, but it is necessary to construct a current limiter for tracking each parameter. If there are too many parameters, memory problems (OOM) may occur.

Solution: dynamic parameters of the current limiter need to “expire”, a period of time after it is not used to automatically expire, release memory. Use Guava LoadingCache to store the dynamic parameters of the current limiter, set the expiration time of the current limiter; Use LRU algorithm to eliminate those “least recently used” current limiter, prevent a large number of dynamic parameters overflow memory.

test

The test project has been added to the project, just need to change the configuration to start. Before you do that, of course, you need to start nacOS and add configuration. The configuration reference is as follows:

{
    "test":[
        {
            "permitsPerSecond":10."warmupPeriod":10."sync":false."timeout":1000."sort": 0."model":"standalone"
        },
		{
            "permitsPerSecond":100."model":"global"."paramNames": ["user"]."sort":1}}]Copy the code

A simple test with JMeter (you can add your own timer if you’re interested) :