赞
踩
第一篇我们总结了秒杀的整个流程,本篇我们详细介绍下redis的秒杀实现,基于.net core2.2开发。
首先,需要安装redis,因为我在本地测试的,所以安装的windows版本的redis。redis分为服务端和客户端,这个redis怎么安装,本篇不详细说明,如果有安装问题和无法下载redis的windows版本的话可以私聊我。
第二步,就是编码部分,新建一个web api接口服务,使用redis的lua脚本做库存扣减,属于原子操作。大家就用下面的代码,可以支持1000个先线程的并发,在大的并发我没有测试,可能需要换架构。核心编码如下:
- public string doKill(string threadId)
- {
- string tId = threadId;
- string uid = getCheckNumber(); //生成一个随机的用户id
- string prodid = "1";//商品固定001
- try
- {
- ConnectionMultiplexer redis = radisTemplate.getConn();
- IDatabase db = redis.GetDatabase();
-
- //定义Lua脚本
- 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..\":user\";\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";
-
- RedisKey[] s = new RedisKey[2];
- s[0] = uid;
- s[1] = prodid;
-
- //使用ScriptEvaluate执行脚本
- Object result = db.ScriptEvaluate(secKillScript, s);
- string result1 = result.ToString();
- if ("0".Equals(result1))
- {
- Log.Info($"线程:{tId}:已抢空");
- return "已抢空!!";
- }
- else if ("1".Equals(result1))
- {
- RequestDto requestDto = new RequestDto
- {
- Id = tId,
- SuccessDate = DateTime.Now,
- Message = $"请求成功:{tId}"
- };
- string str = JsonConvert.SerializeObject(requestDto);
- db.ListLeftPushAsync("sk2", str);
- Log.Info($"线程:{tId}:抢到了");
- return "抢购成功!!!!";
- }
- else if ("2".Equals(result1))
- {
- Log.Info($"线程:{tId}:该用户已抢过");
- return "该用户已抢过!!";
- }
- else
- {
- Log.Info($"线程:{tId}:抢购异常");
- return "抢购异常!!";
- }
- }
- catch (Exception ex)
- {
- Log.Info($"线程:{tId}:发生错误");
- Console.WriteLine(ex);
- Log.Error($"发生错误:{ex}");
- return "系统异常!!";
- }
- }
- public static String getCheckNumber()
- {
- Random rd = new Random();
- int num = rd.Next(100000, 1000000);
- return num.ToString();
- }
- ///这上面是接口代码
-
-
- ///这边封装的一个redis帮助类
-
-
- public class RadisConn
- {
- //定义连接器
- static ConnectionMultiplexer redis = null;
- //获取连接数据库
- public ConnectionMultiplexer getConn()
- {
- try
- {
- if (redis != null && redis.IsConnected)
- {
- //Log.Info($"redis实例有效,直接返回!");
- return redis;
- }
- //Log.Info($"redis实例无效,开始连接服务!");
- redis = ConnectionMultiplexer.Connect("127.0.0.1:6379,password=123456,abortConnect=false");
- //if (redis.IsConnected)
- //{
- // Log.Info($"redis 连接服务成功!");
- //}
- }
- catch (Exception e)
- {
- Console.WriteLine(e.Message);
- }
-
- return redis;
- }
-
- }
-
- public class RequestDto
- {
- public string Id { get; set; }
- public DateTime SuccessDate { get; set; }
- public string Message { get; set; }
- }
第三步,切换到redis客户端,然后设置库存 set sk:1:qt 50
第四部,用压力测试工具或者自己写一个200个任务的请求这个接口,看看库存扣减是否正常。提供一个多线程实例,代码如下:
- using Newtonsoft.Json;
- using System;
- using System.Net.Http;
- using System.Threading.Tasks;
-
- namespace HttpClient请求
- {
- internal class Program
- {
- static void Main(string[] args)
- {
- try
- {
- for (int i = 0; i <40; i++)
- {
- Task task = Task.Run(() =>
- {
- Console.WriteLine($"创建线程成功,当前线程号:{Task.CurrentId}");
- SendRequest(Task.CurrentId.ToString());
- });
- }
- Console.ReadKey();
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- }
- }
-
- static void SendRequest(string threadId)
- {
- try
- {
- var url = $"https://localhost:5001/api/values/doKill?threadId="+ threadId;
- using (var client = new HttpClient())
- {
- var content = client.GetStringAsync(url).Result;
- //var data = JsonConvert.DeserializeObject<dynamic>(content);
- Console.WriteLine($"线程号:{threadId}:{content}");
- }
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- }
- }
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。