当前位置 主页 > 网站技术 > 代码类 >

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

    栏目:代码类 时间:2019-10-02 16:07

    使用场景描述:

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

    其他需求描述:

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

    技术实现:

      对请求的业务内容进行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 true;    }    /// <summary>    /// 尝试获取最后一个对象    /// </summary>    /// <param name="result"></param>    /// <returns></returns>    public bool TryGetTail(out T result)    {      result = default(T);      if (_tail == null)      {        return false;      }      result = _tail.Item;      return true;    }  }}