Redis uses lua to solve the problem of spike inventory

Advantages of LUA scripts in Redis

– Write a complex or multi-step redis operation as a script, and submit it to redis for execution at one time, reducing the number of repeated connections to redis. Improve performance.
– LUA scripts are similar to redis transactions, have certain atomicity, will not be queued by other commands, and can complete some redis transactional operations.
– But pay attention to the lua script function of redis, which can only be used in versions above Redis 2.6.
-Use lua scripts to eliminate users and solve the problem of overselling.

SecKillController3
package com.junsang.jedisdemo01.demos.controller;

import com.junsang.jedisdemo01.demos.config.SecKill_redisByScript;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Random;

@RestController
public class SecKillController3 {
    @PostMapping(value = "/skv3")
    public String skhan(@RequestParam("prodid")String prodid){

        try {
            String userid = new Random(). nextInt(50000) + "";
            boolean b = SecKill_redisByScript.doSecKill(userid, prodid);
            System.out.println("second kill result" + b);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;


    }



}

Jedis connection pool

package com.junsang.jedisdemo01.demos.utils;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Jedis connection pool
 */
public class JedisPoolUtils {
    //Redis server IP address
    private static String ADDR = "10.70.20.47";
    //Redis port number
    private static int PORT = 6379;

    // The maximum number of available connection instances, the default value is 8
// If the assignment is -1, it means no limit
// If the connection pool has allocated maxActive Jedis instances. At this time, the state of the connection pool is exhausted (exhausted)
    private static int MAX_ACTIVE = 20;
    //Control how many Jedis instances are idle (idle) in a connection pool, and the default value is also 8
    private static int MAX_IDLE = 5;
    private static int MIN_IOLE = 0;

    //The maximum time to wait for an available connection. The unit is milliseconds. The default value is -1, which means never timeout
    //If the waiting time is exceeded, JedisConnectionException is thrown directly
    private static int MAX_WAIT = -1;
    // When allocating a Jedis instance. Whether to perform verification operation in advance
    //If true, all Jedis instances obtained are available
    private static boolean TEST_ON_BORROW = true;
    //Whether to check connection availability (ping()) when returning a Jedis instance to the connection pool
    private static boolean TEST_ON_RETURN = true;
    private static JedisPool jedisPool = null;

    /**
     * Initialize the Redis connection pool
     *
     * @return
     */
    public static JedisPool getJedisPoolInstance() {
        if (null == jedisPool) {
            //sync lock
            synchronized(JedisPoolUtils. class) {
                if (null == jedisPool) {
                    //Jedis connection pool configuration
                    JedisPoolConfig poolConfig = new JedisPoolConfig();
                    poolConfig.setMaxTotal(MAX_ACTIVE);

                    poolConfig.setMaxIdle(MAX_IDLE);
                    poolConfig.setMaxWaitMillis(MAX_WAIT);
                    poolConfig.setMinIdle(MIN_IOLE);
                    poolConfig.setTestOnBorrow(TEST_ON_BORROW);
                    poolConfig.setTestOnReturn(TEST_ON_RETURN);
                    jedisPool = new JedisPool(poolConfig, ADDR, PORT,1800000,"123456");
                }
            }
        }
        return jedisPool;
    }

    /**
     * Get the Jedis instance
     *
     * @return
     */
    public synchronized static Jedis getJedis() {
        try {
            if (jedisPool != null) {
                Jedis resource = jedisPool. getResource();
                return resource;
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Release Jedis resources
     *
     * @param jedis
     */
    public static void releaseResource(final Jedis jedis) {
        if (jedis != null) {
            jedisPool. close();
        }
    }

    public static void main(String[] args) {
        JedisPool jedisPool = JedisPoolUtils.getJedisPoolInstance();
        JedisPool jedisPool2 = JedisPoolUtils.getJedisPoolInstance();
        System.out.println(jedisPool == jedisPool2);

        Jedis jedis = null;

        try {
            jedis = JedisPoolUtils. getJedis();
            jedis.set("message", "Redis connection pool");
            System.out.println(jedis.get("message"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //Release the Jedis instance
            JedisPoolUtils. releaseResource(jedis);
        }
    }
}

SecKill_redisByScript
package com.junsang.jedisdemo01.demos.config;


import java.io.IOException;

import com.junsang.jedisdemo01.demos.utils.JedisPoolUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

public class SecKill_redisByScript {


    static String secKillScript = "local userid=KEYS[1];\r\
" + // define the first variable
            "local prodid=KEYS[2];\r\
" + // define the second variable
            "local qtkey='sk:'..prodid..":qt";\r\
" + // inventory key
            "local usersKey='sk:'..prodid..":usr";\r\
" + // key of the user who successfully kills in seconds
            "local userExists=redis.call("sismember",usersKey,userid);\r\
" + //sismember detects whether the key exists in the collection
            "if tonumber(userExists)==1 then \r\
" + // If the result of the above line is 1, it means that the user has snapped up, then return 2 directly
            " return 2;\r\
" +
            "end\r\
" +
            "local num= redis.call("get" ,qtkey);\r\
" + // get inventory
            "if tonumber(num)<=0 then \r\
" + // if inventory<=0
            " return 0;\r\
" + // directly return 0, the inventory is 0, and the rush purchase is empty
            "else \r\
" +
            " redis.call("decr",qtkey);\r\
" + // inventory -1
            " redis.call("sadd",usersKey,userid);\r\
" + // user + 1
            "end\r\
" +
            "return 1";

    static String secKillScript2 =
            "local userExists=redis.call("sismember","{sk}:0101:usr",userid);\r\
" +
                    "return 1";

    /**
     * Serious spike function
     * @param uid
     * @param prodid
     * @return
     * @throws IOException
     */
    public static boolean doSecKill(String uid,String prodid) throws IOException {

        JedisPool jedispool = JedisPoolUtils.getJedisPoolInstance();
        Jedis jedis = jedispool. getResource();

        //String sha1= .secKillScript;
        String sha1= jedis. scriptLoad(secKillScript);
        Object result= jedis. evalsha(sha1, 2, uid, prodid);
        String reString = String. valueOf(result);
        if ("0". equals( reString ) ) {
            System.err.println("Already sold out!!");
        }else if("1".equals( reString ) ) {
            System.out.println("Purchase success!!!");
        }else if("2". equals( reString ) ) {
            System.err.println("The user has robbed!!");
        }else{
            System.err.println("Purchasing exception!!");
        }
        jedis. close();
        return true;
    }
}