מערכות מבוזרות קיימות כבר זמן מה וישנם דפוסים מוכרים שכבר הוקמו בעיצובן. היום, נדון באחד מהדפוסים הפופולריים: "נעילות."
פשוט לומר, נעילות הן הדרך שבה תהליכים מקבלים גישה בלעדית למשאב כדי לבצע פעולה מסוימת. לדוגמה, דמיינו שיש כמה בלובים בחשבון אחסון, ואתם צריכים ש-instance אחד של השירות שלכם יטפל בכל בלוב כדי למנוע עיבוד כפול. הדרך לעשות זאת תהיה לרכוש נעילה על הבלוב, להשלים את העיבוד, ולשחרר אותה. עם זאת, בעיה פוטנציאלית מתעוררת אם תהליך נכשל לפני שחרור הנעילה, בין אם כי התהליך מת או בגלל פיצול ברשת, מה שמשאיר את המשאב נעול לנצח. זה יכול להוביל לנעילות מתות ותחרות על משאבים.
כדי למנוע נעילות מתות, אסטרטגיה אחת שניתן להפעיל היא להשתמש בזמני תום או נעילות מבוססות שכירות.
נעילת תום
- במקרה זה, ישנו תום מוגדר מראש שהתהליך מבקש את הנעילה עבורו. אם הנעילה לא משתחררת לפני תום הזמן, המערכת מבטיחה שהנעילה תשוחרר בסופו של דבר.
נעילת שכירות
-
לנעילות המבוססות על ליסינג, ניתן למצוא ממשק חידוש ליסינג בנוסף למנגנון הזמן המוגבל. התהליך ששומר על הנעילה חייב לקרוא לממשק זה לפני שהליסינג פג תוקף כדי לשמור על גישה בלעדית למשאב. אם התהליך נכשל בחידוש הליסינג בזמן, הנעילה תשוחרר באופן אוטומטי, מאפשר לתהליכים אחרים לרכוש אותה.
יתרונות וחסרונות של נעילות המבוססות על זמן וליסינג
Pros | Cons | |
---|---|---|
נעילה המבוססת על זמן | פשוט ליישום | דורש בחירה זהירה של הזמן המוגבל |
מונע נעילות קבועות | אם העיבוד אינו שלם, אין דרך לחידוש הליסינג | |
נעילת ליסינג | מוריד את הסיכון של פג תוקף מוקדם | דורש מנגנון לחידוש הליסינג |
התהליך יכול להמשיך לבקש את הליסינג עד שהעבודה תושלם. |
שתי האסטרטגיות הנ"ל הן דרך לשחזור מהיר מכשלי תהליכים או מפצלי רשתות במערכות מבוזרות.
אסטרטגיית Lease Lock עם אחסון Azure Storage
בואו נסתכל על איך להשתמש באסטרטגיית Lease Lock עם אחסון Azure Storage. זה גם מכסה את אסטרטגיית נעילת Timeout.
שלב 1: יבא את Nuget של האחסון Blob
"12.23.0" היא הגרסה העדכנית ביותר בזמן כתיבת המאמר הזה. הגרסאות העדכניות ניתן למצוא ב-אחסון Azure Blobs.
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.23.0" />
</ItemGroup>
שלב 2: רכוש את ה- Lease
להלן הקוד לרכישת הליז.
public async Task<string> TryAcquireLeaseAsync(string blobName, TimeSpan durationInSeconds, string leaseId = default)
{
BlobContainerClient blobContainerClient = new BlobContainerClient(new Uri($"https://{storageName}.blob.core.windows.net/processors"), tokenCredential, blobClientOptions);
BlobLeaseClient blobLeaseClient = blobContainerClient.GetBlobClient(blobName).GetBlobLeaseClient(leaseId);
try
{
BlobLease lease = await blobLeaseClient.AcquireAsync(durationInSeconds).ConfigureAwait(false);
return lease.LeaseId;
}
catch (RequestFailedException ex) when (ex.Status == 409)
{
return default;
}
}
- ראשית, אנו יוצרים לקוח קונטיינר Blob ומשיגים את לקוח ה-Blob עבור הBlob המסוים שברצוננו לרכוש ליז עליו.
- שנית, השיטה "Acquire Async" מנסה לרכוש את הליז למשך זמן מסוים. אם הרכישה הייתה מוצלחת, מוחזר זיהוי ליז, אחרת יושלך 409 (קוד סטטוס עבור התנגשות).
- "Acquire Async" היא השיטה המרכזית כאן. שאר הקוד ניתן להתאמה/עריכה על פי הצרכים שלך.
שלב 3: שדרג את הליז
- "Renew Async" היא השיטה ב-SDK של האחסון .NET המשמשת לחידוש הליז.
- אם החידוש אינו מוצלח, ישוגר חריגה יחד עם הסיבה לכשלון.
public async Task ReleaseLeaseAsync(string blobName, string leaseId)
{
BlobLeaseClient blobLeaseClient = this.blobContainerClient.GetBlobClient(blobName).GetBlobLeaseClient(leaseId);
await blobLeaseClient.RenewAsync().ConfigureAwait(false);
}
שלב 4: אורכסטרציה של שיטות הרכישה והתחדוש של הליז
- בהתחלה, אנו קוראים ל"ניסיון לרכוש השכרה אסינכרונית" כדי לקבל את מזהה ההשכרה משלב 2. ברגע שזה מצליח, מופעל משימה ברקע שמתקשרת ל"חדש השכרה אסינכרונית" משלב 3 כל X שניות. רק שימו לב שיש מספיק זמן בין הזמן המוגדר מראש לבין הזמן שבו השיטה לחדש השכרה נקראת.
string leaseId = await this.blobReadProcessor.TryAcquireLeaseAsync(blobName, TimeSpan.FromSeconds(60)).ConfigureAwait(false);
Task leaseRenwerTask = this.taskFactory.StartNew(
async () =>
{
while (leaseId != default && !cancellationToken.IsCancellationRequested)
{
await Task.Delay(renewLeaseMillis).ConfigureAwait(false);
await this.blobReadProcessor.RenewLeaseAsync(blobName, leaseId).ConfigureAwait(false);
}
},
CancellationToken.None,
TaskCreationOptions.LongRunning,
TaskScheduler.Default);
- האסימון הביטול משמש כדי להפסיק בצורה מסודרת את משימת חידוש ההשכרה כאשר היא כבר לא נדרשת.
שלב 5: לבטל את חידוש ההשכרה
- כאשר השיטה "לבטל אסינכרונית" נקראת, ה"דרישה לביטול" בשלב 4 הופכת לאמיתית, ולכן אנו כבר לא נכנסים ללולאת while ומבקשים חידוש השכרה.
await cancellationTokenSource.CancelAsync().ConfigureAwait(false);
await leaseRenwerTask.WaitAsync(Timeout.InfiniteTimeSpan).ConfigureAwait(false);
שלב 6: לשחרר את ההשכרה
לבסוף, כדי לשחרר את ההשכרה פשוט התקשרו לשיטה "שחרר אסינכרונית".
public async Task ReleaseLeaseAsync(string blobName, string leaseId)
{
BlobLeaseClient blobLeaseClient = this.blobContainerClient.GetBlobClient(blobName).GetBlobLeaseClient(leaseId);
await blobLeaseClient.ReleaseAsync().ConfigureAwait(false);
}
סיכום
נעילות הן בין הדפוסים הבסיסיים במערכות מבוזרות כדי להשיג גישה בלעדית למשאבים. יש לשמור על המלכודות בראש בזמן ההתמודדות איתן כדי להבטיח את הפעלת הפעולות בצורה חלקה. באמצעות אחסון אזורי, אנו יכולים ליישם את מנגנוני הנעילה היעילים הללו שיכולים למנוע חסימות בלתי מוגבלות, ובו בזמן, לספק גמישות בדרך שבה הנעילות נשמרות.
Source:
https://dzone.com/articles/locks-in-distributed-systems-timeout-lease-based