.NetCore+Jexus代理+Redis模拟秒杀商品活动

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 开篇叙 本篇将和大家分享一下秒杀商品活动架构,采用的架构方案正如标题名称.NetCore+Jexus代理+Redis,由于精力有限所以这里只设计到商品添加,抢购,订单查询,处理队列抢购订单的功能;有不足或者不够详细的还请见谅,顺手点个推荐也不错; a.

开篇叙

本篇将和大家分享一下秒杀商品活动架构,采用的架构方案正如标题名称.NetCore+Jexus代理+Redis,由于精力有限所以这里只设计到商品添加,抢购,订单查询,处理队列抢购订单的功能;有不足或者不够详细的还请见谅,顺手点个推荐也不错;

a. 秒杀流程

b. 封装StackExchange.Redis的使用类

c. Ubuntu16.04上使用Jexus搭建代理完成分布式部署

d. NetCore写实时监控队列服务

秒杀架构设计图︿( ̄︶ ̄)︿三幅

1. 一般业务性架构

 

2. 后端分布式架构

 

3. 整站分布式

 

项目工程结构描述

a. 该项目git开源地址: https://github.com/shenniubuxing3/SeckillPro ,线上效果地址: http://www.lovexins.com:3333/

b. SeckillPro.Web:面向用户的web站点,主要提供商品展示,秒杀抢购,抢购结果,订单列表等功能;

c. SeckillPro.Api:主要处理秒杀活动的请求,然后加入到秒杀队列中,以及订单状态的查询接口;

d. SeckillPro.Server:处理秒杀队列的服务;根据Redis模糊匹配key的方式,开启多个商品秒杀的任务,并处理秒杀请求和改变订单抢购状态;

e. SeckillPro.Com:集成公共的方法;这里面前有操作Redis的list,hash,string的封装类;

SeckillPro.Web商品后台管理

对于商品活动来说,商品维护是必不可少的,由于这里商品维护的信息比较少,并且这里只加入到了RedisDb中,所以就不直接上代码了;一个列表,一个添加仅此而已;这里就不再贴代码了,如果你感兴趣可以去我的git上面看源码: https://github.com/shenniubuxing3/SeckillPro/blob/master/SeckillPro/SeckillPro.Web/Controllers/HomeController.cs 

SeckillPro.Web用户端商品列表+秒杀请求+用户订单列表

商品列表和订单列表没有可以太多说的,一般订单系统都有这两个列表;关键点在于订单秒杀流程中,咋们来简单分析下面向客户秒杀的流程需要注意的事项:

a. 限制秒杀开始时间和结束时间(测试未限制)

b. 未开始活动限制提交按钮不可点(测试未限制)

c. 获取真实剩余库存限制秒杀提交(获取redis中商品hash存储的真实剩余量)

d. 把客户的秒杀请求转移到另外的api集群,以此提高面向客户端的web站点并发承载率(测试项目中我直接指定4545端口的api测试)

这里就不再贴代码了,如果你感兴趣可以去我的git上面看看这部分源码: https://github.com/shenniubuxing3/SeckillPro/blob/master/SeckillPro/SeckillPro.Web/Controllers/HomeController.cs 

.NetCore写处理秒杀活动队列的服务

这个处理队列服务处理流程:模糊匹配Redis中每种商品的队列key-》开启不同商品的处理队列任务-》处理秒杀订单-》更新库存和秒杀订单状态;

a. 模糊匹配Redis中每种商品的队列key:这里采用的是StackExchange.Redis中指定redis原生命令的方法来获取匹配队列key,设计的代码如下:

 1 /// <summary>
 2         /// 模糊匹配redis中的key
 3         /// </summary>
 4         /// <param name="paramArr"></param>
 5         /// <returns></returns>
 6         public async Task<List<string>> MatchKeys(params string[] paramArr)
 7         {
 8             var list = new List<string>();
 9             try
10             {
11                 var result = await this.ExecuteAsync("keys", paramArr);
12 
13                 var valArr = ((RedisValue[])result);
14                 foreach (var item in valArr)
15                 {
16                     list.Add(item);
17                 }
18             }
19             catch (Exception ex) { }
20             return list;
21         }
22 
23         /// <summary>
24         /// 执行redis原生命令
25         /// </summary>
26         /// <param name="cmd"></param>
27         /// <param name="paramArr"></param>
28         /// <returns></returns>
29         public async Task<RedisResult> ExecuteAsync(string cmd, params string[] paramArr)
30         {
31             try
32             {
33                 var db = this.GetDb();
34                 return await db.ExecuteAsync(cmd, paramArr);
35             }
36             catch (Exception ex) { }
37             return default(RedisResult);
38         }

b. 开启不同商品的处理队列任务:通过Task.Factory.StartNew(action,object)方法开启不同商品的处理秒杀订单的任务;

c. 更新库存和秒杀订单状态:由于抢购商品要求库存剩余实时性,所以每处理一个抢购订单,需要对该商品减去相应的库存和修改秒杀订单的状态方便用户查看秒杀结果;

d. 处理队列具体的实现代码可以去git看下,个人觉得还是有用的:https://github.com/shenniubuxing3/SeckillPro/blob/master/SeckillPro/SeckillPro.Server/Program.cs

使用Jexus代理部署分布式站点和接口

这里部署的代理采用的是Jexus代理;作为在linux和unix上部署.net程序实用的工具,真的很感谢jexus作者;首先本篇讲解的部署环境是ubunt16.04x64(至于这么安装jexus可以参考上一篇分享文章),为了更直观的看出来效果我在服务器上拷贝了两份SeckillPro.Web发布的站点,他们代码都是一样的只是分别把_Layout.cshtml试图模板中加入了端口7777和8888,我就用这两个端口来测试jexus的代理效果;

测试方便直接分别在两个复制站点中执行如下终端命令:dotnet SeckillPro.Web.dll http://ip:端口 ;一个监听7777端口一个监听8888;执行命令效果图:

监听7777和8888端口成功后,我们就可以直接在浏览器输入:http://172.16.9.66:7777 访问,正常情况下能够看到如下图示例:

单个站点访问没问题了,下面开始配置jexus代理;只需要在jexus/siteconf的配置文件中(我这里是default配置文件),增加如下设置:

注意reproxy参数:

a. 第一个/表示根目录,一般不变

b. 多个被代理地址使用‘,’隔开;

c. 被代理地址后面也同样需要加/

此时我们配置完后,只需要启动jexus就行了:./jws start (怎么启动可以参考上一篇文章);当启动jws成功后,我们就能通过配置的80端口,来访问SeckillPro.Web站点了,效果图:

至于代理分发的策略暂不在本章的讨论范围内,如果可以建议去jexus官网了解下;同样对于Seckill.Api我们也可以这样部署,这里部署了个秒杀线上地址,有兴趣的朋友可以点击试试:http://www.lovexins.com:3333/ (注:这里没有使用代理)

封装StackExchange.Redis的使用类StackRedis.cs

其实这个在之前已经分享过了,只不过只有操作string和list的分装;本篇测试涉及到订单查询和商品查询等功能,所以这里我又扩展了对hash的操作方法,可以说更丰富了吧,如果您正打算使用redis或许直接用我这个封装类是个不错的打算;

  1 public class StackRedis : IDisposable
  2     {
  3         #region 配置属性   基于 StackExchange.Redis 封装
  4         //连接串 (注:IP:端口,属性=,属性=)
  5         public string _ConnectionString = "127.0.0.1:6377,password=shenniubuxing3";
  6         //操作的库(注:默认0库)
  7         public int _Db = 0;
  8         #endregion
  9 
 10         #region 管理器对象
 11 
 12         /// <summary>
 13         /// 获取redis操作类对象
 14         /// </summary>
 15         private static StackRedis _StackRedis;
 16         private static object _locker_StackRedis = new object();
 17         public static StackRedis Current
 18         {
 19             get
 20             {
 21                 if (_StackRedis == null)
 22                 {
 23                     lock (_locker_StackRedis)
 24                     {
 25                         _StackRedis = _StackRedis ?? new StackRedis();
 26                         return _StackRedis;
 27                     }
 28                 }
 29 
 30                 return _StackRedis;
 31             }
 32         }
 33 
 34         /// <summary>
 35         /// 获取并发链接管理器对象
 36         /// </summary>
 37         private static ConnectionMultiplexer _redis;
 38         private static object _locker = new object();
 39         public ConnectionMultiplexer Manager
 40         {
 41             get
 42             {
 43                 if (_redis == null)
 44                 {
 45                     lock (_locker)
 46                     {
 47                         _redis = _redis ?? GetManager(this._ConnectionString);
 48                         return _redis;
 49                     }
 50                 }
 51 
 52                 return _redis;
 53             }
 54         }
 55 
 56         /// <summary>
 57         /// 获取链接管理器
 58         /// </summary>
 59         /// <param name="connectionString"></param>
 60         /// <returns></returns>
 61         public ConnectionMultiplexer GetManager(string connectionString)
 62         {
 63             return ConnectionMultiplexer.Connect(connectionString);
 64         }
 65 
 66         /// <summary>
 67         /// 获取操作数据库对象
 68         /// </summary>
 69         /// <returns></returns>
 70         public IDatabase GetDb()
 71         {
 72             return Manager.GetDatabase(_Db);
 73         }
 74         #endregion
 75 
 76         #region 操作方法
 77 
 78         #region string 操作
 79 
 80         /// <summary>
 81         /// 根据Key移除
 82         /// </summary>
 83         /// <param name="key"></param>
 84         /// <returns></returns>
 85         public async Task<bool> Remove(string key)
 86         {
 87             var db = this.GetDb();
 88 
 89             return await db.KeyDeleteAsync(key);
 90         }
 91 
 92         /// <summary>
 93         /// 根据key获取string结果
 94         /// </summary>
 95         /// <param name="key"></param>
 96         /// <returns></returns>
 97         public async Task<string> Get(string key)
 98         {
 99             var db = this.GetDb();
100             return await db.StringGetAsync(key);
101         }
102 
103         /// <summary>
104         /// 根据key获取string中的对象
105         /// </summary>
106         /// <typeparam name="T"></typeparam>
107         /// <param name="key"></param>
108         /// <returns></returns>
109         public async Task<T> Get<T>(string key)
110         {
111             var t = default(T);
112             try
113             {
114                 var _str = await this.Get(key);
115                 if (string.IsNullOrWhiteSpace(_str)) { return t; }
116 
117                 t = JsonConvert.DeserializeObject<T>(_str);
118             }
119             catch (Exception ex) { }
120             return t;
121         }
122 
123         /// <summary>
124         /// 存储string数据
125         /// </summary>
126         /// <param name="key"></param>
127         /// <param name="value"></param>
128         /// <param name="expireMinutes"></param>
129         /// <returns></returns>
130         public async Task<bool> Set(string key, string value, int expireMinutes = 0)
131         {
132             var db = this.GetDb();
133             if (expireMinutes > 0)
134             {
135                 return db.StringSet(key, value, TimeSpan.FromMinutes(expireMinutes));
136             }
137             return await db.StringSetAsync(key, value);
138         }
139 
140         /// <summary>
141         /// 存储对象数据到string
142         /// </summary>
143         /// <typeparam name="T"></typeparam>
144         /// <param name="key"></param>
145         /// <param name="value"></param>
146         /// <param name="expireMinutes"></param>
147         /// <returns></returns>
148         public async Task<bool> Set<T>(string key, T value, int expireMinutes = 0)
149         {
150             try
151             {
152                 var jsonOption = new JsonSerializerSettings()
153                 {
154                     ReferenceLoopHandling = ReferenceLoopHandling.Ignore
155                 };
156                 var _str = JsonConvert.SerializeObject(value, jsonOption);
157                 if (string.IsNullOrWhiteSpace(_str)) { return false; }
158 
159                 return await this.Set(key, _str, expireMinutes);
160             }
161             catch (Exception ex) { }
162             return false;
163         }
164 
165         /// <summary>
166         /// 是否存在key
167         /// </summary>
168         /// <typeparam name="T"></typeparam>
169         /// <param name="key"></param>
170         /// <returns></returns>
171         public async Task<bool> KeyExists(string key)
172         {
173             try
174             {
175                 var db = this.GetDb();
176                 return await db.KeyExistsAsync(key);
177             }
178             catch (Exception ex) { }
179             return false;
180         }
181 
182         #endregion
183 
184         #region hash操作
185 
186         /// <summary>
187         /// 是否存在hash的列
188         /// </summary>
189         /// <param name="key"></param>
190         /// <param name="filedKey"></param>
191         /// <returns></returns>
192         public async Task<bool> HashFieldExists(string key, string filedKey)
193         {
194             try
195             {
196                 if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(filedKey)) { return false; }
197 
198                 var result = await this.HashFieldsExists(key, new Dictionary<string, bool> { { filedKey, false } });
199                 return result[filedKey];
200             }
201             catch (Exception ex) { }
202             return false;
203         }
204 
205         /// <summary>
206         /// 是否存在hash的列集合
207         /// </summary>
208         /// <param name="key"></param>
209         /// <param name="dics"></param>
210         /// <returns></returns>
211         public async Task<Dictionary<string, bool>> HashFieldsExists(string key, Dictionary<string, bool> dics)
212         {
213             try
214             {
215                 if (dics.Count <= 0) { return dics; }
216 
217                 var db = this.GetDb();
218                 foreach (var fieldKey in dics.Keys)
219                 {
220                     dics[fieldKey] = await db.HashExistsAsync(key, fieldKey);
221                 }
222             }
223             catch (Exception ex) { }
224             return dics;
225         }
226 
227         /// <summary>
228         /// 设置hash
229         /// </summary>
230         /// <typeparam name="T"></typeparam>
231         /// <param name="key"></param>
232         /// <param name="filedKey"></param>
233         /// <param name="t"></param>
234         /// <returns></returns>
235         public async Task<long> SetOrUpdateHashsField<T>(string key, string filedKey, T t, bool isAdd = true)
236         {
237             var result = 0L;
238             try
239             {
240                 return await this.SetOrUpdateHashsFields<T>(key, new Dictionary<string, T> { { filedKey, t } }, isAdd);
241             }
242             catch (Exception ex) { }
243             return result;
244         }
245 
246         /// <summary>
247         /// 设置hash集合,添加和更新操作
248         /// </summary>
249         /// <typeparam name="T"></typeparam>
250         /// <param name="key"></param>
251         /// <param name="dics"></param>
252         /// <returns></returns>
253         public async Task<long> SetOrUpdateHashsFields<T>(string key, Dictionary<string, T> dics, bool isAdd = true)
254         {
255             var result = 0L;
256             try
257             {
258                 var jsonOption = new JsonSerializerSettings()
259                 {
260                     ReferenceLoopHandling = ReferenceLoopHandling.Ignore
261                 };
262                 var db = this.GetDb();
263                 foreach (var fieldKey in dics.Keys)
264                 {
265                     var item = dics[fieldKey];
266                     var _str = JsonConvert.SerializeObject(item, jsonOption);
267                     result += await db.HashSetAsync(key, fieldKey, _str) ? 1 : 0;
268                     if (!isAdd) { result++; }
269                 }
270                 return result;
271             }
272             catch (Exception ex) { }
273             return result;
274         }
275 
276         /// <summary>
277         /// 移除hash的列
278         /// </summary>
279         /// <param name="key"></param>
280         /// <param name="filedKey"></param>
281         /// <returns></returns>
282         public async Task<bool> RemoveHashField(string key, string filedKey)
283         {
284             try
285             {
286                 if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(filedKey)) { return false; }
287 
288                 var result = await this.RemoveHashFields(key, new Dictionary<string, bool> { { filedKey, false } });
289                 return result[filedKey];
290             }
291             catch (Exception ex) { }
292             return false;
293         }
294 
295         /// <summary>
296         /// 异常hash的列集合
297         /// </summary>
298         /// <param name="key"></param>
299         /// <param name="dics"></param>
300         /// <returns></returns>
301         public async Task<Dictionary<string, bool>> RemoveHashFields(string key, Dictionary<string, bool> dics)
302         {
303 
304             try
305             {
306                 var jsonOption = new JsonSerializerSettings()
307                 {
308                     ReferenceLoopHandling = ReferenceLoopHandling.Ignore
309                 };
310                 var db = this.GetDb();
311                 foreach (var fieldKey in dics.Keys)
312                 {
313                     dics[fieldKey] = await db.HashDeleteAsync(key, fieldKey);
314                 }
315                 return dics;
316             }
317             catch (Exception ex) { }
318             return dics;
319         }
320 
321         /// <summary>
322         /// 设置hash
323         /// </summary>
324         /// <typeparam name="T"></typeparam>
325         /// <param name="key"></param>
326         /// <param name="filedKey"></param>
327         /// <param name="t"></param>
328         /// <returns></returns>
329         public async Task<T> GetHashField<T>(string key, string filedKey)
330         {
331             var t = default(T);
332             try
333             {
334                 var dics = await this.GetHashFields<T>(key, new Dictionary<string, T> { { filedKey, t } });
335                 return dics[filedKey];
336             }
337             catch (Exception ex) { }
338             return t;
339         }
340 
341         /// <summary>
342         /// 获取hash的列值集合
343         /// </summary>
344         /// <typeparam name="T"></typeparam>
345         /// <param name="key"></param>
346         /// <param name="dics"></param>
347         /// <returns></returns>
348         public async Task<Dictionary<string, T>> GetHashFields<T>(string key, Dictionary<string, T> dics)
349         {
350             try
351             {
352                 var db = this.GetDb();
353                 foreach (var fieldKey in dics.Keys)
354                 {
355                     var str = await db.HashGetAsync(key, fieldKey);
356                     if (string.IsNullOrWhiteSpace(str)) { continue; }
357 
358                     dics[fieldKey] = JsonConvert.DeserializeObject<T>(str);
359                 }
360                 return dics;
361             }
362             catch (Exception ex) { }
363             return dics;
364         }
365 
366         /// <summary>
367         /// 获取hash的key的所有列的值
368         /// </summary>
369         /// <typeparam name="T"></typeparam>
370         /// <param name="key"></param>
371         /// <returns></returns>
372         public async Task<Dictionary<string, T>> GetHashs<T>(string key)
373         {
374             var dic = new Dictionary<string, T>();
375             try
376             {
377                 var db = this.GetDb();
378 
379                 var hashFiles = await db.HashGetAllAsync(key);
380                 foreach (var field in hashFiles)
381                 {
382                     dic[field.Name] = JsonConvert.DeserializeObject<T>(field.Value);
383                 }
384                 return dic;
385             }
386             catch (Exception ex) { }
387             return dic;
388         }
389 
390         /// <summary>
391         /// 获取hash的Key的所有列的值的list集合
392         /// </summary>
393         /// <typeparam name="T"></typeparam>
394         /// <param name="key"></param>
395         /// <returns></returns>
396         public async Task<List<T>> GetHashsToList<T>(string key)
397         {
398             var list = new List<T>();
399             try
400             {
401                 var db = this.GetDb();
402 
403                 var hashFiles = await db.HashGetAllAsync(key);
404                 foreach (var field in hashFiles)
405                 {
406                     var item = JsonConvert.DeserializeObject<T>(field.Value);
407                     if (item == null) { continue; }
408                     list.Add(item);
409                 }
410             }
411             catch (Exception ex) { }
412             return list;
413         }
414 
415         #endregion
416 
417         #region List操作(注:可以当做队列使用)
418 
419         /// <summary>
420         /// list长度
421         /// </summary>
422         /// <typeparam name="T"></typeparam>
423         /// <param name="key"></param>
424         /// <returns></returns>
425         public async Task<long> GetListLen<T>(string key)
426         {
427             try
428             {
429                 var db = this.GetDb();
430                 return await db.ListLengthAsync(key);
431             }
432             catch (Exception ex) { }
433             return 0;
434         }
435 
436         /// <summary>
437         /// 获取List数据
438         /// </summary>
439         /// <typeparam name="T"></typeparam>
440         /// <param name="key"></param>
441         /// <returns></returns>
442         public async Task<List<T>> GetList<T>(string key)
443         {
444             var t = new List<T>();
445             try
446             {
447                 var db = this.GetDb();
448                 var _values = await db.ListRangeAsync(key);
449                 foreach (var item in _values)
450                 {
451                     if (string.IsNullOrWhiteSpace(item)) { continue; }
452                     t.Add(JsonConvert.DeserializeObject<T>(item));
453                 }
454             }
455             catch (Exception ex) { }
456             return t;
457         }
458 
459         /// <summary>
460         /// 获取队列出口数据并移除
461         /// </summary>
462         /// <typeparam name="T"></typeparam>
463         /// <param name="key"></param>
464         /// <returns></returns>
465         public async Task<T> GetListAndPop<T>(string key)
466         {
467             var t = default(T);
468             try
469             {
470                 var db = this.GetDb();
471                 var _str = await db.ListRightPopAsync(key);
472                 if (string.IsNullOrWhiteSpace(_str)) { return t; }
473                 t = JsonConvert.DeserializeObject<T>(_str);
474             }
475             catch (Exception ex) { }
476             return t;
477         }
478 
479         /// <summary>
480         /// 集合对象添加到list左边
481         /// </summary>
482         /// <typeparam name="T"></typeparam>
483         /// <param name="key"></param>
484         /// <param name="values"></param>
485         /// <returns></returns>
486         public async Task<long> SetLists<T>(string key, List<T> values)
487         {
488             var result = 0L;
489             try
490             {
491                 var jsonOption = new JsonSerializerSettings()
492                 {
493                     ReferenceLoopHandling = ReferenceLoopHandling.Ignore
494                 };
495                 var db = this.GetDb();
496                 foreach (var item in values)
497                 {
498                     var _str = JsonConvert.SerializeObject(item, jsonOption);
499                     result += await db.ListLeftPushAsync(key, _str);
500                 }
501                 return result;
502             }
503             catch (Exception ex) { }
504             return result;
505         }
506 
507         /// <summary>
508         /// 单个对象添加到list左边
509         /// </summary>
510         /// <typeparam name="T"></typeparam>
511         /// <param name="key"></param>
512         /// <param name="value"></param>
513         /// <returns></returns>
514         public async Task<long> SetList<T>(string key, T value)
515         {
516             var result = 0L;
517             try
518             {
519                 result = await this.SetLists(key, new List<T> { value });
520             }
521             catch (Exception ex) { }
522             return result;
523         }
524 
525 
526         #endregion
527 
528         #region 额外扩展
529 
530         public async Task<List<string>> MatchKeys(params string[] paramArr)
531         {
532             var list = new List<string>();
533             try
534             {
535                 var result = await this.ExecuteAsync("keys", paramArr);
536 
537                 var valArr = ((RedisValue[])result);
538                 foreach (var item in valArr)
539                 {
540                     list.Add(item);
541                 }
542             }
543             catch (Exception ex) { }
544             return list;
545         }
546 
547         /// <summary>
548         /// 执行redis原生命令
549         /// </summary>
550         /// <param name="cmd"></param>
551         /// <param name="paramArr"></param>
552         /// <returns></returns>
553         public async Task<RedisResult> ExecuteAsync(string cmd, params string[] paramArr)
554         {
555             try
556             {
557                 var db = this.GetDb();
558                 return await db.ExecuteAsync(cmd, paramArr);
559             }
560             catch (Exception ex) { }
561             return default(RedisResult);
562         }
563 
564         /// <summary>
565         /// 手动回收管理器对象
566         /// </summary>
567         public void Dispose()
568         {
569             this.Dispose(_redis);
570         }
571 
572         public void Dispose(ConnectionMultiplexer con)
573         {
574             if (con != null)
575             {
576                 con.Close();
577                 con.Dispose();
578             }
579         }
580 
581         #endregion
582 
583         #endregion
584     }
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
存储 开发框架 JSON
ASP.NET Core OData 9 正式发布
【10月更文挑战第8天】Microsoft 在 2024 年 8 月 30 日宣布推出 ASP.NET Core OData 9,此版本与 .NET 8 的 OData 库保持一致,改进了数据编码以符合 OData 规范,并放弃了对旧版 .NET Framework 的支持,仅支持 .NET 8 及更高版本。新版本引入了更快的 JSON 编写器 `System.Text.UTF8JsonWriter`,优化了内存使用和序列化速度。
|
1月前
|
存储 消息中间件 NoSQL
Redis 入门 - C#.NET Core客户端库六种选择
Redis 入门 - C#.NET Core客户端库六种选择
60 8
|
2月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
2月前
|
开发框架 .NET 中间件
ASP.NET Core Web 开发浅谈
本文介绍ASP.NET Core,一个轻量级、开源的跨平台框架,专为构建高性能Web应用设计。通过简单步骤,你将学会创建首个Web应用。文章还深入探讨了路由配置、依赖注入及安全性配置等常见问题,并提供了实用示例代码以助于理解与避免错误,帮助开发者更好地掌握ASP.NET Core的核心概念。
99 3
|
1月前
|
开发框架 JavaScript 前端开发
一个适用于 ASP.NET Core 的轻量级插件框架
一个适用于 ASP.NET Core 的轻量级插件框架
|
2月前
|
开发框架 NoSQL .NET
利用分布式锁在ASP.NET Core中实现防抖
【9月更文挑战第5天】在 ASP.NET Core 中,可通过分布式锁实现防抖功能,仅处理连续相同请求中的首个请求,其余请求返回 204 No Content,直至锁释放。具体步骤包括:安装分布式锁库如 `StackExchange.Redis`;创建分布式锁服务接口及其实现;构建防抖中间件;并在 `Startup.cs` 中注册相关服务和中间件。这一机制有效避免了短时间内重复操作的问题。
|
3月前
|
缓存 NoSQL Java
【Azure Redis 缓存 Azure Cache For Redis】Redis出现 java.net.SocketTimeoutException: Read timed out 异常
【Azure Redis 缓存 Azure Cache For Redis】Redis出现 java.net.SocketTimeoutException: Read timed out 异常
|
3月前
|
缓存 NoSQL 网络协议
【Azure Redis 缓存】Redisson 连接 Azure Redis出现间歇性 java.net.UnknownHostException 异常
【Azure Redis 缓存】Redisson 连接 Azure Redis出现间歇性 java.net.UnknownHostException 异常
|
3月前
|
存储 NoSQL Redis
【Azure Developer】一个复制Redis Key到另一个Redis服务的工具(redis_copy_net8)
【Azure Developer】一个复制Redis Key到另一个Redis服务的工具(redis_copy_net8)
【Azure Developer】一个复制Redis Key到另一个Redis服务的工具(redis_copy_net8)
|
3月前
|
开发框架 监控 .NET
开发者的革新利器:ASP.NET Core实战指南,构建未来Web应用的高效之道
【8月更文挑战第28天】本文探讨了如何利用ASP.NET Core构建高效、可扩展的Web应用。ASP.NET Core是一个开源、跨平台的框架,具有依赖注入、配置管理等特性。文章详细介绍了项目结构规划、依赖注入配置、中间件使用及性能优化方法,并讨论了安全性、可扩展性以及容器化的重要性。通过这些技术要点,开发者能够快速构建出符合现代Web应用需求的应用程序。
58 0