This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

分布式锁

分布式锁为应用程序提供对共享资源的互斥访问。

1 - 分布式锁概述

分布式锁 API 构建块概述

简介

锁用于提供对资源的互斥访问。例如,您可以使用锁来:

  • 提供对数据库行、表或整个数据库的独占访问
  • 以顺序方式锁定从队列读取消息

任何发生更新的共享资源都可以成为锁的目标。锁通常用于改变状态的操作,而不是读取操作。

每个锁都有一个名称。应用程序决定命名锁访问的资源。通常,同一应用程序的多个实例使用此命名锁来独占访问资源并执行更新。

例如,在竞争消费者模式中,应用程序的多个实例访问一个队列。您可以决定在应用程序运行其业务逻辑时锁定队列。

在下图中,同一应用程序 App1 的两个实例使用 Redis 锁组件 对共享资源进行加锁。

  • 第一个应用实例获取命名锁并获得独占访问权限。
  • 第二个应用实例无法获取锁,因此不允许访问资源,直到锁被释放,要么:
    • 通过 unlock API 由应用程序显式释放,或
    • 由于租约超时,在一段时间后自动释放。

*此 API 目前处于 Alpha 状态。

功能

对资源的互斥访问

在任何给定时刻,只有一个应用程序实例可以持有命名锁。锁的作用域限定为 Dapr app-id。

使用租约避免死锁

Dapr 分布式锁使用基于租约的锁定机制。如果应用程序获取锁后遇到异常而无法释放锁,则锁会在一段时间后通过租约自动释放。这可以防止应用程序失败时的资源死锁。

演示

观看此视频了解分布式锁 API 的概述

后续步骤

遵循以下指南:

2 - 操作指南:使用锁

了解如何使用分布式锁为资源提供独占访问

现在你已经了解了 Dapr 分布式锁 API 构建块所提供的功能,接下来学习它如何在你的服务中工作。在本指南中,一个示例应用程序使用 Redis 锁组件获取锁,以演示如何锁定资源。有关支持的锁存储列表,请参阅此参考页面

在下图中,同一应用程序的两个实例尝试获取锁,其中一个实例成功,另一个被拒绝。

下图显示同一应用程序的两个实例尝试获取锁,其中一个实例成功,另一个被拒绝

下图显示同一应用程序的两个实例,其中一个实例释放锁后,另一个实例能够获取该锁。

显示从同一应用程序的多个实例释放锁的示意图

下图显示两个不同应用程序的实例,在同一资源上获取不同的锁。

下图显示两个不同应用程序的实例,在同一资源上获取不同的锁

配置锁组件

将以下组件文件保存到你机器上的默认组件文件夹中。

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: lockstore
spec:
  type: lock.redis
  version: v1
  metadata:
  - name: redisHost
    value: localhost:6379
  - name: redisPassword
    value: <PASSWORD>

获取锁




curl -X POST http://localhost:3500/v1.0-alpha1/lock/lockstore
   -H 'Content-Type: application/json'
   -d '{"resourceId":"my_file_name", "lockOwner":"random_id_abc123", "expiryInSeconds": 60}'
using System;
using Dapr.Client;

namespace LockService
{
    class Program
    {
        [Obsolete("Distributed Lock API is in Alpha, this can be removed once it is stable.")]
        static async Task Main(string[] args)
        {
            string DAPR_LOCK_NAME = "lockstore";
            string fileName = "my_file_name";
            var client = new DaprClientBuilder().Build();
    
            await using (var fileLock = await client.Lock(DAPR_LOCK_NAME, fileName, "random_id_abc123", 60))
            {
                if (fileLock.Success)
                {
                    Console.WriteLine("Success");
                }
                else
                {
                    Console.WriteLine($"Failed to lock {fileName}.");
                }
            }
        }
    }
}
package main

import (
    "fmt"

    dapr "github.com/dapr/go-sdk/client"
)

func main() {
    client, err := dapr.NewClient()
    if err != nil {
        panic(err)
    }
    defer client.Close()
    
    resp, err := client.TryLockAlpha1(ctx, "lockstore", &dapr.LockRequest{
			LockOwner:         "random_id_abc123",
			ResourceID:      "my_file_name",
			ExpiryInSeconds: 60,
		})

    fmt.Println(resp.Success)
}

解锁现有锁




curl -X POST http://localhost:3500/v1.0-alpha1/unlock/lockstore
   -H 'Content-Type: application/json'
   -d '{"resourceId":"my_file_name", "lockOwner":"random_id_abc123"}'
using System;
using Dapr.Client;

namespace LockService
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string DAPR_LOCK_NAME = "lockstore";
            var client = new DaprClientBuilder().Build();

            var response = await client.Unlock(DAPR_LOCK_NAME, "my_file_name", "random_id_abc123"));
            Console.WriteLine(response.status);
        }
    }
}
package main

import (
    "fmt"

    dapr "github.com/dapr/go-sdk/client"
)

func main() {
    client, err := dapr.NewClient()
    if err != nil {
        panic(err)
    }
    defer client.Close()
    
    resp, err := client.UnlockAlpha1(ctx, "lockstore", &UnlockRequest{
			LockOwner:    "random_id_abc123",
			ResourceID: "my_file_name",
		})

    fmt.Println(resp.Status)
}

下一步

阅读分布式锁 API 概述了解更多内容。