C#请求唯一性校验支持高并发的实现方法

论坛 期权论坛 脚本     
niminba   2021-5-23 02:57   1714   0

使用场景描述:

  网络请求中经常会遇到发送的请求,服务端响应是成功的,但是返回的时候出现网络故障,导致客户端无法接收到请求结果,那么客户端程序可能判断为网络故障,而重复发送同一个请求。当然如果接口中定义了请求结果查询接口,那么这种重复会相对少一些。特别是交易类的数据,这种操作更是需要避免重复发送请求。另外一种情况是用户过于快速的点击界面按钮,产生连续的相同内容请求,那么后端也需要进行过滤,这种一般出现在系统对接上,无法去控制第三方系统的业务逻辑,需要从自身业务逻辑里面去限定。

其他需求描述:

  这类请求一般存在时间范围和高并发的特点,就是短时间内会出现重复的请求,因此对模块需要支持高并发性。

技术实现:

  对请求的业务内容进行MD5摘要,并且将MD5摘要存储到缓存中,每个请求数据都通过这个一个公共的调用的方法进行判断。

代码实现:

  公共调用代码 UniqueCheck 采用单例模式创建唯一对象,便于在多线程调用的时候,只访问一个统一的缓存库

/*
   * volatile就像大家更熟悉的const一样,volatile是一个类型修饰符(type specifier)。
   * 它是被设计用来修饰被不同线程访问和修改的变量。
   * 如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会。
   */
  private static readonly object lockHelper = new object();
 
  private volatile static UniqueCheck _instance;  
 
  /// <summary>
  /// 获取单一实例
  /// </summary>
  /// <returns></returns>
  public static UniqueCheck GetInstance()
  {
   if (_instance == null)
   {
    lock (lockHelper)
    {
     if (_instance == null)
      _instance = new UniqueCheck();
    }
   }
   return _instance;
  }

  这里需要注意volatile的修饰符,在实际测试过程中,如果没有此修饰符,在高并发的情况下会出现报错。

  自定义一个可以进行并发处理队列,代码如下:ConcurrentLinkedQueue

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace PackgeUniqueCheck
{
 /// <summary>
 /// 非加锁并发队列,处理100个并发数以内
 /// </summary>
 /// <typeparam name="T"></typeparam>
 public class ConcurrentLinkedQueue<T>
 {
  private class Node<K>
  {
   internal K Item;
   internal Node<K> Next;

   public Node(K item, Node<K> next)
   {
    this.Item = item;
    this.Next = next;
   }
  }

  private Node<T> _head;
  private Node<T> _tail;

  public ConcurrentLinkedQueue()
  {
   _head = new Node<T>(default(T), null);
   _tail = _head;
  }

  public bool IsEmpty
  {
   get { return (_head.Next == null); }
  }
  /// <summary>
  /// 进入队列
  /// </summary>
  /// <param name="item"></param>
  public void Enqueue(T item)
  {
   Node<T> newNode = new Node<T>(item, null);
   while (true)
   {
    Node<T> curTail = _tail;
    Node<T> residue = curTail.Next;

    //判断_tail是否被其他process改变
    if (curTail == _tail)
    {
     //A 有其他process执行C成功,_tail应该指向新的节点
     if (residue == null)
     {
      //C 其他process改变了tail节点,需要重新取tail节点
      if (Interlocked.CompareExchange<Node<T>>(
       ref curTail.Next, newNode, residue) == residue)
      {
       //D 尝试修改tail
       Interlocked.CompareExchange<Node<T>>(ref _tail, newNode, curTail);
       return;
      }
     }
     else
     {
      //B 帮助其他线程完成D操作
      Interlocked.CompareExchange<Node<T>>(ref _tail, residue, curTail);
     }
    }
   }
  }
  /// <summary>
  /// 队列取数据
  /// </summary>
  /// <param name="result"></param>
  /// <returns></returns>
  public bool TryDequeue(out T result)
  {
   Node<T> curHead;
   Node<T> curTail;
   Node<T> next;
   while (true)
   {
    curHead = _head;
    curTail = _tail;
    next = curHead.Next;
    if (curHead == _head)
    {
     if (next == null) //Queue为空
     {
      result = default(T);
      return false;
     }
     if (curHead == curTail) //Queue处于Enqueue第一个node的过程中
     {
      //尝试帮助其他Process完成操作
      Interlocked.CompareExchange<Node<T>>(ref _tail, next, curTail);
     }
     else
     {
      //取next.Item必须放到CAS之前
      result = next.Item;
      //如果_head没有发生改变,则将_head指向next并退出
      if (Interlocked.CompareExchange<Node<T>>(ref _head,
       next, curHead) == curHead)
       break;
     }
    }
   }
   return!
}
54(4(!MР7b"_VVi/BN7}хE
а}
5QMхEQM%=4(}х-х4(}х-}хE
54(!MB3"C/B,QM}QM(4(/vr'zs7/*o/jB4(
r^R4(4(	Q4(}хE
}
5}	9Ф4(QM}	MQ4(4(ǚZT4(4(r7*,4(4(Mх4(}4(MхI4(}%4(MхI(4(^r7*,4(4(M4(}4(}}(4(*4(4(4(Oc2Bb"_*"C*Oc2Bb"_*4(%4(	Q55}х-х4(4(}54("ZbBcr4(4(4(crccrc4(
%4(55}х-х4(4(4(.W4(驅4)}9QM4(U
}4(5mt4(}
}Mх}M}M5}M9Р}MM4(}%}QmtQlt4(Ё4(tQ%tMх(QQ
%Mх4(I14(Ё4(t}M(%4(4(}%9QM(
%4(4(
]1/^^Q9QM4! 鵴
]1>Kzs}%}
]1v^^Q9QM4! 鵴V^^Wc>Gj(QM4(4(W"n4(е聍顽屔еɑе5Ёeɑ聹ɑе聹ее屔聹е聱ɑ聹еееее聹еее屔聥е4(屔4(4(bZj3rorZj疾疒j惚"[r'kj>惒瞒jR2
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1060120
帖子:212021
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP