How to realize the program of high concurrent robbery of red envelopes based on Redis

Source: Internet
Author: User
Tags lua redis
Here is a scheme based on Redis to rob red envelopes.
The original red envelopes are called Big Red envelopes, and the red envelopes after the split are called small red envelopes.
1. Small red envelopes are generated in advance, inserted into the database, the red envelopes corresponding to the user ID is null, red envelope generation algorithm is as follows:
Pre-generated all red envelopes or a request randomly generate a red envelope
In simple terms, it is the process of decomposing a large integer m (directly to "divided into units, such as 1 or 100) into n small integers, and the range of small integers is [min, Max]."
The simplest way of thinking, the first guarantee, each small red envelopes guaranteed to have min, and then each request randomly generated a 0 to (max-min) range of integers, plus min is the amount of red envelopes.
Although this algorithm is simple, but there is a disadvantage: the last generation of red envelopes may be the min money. That is to say, maybe the last red envelopes are 0.01 yuan.
Another way is to generate all the red envelopes beforehand, which makes it easier to control. I chose to generate all the red envelopes beforehand.
The ideal red envelope generation algorithm:
The ideal result of red envelopes is the average number of red envelopes nearby, the amount of large red envelopes and small red envelopes is relatively low.
It can be imagined that the distribution of the number of red envelopes is a bit like a normal distribution.
So how to achieve this average line near the value of more requirements.
is to find an algorithm that can increase the probability near the average. Then use a "swell" and "shrink" way to achieve this effect.
Square first, then generate the square range of random number, and then root, then the probability is no longer the average.
2. Each big red envelope corresponds to two redis queues, one is the unpaid red envelope queue, the other is the red envelope. At the beginning, put all the little red envelopes that were not robbed into a queue of unpaid red envelopes.
The unpaid red envelopes are JSON strings, such as {userId: ' 789′, Money: ' 300′}.
3. Use a map in the Redis to filter the users who have robbed the red envelopes.
4. Rob Red Envelopes, the first judge whether the user robbed red envelopes, if not, then never a red envelope to take out a small red envelope, and then push to another consumption queue, and finally put the user ID into the heavy map.
5. Use a single thread batch to take out the red envelopes in the consumption queue, and then batch update the user ID of the red envelopes into the database.
The above process is very clear, but in the 4th step, if the user quickly click two times, or open two browser to rob Red envelopes, it will be possible for users to grab two red envelopes.
To solve this problem, the Lua script was adopted, allowing the entire process of step 4th to be executed atomically.
The following is a LUA script executed on the Redis:
--function: Try to get red envelopes, if successful, return the JSON string, if unsuccessful, return null
--Parameters: Red envelope queue name, consumed queue name, go to heavy map name, user ID
--return value: Nil or JSON string containing user Id:userid, red envelope id:id, red envelope amount: money
-If the user has robbed the red envelope, return nil
If Redis.call (' hexists ', keys[3], keys[4]) ~= 0 Then
return Nil
Else
--Take out a small red envelope first
Local Hongbao = Redis.call (' Rpop ', keys[1]);
If Hongbao Then
Local x = Cjson.decode (Hongbao);
--Add User ID information
x[' userId '] = keys[4];
local re = Cjson.encode (x);
--Put the user ID in the heavy set
Redis.call (' Hset ', keys[3], keys[4], keys[4]);
--Put the red envelopes in the consumer queue
Redis.call (' Lpush ', keys[2], re);
return re;
End
End
return Nil

Here is the test code:
public class Testeval {
Static String host = "localhost";
static int honbaocount = 1_0_0000;
static int threadcount = 20;
static String hongbaolist = "Hongbaolist";
static String hongbaoconsumedlist = "Hongbaoconsumedlist";
static String Hongbaoconsumedmap = "Hongbaoconsumedmap";
static Random Random = new Random ();
--function: Try to get red envelopes, if successful, return the JSON string, if unsuccessful, return null
--Parameters: Red envelope queue name, consumed queue name, go to heavy map name, user ID
--return value: Nil or JSON string containing user Id:userid, red envelope id:id, red envelope amount: money
static String Trygethongbaoscript =
"Local bconsumed = Redis.call (' hexists ', keys[3], keys[4]); \ n"
+ "Print" (' bconsumed: ', bconsumed); \ n "
"If Redis.call (' hexists ', keys[3], keys[4]) ~= 0 then\n"
+ "Return nil\n"
+ "else\n"
+ "Local Hongbao = Redis.call (' Rpop ', keys[1]); \ n"
+ "Print" (' Hongbao: ', hongbao); \ n "
+ "If Hongbao then\n"
+ "Local x = Cjson.decode (hongbao); \ n"
+ "x[' userId '] = keys[4];\n"
+ "Local re = Cjson.encode (x); \ n"
+ "Redis.call (' Hset ', keys[3], keys[4], keys[4]); \ n"
+ "Redis.call (' Lpush ', keys[2], re); \ n"
+ "Return re;\n"
+ "end\n"
+ "end\n"
+ "return nil";
Static Stopwatch watch = new stopwatch ();

public static void Main (string[] args) throws Interruptedexception {
Testeval ();
Generatetestdata ();
Testtrygethongbao ();
}

static public void Generatetestdata () throws Interruptedexception {
Jedis Jedis = new Jedis (host);
Jedis.flushall ();
Final Countdownlatch latch = new Countdownlatch (threadcount);
for (int i = 0; i < ThreadCount; ++i) {
Final int temp = i;
Thread thread = new Thread () {
public void Run () {
Jedis Jedis = new Jedis (host);
int per = Honbaocount/threadcount;
Jsonobject object = new Jsonobject ();
for (int j = temp * Each J < (temp+1) * per; J +) {
Object.put ("id", j);
Object.put ("Money", j);
Jedis.lpush (Hongbaolist, object.tojsonstring ());
}
Latch.countdown ();
}
};
Thread.Start ();
}
Latch.await ();
}

static public void Testtrygethongbao () throws Interruptedexception {
Final Countdownlatch latch = new Countdownlatch (threadcount);
System.err.println ("Start:" + System.currenttimemillis ()/1000);
Watch.start ();
for (int i = 0; i < ThreadCount; ++i) {
Final int temp = i;
Thread thread = new Thread () {
public void Run () {
Jedis Jedis = new Jedis (host);
String sha = Jedis.scriptload (Trygethongbaoscript);
Int j = Honbaocount/threadcount * TEMP;
while (true) {
Object = Jedis.eval (Trygethongbaoscript, 4, Hongbaolist, Hongbaoconsumedlist, Hongbaoconsumedmap, "" + j);
j + +;
if (object!= null) {
System.out.println ("Get Hongbao:" + object);
}else {
It's finished.
if (Jedis.llen (hongbaolist) = = 0)
Break
}
}
Latch.countdown ();
}
};
Thread.Start ();
}

Latch.await ();
Watch.stop ();

System.err.println ("Time:" + watch.gettotaltimeseconds ());
System.err.println ("Speed:" + honbaocount/watch.gettotaltimeseconds ());
System.err.println ("End:" + System.currenttimemillis ()/1000);
}
}

Test results of 20 threads, can rob 25,000 per second, enough to deal with most of the red envelopes scene.
If it is really unable to cope, split into a few redis clusters, or to bulk rob red envelopes, but also enough to deal with.
Redis's scheme, although in extreme cases (that is, Redis hang out) will lose a second of data, but it is a strong enough to cope with the high concurrent Robbery scheme.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.