1, Resolve transaction operations for counters and personnel records
2, Redis transaction - second kill concurrency simulation
Use tool ab to simulate the test
1. Networking: Yum install httpd tools
2. No network
(1) Enter cd /run/media/root/CentOS 7 x86_64/Packages (the path is different from centos6)
(2) Sequential installation
apr-1.4.8-3.el7.x86_64.rpm apr-util-1.5.2-6.el7.x86_64.rpm httpd-tools-2.4.6-67.el7.centos.x86_64.rpm
3. Test and results
① Pass ab test
vim postfile simulates form submission parameters, ending with & symbol; Store the current directory.
Content: prodid = 0101&
command
ab -n 2000 -c 200 -k -p ~/postfile -T application/x-www-form-urlencoded http://192.168.2.115:8081/Seckill/doseckill
② . oversold
3, Oversold problem
4, Use optimistic lock to eliminate users and solve the oversold problem.
5, Continue to add concurrent tests
1. Connection Limited
ab -n 2000 -c 200 -k -p postfile -T 'application/x-www-form-urlencoded' http://192.168.140.1:8080/seckill/doseckill
Add the - r parameter, - r Don't exit on socket receive errors
ab -n 2000 -c 100 -r -p postfile -T 'application/x-www-form-urlencoded' http://192.168.140.1:8080/seckill/doseckill
2. It's been seconds, but there's still inventory
ab -n 2000 -c 100 -p postfile -T 'application/x-www-form-urlencoded' http://192.168.137.1:8080/seckill/doseckill
It's been seconds, but there's still inventory. The reason is that optimistic locks cause many requests to fail. The first one didn't arrive, and the second one may arrive.
3. Connection timeout, solved by connection pool
4. Connection pool
Save the consumption caused by each connection to the redis service, and reuse the connected instances.
Manage the behavior of connections through parameters
Connection pool parameters
MaxTotal: Control one pool How many can be allocated jedis Instance, by pool.getResource()To obtain; If the value is-1,Means no restriction; If pool Already assigned MaxTotal individual jedis Instance, then pool The status of is exhausted. maxIdle: Control one pool How many states are at most idle(free)of jedis example; MaxWaitMillis: Mean when borrow One jedis The maximum number of milliseconds to wait for an instance. If the waiting time is exceeded, it will be thrown directly JedisConnectionException; testOnBorrow: Get one jedis Whether to check the connection availability when the instance is running( ping());If yes true,Then you get jedis All instances are available;
6, Solve inventory problems
1. LUA script
Lua is a small script language. Lua script can be easily called by C/C + + code or call C/C + + functions in turn. Lua does not provide a powerful library. A complete Lua interpreter is only 200k, so Lua is not suitable for developing independent applications, but as an embedded script language.
Many applications and games use LUA as their own embedded scripting language to achieve configurability and scalability.
This includes many game plug-ins or plug-ins such as Warcraft map, world of Warcraft, Bode's gate, angry birds and so on.
https://www.w3cschool.cn/lua/
2. Advantages of LUA script in Redis
Write complex or multi-step redis operations into a script and submit them to redis for execution at one time to reduce the number of repeated connections to redis. Improve performance.
LUA scripts are similar to redis transactions. They have certain atomicity and will not be cut in line by other commands. They can complete some redis transactional operations.
Note that the lua script function of redis can only be used in redis version 2.6 or above.
Use lua script to eliminate users and solve the oversold problem.
After version 2.6 of redis, the contention problem is solved through lua script. In fact, redis uses its single thread feature to solve the multi task concurrency problem by means of task queue.
7, Redis_ Business_ Second kill case_ code
1. Project structure
2. First Edition: simple Edition
Click 10 times, normal second kill
It's normal for students to have a try together. This is because the effect of concurrency cannot be achieved.
Using tool ab to simulate concurrent testing, oversold will occur. A negative number appears when viewing inventory.
3. Second Edition: add transaction optimistic lock (solve oversold), but there are legacy inventory and connection timeout
4. Version 3: connection pool solves timeout problem
5. Fourth Edition: Solving Inventory dependency, LUA script
code
package com.atguigu; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class JedisPoolUtil { private static volatile JedisPool jedisPool = null; private JedisPoolUtil() { } public static JedisPool getJedisPoolInstance() { if (null == jedisPool) { synchronized (JedisPoolUtil.class) { if (null == jedisPool) { JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(200); poolConfig.setMaxIdle(32); poolConfig.setMaxWaitMillis(100*1000); poolConfig.setBlockWhenExhausted(true); poolConfig.setTestOnBorrow(true); // ping PONG jedisPool = new JedisPool(poolConfig, "192.168.44.168", 6379, 60000 ); } } } return jedisPool; } public static void release(JedisPool jedisPool, Jedis jedis) { if (null != jedis) { jedisPool.returnResource(jedis); } } }
Via lua script
package com.atguigu; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.LoggerFactory; import ch.qos.logback.core.joran.conditional.ElseAction; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.ShardedJedisPool; import redis.clients.jedis.Transaction; public class SecKill_redisByScript { private static final org.slf4j.Logger logger =LoggerFactory.getLogger(SecKill_redisByScript.class) ; public static void main(String[] args) { JedisPool jedispool = JedisPoolUtil.getJedisPoolInstance(); Jedis jedis=jedispool.getResource(); System.out.println(jedis.ping()); Set<HostAndPort> set=new HashSet<HostAndPort>(); // doSecKill("201","sk:0101"); } static String secKillScript ="local userid=KEYS[1];\r\n" + "local prodid=KEYS[2];\r\n" + "local qtkey='sk:'..prodid..\":qt\";\r\n" + "local usersKey='sk:'..prodid..\":usr\";\r\n" + "local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" + "if tonumber(userExists)==1 then \r\n" + " return 2;\r\n" + "end\r\n" + "local num= redis.call(\"get\" ,qtkey);\r\n" + "if tonumber(num)<=0 then \r\n" + " return 0;\r\n" + "else \r\n" + " redis.call(\"decr\",qtkey);\r\n" + " redis.call(\"sadd\",usersKey,userid);\r\n" + "end\r\n" + "return 1" ; static String secKillScript2 = "local userExists=redis.call(\"sismember\",\"{sk}:0101:usr\",userid);\r\n" + " return 1"; public static boolean doSecKill(String uid,String prodid) throws IOException { JedisPool jedispool = JedisPoolUtil.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("Empty!!"); }else if("1".equals( reString ) ) { System.out.println("Rush purchase succeeded!!!!"); }else if("2".equals( reString ) ) { System.err.println("The user has robbed!!"); }else{ System.err.println("Panic buying exception!!"); } jedis.close(); return true; } }
package com.atguigu; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.LoggerFactory; import ch.qos.logback.core.rolling.helper.IntegerTokenConverter; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.ShardedJedisPool; import redis.clients.jedis.Transaction; /** * */ public class SecKill_redis { public static void main(String[] args) { Jedis jedis =new Jedis("192.168.44.168",6379); System.out.println(jedis.ping()); jedis.close(); } //Second kill process public static boolean doSecKill(String uid,String prodid) throws IOException { //1. Judgment of uid and prodid non null if(uid == null || prodid == null) { return false; } //2. Connect to redis //Jedis jedis = new Jedis("192.168.44.168",6379); //Get jedis object through connection pool JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance(); Jedis jedis = jedisPoolInstance.getResource(); //3 splicing key // 3.1 inventory key String kcKey = "sk:"+prodid+":qt"; // 3.2 second kill successful user key String userKey = "sk:"+prodid+":user"; //Monitor inventory jedis.watch(kcKey); //4. Get the inventory. If the inventory is null, the second kill has not started yet String kc = jedis.get(kcKey); if(kc == null) { System.out.println("The second kill hasn't started yet, please wait"); jedis.close(); return false; } // 5 judge whether the user repeats the second kill operation if(jedis.sismember(userKey, uid)) { System.out.println("The second kill has been successful. You can't repeat the second kill"); jedis.close(); return false; } //6. Judge if the commodity quantity and inventory quantity are less than 1, the second kill is over if(Integer.parseInt(kc)<=0) { System.out.println("The second kill is over"); jedis.close(); return false; } //7 second kill process //Use transaction Transaction multi = jedis.multi(); //Team operation multi.decr(kcKey); multi.sadd(userKey,uid); //implement List<Object> results = multi.exec(); if(results == null || results.size()==0) { System.out.println("The second kill failed...."); jedis.close(); return false; } //7.1 inventory-1 //jedis.decr(kcKey); //7.2 add successful users to the list //jedis.sadd(userKey,uid); System.out.println("The second kill succeeded.."); jedis.close(); return true; } }