当前位置:   article > 正文

基于net core2.2的redis秒杀+数据持久化+数据恢复系列(2)_.net core 使用redis持久化

.net core 使用redis持久化

第一篇我们总结了秒杀的整个流程,本篇我们详细介绍下redis的秒杀实现,基于.net core2.2开发。

首先,需要安装redis,因为我在本地测试的,所以安装的windows版本的redis。redis分为服务端和客户端,这个redis怎么安装,本篇不详细说明,如果有安装问题和无法下载redis的windows版本的话可以私聊我。

第二步,就是编码部分,新建一个web api接口服务,使用redis的lua脚本做库存扣减,属于原子操作。大家就用下面的代码,可以支持1000个先线程的并发,在大的并发我没有测试,可能需要换架构。核心编码如下:

  1. public string doKill(string threadId)
  2. {
  3. string tId = threadId;
  4. string uid = getCheckNumber(); //生成一个随机的用户id
  5. string prodid = "1";//商品固定001
  6. try
  7. {
  8. ConnectionMultiplexer redis = radisTemplate.getConn();
  9. IDatabase db = redis.GetDatabase();
  10. //定义Lua脚本
  11. string secKillScript = "local userid=KEYS[1];\r\n" +
  12. "local prodid=KEYS[2];\r\n" +
  13. "local qtkey='sk:'..prodid..\":qt\";\r\n" +
  14. "local usersKey='sk:'..prodid..\":user\";\r\n" +
  15. "local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" +
  16. "if tonumber(userExists)==1 then \r\n" +
  17. " return 2;\r\n" +
  18. "end\r\n" +
  19. "local num= redis.call(\"get\" ,qtkey);\r\n" +
  20. "if tonumber(num)<=0 then \r\n" +
  21. " return 0;\r\n" +
  22. "else \r\n" +
  23. " redis.call(\"decr\",qtkey);\r\n" +
  24. " redis.call(\"sadd\",usersKey,userid);\r\n" +
  25. "end\r\n" +
  26. "return 1";
  27. RedisKey[] s = new RedisKey[2];
  28. s[0] = uid;
  29. s[1] = prodid;
  30. //使用ScriptEvaluate执行脚本
  31. Object result = db.ScriptEvaluate(secKillScript, s);
  32. string result1 = result.ToString();
  33. if ("0".Equals(result1))
  34. {
  35. Log.Info($"线程:{tId}:已抢空");
  36. return "已抢空!!";
  37. }
  38. else if ("1".Equals(result1))
  39. {
  40. RequestDto requestDto = new RequestDto
  41. {
  42. Id = tId,
  43. SuccessDate = DateTime.Now,
  44. Message = $"请求成功:{tId}"
  45. };
  46. string str = JsonConvert.SerializeObject(requestDto);
  47. db.ListLeftPushAsync("sk2", str);
  48. Log.Info($"线程:{tId}:抢到了");
  49. return "抢购成功!!!!";
  50. }
  51. else if ("2".Equals(result1))
  52. {
  53. Log.Info($"线程:{tId}:该用户已抢过");
  54. return "该用户已抢过!!";
  55. }
  56. else
  57. {
  58. Log.Info($"线程:{tId}:抢购异常");
  59. return "抢购异常!!";
  60. }
  61. }
  62. catch (Exception ex)
  63. {
  64. Log.Info($"线程:{tId}:发生错误");
  65. Console.WriteLine(ex);
  66. Log.Error($"发生错误:{ex}");
  67. return "系统异常!!";
  68. }
  69. }
  70. public static String getCheckNumber()
  71. {
  72. Random rd = new Random();
  73. int num = rd.Next(100000, 1000000);
  74. return num.ToString();
  75. }
  76. ///这上面是接口代码
  77. ///这边封装的一个redis帮助类
  78. public class RadisConn
  79. {
  80. //定义连接器
  81. static ConnectionMultiplexer redis = null;
  82. //获取连接数据库
  83. public ConnectionMultiplexer getConn()
  84. {
  85. try
  86. {
  87. if (redis != null && redis.IsConnected)
  88. {
  89. //Log.Info($"redis实例有效,直接返回!");
  90. return redis;
  91. }
  92. //Log.Info($"redis实例无效,开始连接服务!");
  93. redis = ConnectionMultiplexer.Connect("127.0.0.1:6379,password=123456,abortConnect=false");
  94. //if (redis.IsConnected)
  95. //{
  96. // Log.Info($"redis 连接服务成功!");
  97. //}
  98. }
  99. catch (Exception e)
  100. {
  101. Console.WriteLine(e.Message);
  102. }
  103. return redis;
  104. }
  105. }
  106. public class RequestDto
  107. {
  108. public string Id { get; set; }
  109. public DateTime SuccessDate { get; set; }
  110. public string Message { get; set; }
  111. }

第三步,切换到redis客户端,然后设置库存 set sk:1:qt 50

第四部,用压力测试工具或者自己写一个200个任务的请求这个接口,看看库存扣减是否正常。提供一个多线程实例,代码如下:

  1. using Newtonsoft.Json;
  2. using System;
  3. using System.Net.Http;
  4. using System.Threading.Tasks;
  5. namespace HttpClient请求
  6. {
  7. internal class Program
  8. {
  9. static void Main(string[] args)
  10. {
  11. try
  12. {
  13. for (int i = 0; i <40; i++)
  14. {
  15. Task task = Task.Run(() =>
  16. {
  17. Console.WriteLine($"创建线程成功,当前线程号:{Task.CurrentId}");
  18. SendRequest(Task.CurrentId.ToString());
  19. });
  20. }
  21. Console.ReadKey();
  22. }
  23. catch (Exception e)
  24. {
  25. Console.WriteLine(e);
  26. }
  27. }
  28. static void SendRequest(string threadId)
  29. {
  30. try
  31. {
  32. var url = $"https://localhost:5001/api/values/doKill?threadId="+ threadId;
  33. using (var client = new HttpClient())
  34. {
  35. var content = client.GetStringAsync(url).Result;
  36. //var data = JsonConvert.DeserializeObject<dynamic>(content);
  37. Console.WriteLine($"线程号:{threadId}:{content}");
  38. }
  39. }
  40. catch (Exception e)
  41. {
  42. Console.WriteLine(e);
  43. }
  44. }
  45. }
  46. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/84270
推荐阅读
相关标签
  

闽ICP备14008679号