c# .NET 高级编程 高并发必备技巧(二) - 分布式锁
阅读原文时间:2023年08月24日阅读:6

上一篇文章简单的介绍了单机的情况下如何进行加锁,防止高并发带来的问题。

然而现实中,一般会高并发的应用,很少会单机部署。当用户量达到一定的程度,分布式、集群部署是必然的选择。在分布式部署的情况下,之前的单机锁还会有效吗?代码还是之前的代码:

    private static object lck = new object();
    /// <summary>
    /// 单机锁
    /// </summary>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    [HttpGet]
    public  int Reduce1()
    {
        lock(lck)
        {
            int r = 0;
            string key = "stock";
            string stock = Rds.cli.Get(key);
            r = int.Parse(stock);
            if (r > 0)
            {
                r--;
                Rds.cli.Set(key, r);
            }
            else
            {
                throw new Exception("库存用尽!");
            }
            return r;
        }
    }

今天再来测试一下,首先在本机模拟分布式的部署。

api 部署3个,分别对应的端口 1020、1021、1022。使用nginx进行负载均衡转发,Nginx简单配置信息如下:

Jmeter请求的接口是nginx的8000,请求线程和上一次一样100*10

1000次请求后,再去查库存,发现库存并不为0。所以单机锁,在分布式的情况下,根本没起作用。

所以在分布式的情况下,必须要借助第三方的中间件。Redis是其中比较常见的解决方案,以下是简单的实现代码:

    /// <summary>
    /// 分布式锁
    /// </summary>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    [HttpGet]
    public int Reduce2()
    {
        bool Lck1 =false;
        int r = 0;
        string identity=Guid.NewGuid().ToString(); //设置识别,避免错误释放锁。
        int OverTime = 10; //根据实际业务场景设置 超时时间,避免出现死锁
        try
        {
            Lck1 = Rds.cli.SetNx("lock", identity, OverTime);
            while (!Lck1)
            {
                Lck1 = Rds.cli.SetNx("lock", identity, OverTime);
            }
            string key = "stock";
            string stock = Rds.cli.Get(key);
            r = int.Parse(stock);
            if (r > 0)
            {
                r--;
                Rds.cli.Set(key, r);
            }
            else
            {
                throw new Exception("库存用尽!");
            }
        }
        catch (Exception ex)
        {
            throw;
        }
        finally
        {
            string id = Rds.cli.Get("lock");
            if(id==identity)
            {
                Rds.cli.Del("lock");
            }
        }
        return r;
    }

再次通过Jmeter 请求nginx 的端口。经过改造后的代码方法,在1000次请求后,库存已经为0。说明此次的分布式锁是有效的。

需要注意的是,要避免死锁,所以加锁的时候,要根据业务场景 设置 过期时间。

为了避免释放错误,加锁的时候也要加上身份认证。

好了此次关于锁的分享完毕。