Back to "यु. पू."

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET Core BackgroundService Hangfire IHostedService

यु. पू.

Thursday, 27 November 2025

प्रत्येक आधुनिक वेब अनुप्रयोग में कार्य है जो कि एचटीटीपी निवेदन को ब्लॉक नहीं करना चाहिए - आगे बढ़ने वाली ई- मेल- मेल, प्रक्रिया, बाहरी सेवाओं के साथ सिंकिंग, निगरानी. एनईसी कोरल इस पृष्ठभूमि कार्य को चलाने के लिए अनेक निकट आता है, सरल से IHostedService यह पहली भाग में, हम मूल पैटर्नों की जाँच करेंगे और प्रत्येक का उपयोग करने के लिए जब.

परिचय

LATAN; मैं पृष्ठभूमि सेवा सेवा की तरह, इस साइट (एक विशाल साइट) में आधे से अधिक BOrowogowogods के ऊपर काम कर रहा है, लेकिन जैसे उनके पास कुछ ODEDS और अभ्यास है जो आप का उपयोग करने के लिए एक बहुत अच्छा बना देगा. पृष्ठभूमि सेवा आधुनिक वेब अनुप्रयोगों के अनंग नायक हैं. Wont आपके नियंत्रण HTTP अनुरोधों को अग्रभूमि में नियंत्रित करता है, पृष्ठभूमि सेवाओं को चुपचाप कतार में लागू करता है, खोज के लिए तालिका सामग्री, बाहरी एपीआई, स्वच्छ अस्थायी फ़ाइलों को साफ करने के लिए, और अन्य कई अन्य कार्य जो आपके अनुरोध को अन्यथा अवरोधित करता है.

इस दो भाग श्रृंखला में, हम भिन्न दृष्टिकोण का पता लगाने के लिए एक अनजाने में पृष्ठभूमि सेवाओं को लागू करने के लिए। IHostedService और BackgroundService हैंग आग की तरह अधिक जटिल समाधान के लिए एक शिकार. भाग 1 में हम बुनियादी दृष्टिकोण और उनकी विशेषताओं की जाँच करेंगे. पार्ट 2, हम एक उत्पादन कोड बेस से वास्तविक दुनिया कार्यान्वयन में कटौती करेंगे.

आयात हम जीवनभर प्रबंधन के लिए विशेष ध्यान दे देंगे - अकसर अप अप रूप से देखा गया StopAsync विधि, जहाँ कई विकासकर्ता प्रतिक्रियाओं का सामना करते हैं जब उनके अनुप्रयोगों को बंद कर दिया जाता है.

पृष्ठभूमि सेवा क्यों?

"कैसे" में बदलाव करने से पहले, चलो संक्षिप्त रूप से "क्यों." पृष्ठभूमि सेवाओं पर विचार करें आप:

  1. धीमा संचालन बन्द करें - जब तक आप ई- मेल भेजते हैं या PDF बनाते हैं, उपयोक्ता इंतजार नहीं करें
  2. बारंबार होने वाले कार्यों को समयबद्ध करें - दो रात में हर रात पुराने रिकॉर्ड साफ करें
  3. प्रक्रिया क़तार - चैनल या संदेश मालिक से हैंडल करें
  4. मॉनीटर बाहरी स्थिति - परिवर्तन के लिए पिन एपीआई या फ़ाइल सिस्टम देखें
  5. निर्देशांक जटिल कार्य प्रवाह - बहु- चरण प्रक्रियाओं का प्रबंधन करें जो कि कतार मिनट या घंटे

एनईएस.NT कोर इन सेवाओं को लागू करने के लिए कई पास प्रदान करता है, प्रत्येक के साथ अलग व्यापार-फ़ल.

ऐतिहासिक संदर्भ: क्यों पृष्ठभूमि सेवा अब अनिवार्य हैं

"पुराने दिनों" (प्रयोगात्मक-2010) में, जो कि आपके वेब अनुप्रयोग में पृष्ठभूमि कार्य करता है, आमतौर पर बुरा विचार माना जाता था. सामान्यत: वेब सर्वर निवेदन करता है. पृष्ठभूमि कार्य अलग सर्वर पर होता है.

यह सिर्फ माल की बुद्धि नहीं थी, यह असली तकनीकी सीमाओं पर आधारित था:

एकल- कोरेव

प्रारंभिक वेब सर्वर (और अब ईमानदारी से, 'पीप'फ़ॉर्म सेवाएँ) आम तौर पर चल रहे हैं एकल- चिप या दोहरा सीपीयू्स. यदि आप एक सीपीयू-इन्ड पृष्ठभूमि कार्य चला रहे हैं, तो यह सीधे ही वेब के लिए निवेदन करता है:

Single Core (2005):
┌─────────────────────┐
│  Background Task    │  ← Uses 80% CPU
│  (80% of core)      │
├─────────────────────┤
│  Web Requests       │  ← Only 20% left!
│  (20% of core)      │  ← Slow responses
└─────────────────────┘

परिणाम: आपकी वेबसाइट उस घड़ी के पृष्ठभूमि कार्य में भर गयी ।

पोल स्टारिंग लड़ी

क्लासिक एई. थ्रेड-परेशनunit-formatथ्रेड पूल अपेक्षाकृत छोटा (25- 100 थ्रेड विशेष रूप से मेल खाता था, और पृष्ठभूमि कार्य थ्रेड जो वेब निवेदन किए जाने चाहिए:

// Classic ASP.NET (2008)
ThreadPool.QueueUserWorkItem(_ =>
{
    // This steals a thread from the pool!
    ProcessLongRunningTask();
});

// Meanwhile, web requests are queued waiting for threads
// HTTP 503 Service Unavailable

आईआईएस अनुप्रयोग पूल रीम्पिंग

RISHAC अनुप्रयोग पूल्स (आपके एप्पल को प्रारंभ करें) स्मृति सीमा, गणना, या समय सारिणी के आधार पर. पृष्ठभूमि कार्य मध्य- चरणों में मारा जाएगा:

00:00 - Background import starts (2 hour task)
02:00 - IIS recycles app pool (scheduled)
      - Background task killed
      - Work lost, must start again

सीमा एसईसी/ सी समर्थन

.ENT 4.5 (202) से पहले, प्रोग्रामिंग दर्दनाक थी. पृष्ठभूमि कार्य अक्सर लड़ीों को बेवजह बंद कर दिया गया:

// Pre-async (2008)
void ProcessEmails()
{
    foreach (var email in GetEmails())
    {
        smtp.Send(email);  // Blocks thread for 500ms per email
    }
}
// 100 emails = 50 seconds of blocked thread time

कौन - सा परिवर्तित: आधुनिक युग

आज का वातावरण बहुत अलग है:

1. बहु- कोर एफ़फोर्डेबल है

(Applause.) आर्थिक रूप से बंद किया गया है. बादल बहुत से कोर के साथ बहुत से कोरसों के साथ बहुत कम मूल्य हैं, और धातु के सर्वरों को जन्म रहे हैं आश्चर्यजनक हैं. इस ब्लॉग एक समर्पित 8-कोयर सर्वर पर दौड़ता है कि एक के रूप में एक के रूप में डी.एम, और मैं उन सभी को मिलता है जो अपने आप के लिए सबसे कम खर्च करते हैं, कोई भी भीड़ वाले पड़ोसियों पर निर्भर नहीं करता:

8-Core Server (2024):
Core 1: ████████████████████ Web Requests
Core 2: ████████████████████ Web Requests
Core 3: ████████████████████ Web Requests
Core 4: ████████████████████ Web Requests
Core 5: ████████████████████ Background Task ← Isolated
Core 6: ████████████████████ Background Task
Core 7: ████████████████████ Background Task
Core 8: ████████████████████ Background Task

2 एसईएसी/AWH हर जगह

आधुनिक.नेटट प्रोग्रामिंग सरल बनाता है. पृष्ठभूमि कार्य मैं/O बिना ब्लॉक थ्रेड के इंतजार कर सकते हैं:

// Modern async (2024)
async Task ProcessEmailsAsync(CancellationToken ct)
{
    await foreach (var email in GetEmailsAsync(ct))
    {
        await smtp.SendAsync(email, ct);  // Doesn't block thread!
    }
}
// 100 emails processed efficiently, thread returns to pool during I/O

3 बेहतर प्रक्रिया होस्ट

  • डॉकर - कंटेनरों में पृष्ठभूमि सेवाओं के उपयोग में नहीं मिलता है
  • कुबरे - ठीक ढंग से बंद करें के साथ SIGTERM
  • तंत्र - लिनक्स सेवाएँ जो पुनः प्रारंभ होती हैं
  • विंडोज़ सेवाएँName - सही लंबी दूरी प्रक्रिया मॉडल

4. चैनल्स और आधुनिक आदिम

.NT अब पहले से-क्लास समर्थन के लिए एक को वर्तमान प्रोग्रामिंग के साथ System.Threading.Channels:

// System.Threading.Channels
var channel = Channel.CreateBounded<Email>(100);

// Producer (web request)
await channel.Writer.WriteAsync(email);  // Fast, non-blocking

// Consumer (background service)
await foreach (var email in channel.Reader.ReadAllAsync())
{
    await ProcessAsync(email);  // Efficient, async
}

5 रिसोर्स सीमा तथा समूह

आधुनिक प्लास्टिक के भाई - बहन आपको जाने देते हैं संसाधन प्रयोग की सीमा:

# Kubernetes resource limits
resources:
  limits:
    cpu: "500m"        # Background task can't use more than 0.5 CPU
    memory: "512Mi"    # Or more than 512 MB RAM

इसका अर्थ है कि एक थके हुए पृष्ठभूमि कार्य अपने वेब टाई नहीं खिला सकता.

सवाल अब नहीं रहा "क्या हम अपने वेब ए. में पृष्ठभूमि सेवा चला सकते हैं?" लेकिन "लेकिन"हमें क्या करना चाहिए?"हम इस निर्णय की जाँच करेंगे "जब पृष्ठभूमि सेवा विभाग का उपयोग नहीं करने के लिए नहीं" खण्ड बाद में.

अंतर्निर्मित विकल्प

IHobed सेवा: बुनियाद

अपने कोर में, हर पृष्ठभूमि सेवा . IHostedService.. इस इंटरफेस बहुत सरल है:

public interface IHostedService
{
    Task StartAsync(CancellationToken cancellationToken);
    Task StopAsync(CancellationToken cancellationToken);
}

दो तरीकों से। StartAsync जब आपका अनुप्रयोग प्रारंभ होता है तो बुलाया जाता है, तथा StopAsync जब यह बंद हो जाता है.

अपनी सेवा को इसमें पंजीकृत करें Program.cs:

builder.Services.AddHostedService<MyBackgroundService>();

यहाँ जीवनीय दृश्‍य है:

graph LR
    A[Application Starts] --> B[StartAsync Called]
    B --> C[Service Running]
    C --> D[Application Shutting Down]
    D --> E[StopAsync Called]
    E --> F[Application Stopped]

    style A stroke:#059669,stroke-width:3px,color:#10b981
    style C stroke:#2563eb,stroke-width:3px,color:#3b82f6
    style F stroke:#dc2626,stroke-width:3px,color:#ef4444

एसिंक प्रारंभ करें: एसिंक वीससिंक प्रारंभ होता है

काम करते वक्‍त एक ज़रूरी फैसला IHostedService आपका है कि क्या StartAsync विधि ब्लॉक या तत्काल पलटना चाहिए.

सिंकसिंक ( ब्लॉकिंग) प्रारंभ:

public class BlockingStartService : IHostedService
{
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        // This blocks application startup until complete
        await InitializeDatabaseAsync(cancellationToken);
        await LoadConfigurationAsync(cancellationToken);

        // Only now will the application continue starting
    }

    public Task StopAsync(CancellationToken cancellationToken)
        => Task.CompletedTask;
}

एग्रीच (n- ब्लॉकिंग) प्रारंभ होता है:

public class NonBlockingStartService : IHostedService
{
    private Task _backgroundTask;
    private readonly CancellationTokenSource _cts = new();

    public Task StartAsync(CancellationToken cancellationToken)
    {
        // Start background work but return immediately
        _backgroundTask = Task.Run(async () =>
        {
            // Give other services time to initialise
            await Task.Delay(TimeSpan.FromSeconds(5), _cts.Token);
            await DoLongRunningWorkAsync(_cts.Token);
        }, _cts.Token);

        return Task.CompletedTask;
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        _cts.Cancel();
        await _backgroundTask; // Wait for completion
    }
}

जब (वह कोई काम करता हैं तो) दो लिखने वाले (केरामन क़ातेबीन)

  • ब्लॉक: जब सेवा पूरी हो तो अनुप्रयोग निवेदन संभाल कर सकता है (उदाहरण के लिए, महत्वपूर्ण विन्यास लोड किया जा रहा है, तापमान कम करने के लिए)
  • नॉन- ब्लॉक: जब सेवा पृष्ठभूमि में प्रारंभ होता है जब तक कि अन्य सेवा प्रारंभ नहीं होती (उदा., इंडेक्सिंग मौजूदा सामग्री, बाहरी सेवाओं के साथ तुल्यकालन किया जा सकता है)

शॉक बंद करो: सामान्य Phonon

यहाँ चीजों को दिलचस्प मिलता है, और जहां कई विकासकर्ता समस्याओं का सामना करते हैं. जब आपका अनुप्रयोग नीचे बंद होता है, एक गुप्त कॉल. StopAsync सभी होस्ट्ड सेवाओं पर. आपके पास एक सीमित विंडो (डिफ़ॉल्ट 5 सेकण्ड) है जिसे आप साफ करने के लिए भेज सकते हैं. आप इसमें जा सकते हैं. Program.cs:

builder.Services.Configure<HostOptions>(options =>
{
    options.ShutdownTimeout = TimeSpan.FromSeconds(30);
});

सबसे सामान्य गलती:

public class BrokenService : IHostedService
{
    private readonly Channel<string> _channel = Channel.CreateUnbounded<string>();
    private Task _processingTask;

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _processingTask = ProcessMessagesAsync();
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        // WRONG: The channel is still open, ProcessMessagesAsync
        // will hang on WaitToReadAsync forever!
        return Task.CompletedTask;
    }

    private async Task ProcessMessagesAsync()
    {
        // This will never exit because the channel is never completed
        await foreach (var message in _channel.Reader.ReadAllAsync())
        {
            await ProcessAsync(message);
        }
    }
}

जब आप इस सेवा को चलाते हैं और अपने अनुप्रयोग को बंद करते हैं, आप जैसे त्रुटियाँ देखेंगे:

Unable to cast object of type 'TaskCompletionSource`1[System.Threading.Tasks.VoidTaskResult]' to type 'System.Threading.Tasks.Task'

या अनुप्रयोग ढीले पड़ने से पहले बस बंद होने की अवधि के लिए लटका रहेगा.

सही तरीका:

public class CorrectService : IHostedService
{
    private readonly Channel<string> _channel = Channel.CreateUnbounded<string>();
    private readonly CancellationTokenSource _cts = new();
    private Task _processingTask;

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _processingTask = ProcessMessagesAsync(_cts.Token);
        return Task.CompletedTask;
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        // CORRECT: Signal cancellation and complete the channel
        await _cts.CancelAsync();
        _channel.Writer.Complete();

        try
        {
            // Wait for processing to finish or for the shutdown timeout
            await Task.WhenAny(_processingTask,
                Task.Delay(Timeout.Infinite, cancellationToken));
        }
        catch (OperationCanceledException)
        {
            // Expected when shutdown timeout is reached
        }
    }

    private async Task ProcessMessagesAsync(CancellationToken token)
    {
        await foreach (var message in _channel.Reader.ReadAllAsync(token))
        {
            try
            {
                await ProcessAsync(message);
            }
            catch (OperationCanceledException)
            {
                // Shutdown requested, exit gracefully
                break;
            }
        }
    }
}

सही संपादन कार्यान्वयन के लिए कुंजी इंगित करता है:

  1. सिग्नल रद्द - एक का उपयोग करें CancellationTokenSource और इसे रद्द करें
  2. चैनल पूरा करें - यदि आप चैनल का उपयोग कर रहे हैं, कॉल Writer.Complete()
  3. पृष्ठभूमि कार्य हेतु इंतजार करें - उपयोग Task.WhenAny रद्दीकरण टोकन के साथ
  4. ऑपरेशन रद्द किया - यह उम्मीद है और पकड़ा जाना चाहिए
  5. अपवादों को मत छोड़ें -में अपवाद StopAsync अस्वाभाविक व्यवहार उत्पन्‍न कर सकता है

पृष्ठभूमि सेवा:

लिखा जा रहा है IHostedService कार्यान्वयन बारंबार हो सकता है. आपको हमेशा एक पृष्ठभूमि कार्य, एक रद्द टोकन स्रोत, और वही साफ- सफाई पैटर्न की आवश्यकता है. BackgroundService यह फोड़े आपके लिए संभालता है:

public abstract class BackgroundService : IHostedService, IDisposable
{
    private Task _executeTask;
    private CancellationTokenSource _stoppingCts;

    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        _stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
        _executeTask = ExecuteAsync(_stoppingCts.Token);
        return Task.CompletedTask;
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        if (_executeTask == null) return;

        try
        {
            _stoppingCts.Cancel();
        }
        finally
        {
            await Task.WhenAny(_executeTask, Task.Delay(Timeout.Infinite, cancellationToken));
        }
    }

    public virtual void Dispose()
    {
        _stoppingCts?.Cancel();
    }
}

आप सिर्फ लागू करें ExecuteAsync और उप हुआ वर्ग;

public class SimpleBackgroundService : BackgroundService
{
    private readonly ILogger<SimpleBackgroundService> _logger;

    public SimpleBackgroundService(ILogger<SimpleBackgroundService> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Service starting");

        // Wait for app to finish starting
        await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);

        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                await DoWorkAsync(stoppingToken);
                await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
            }
            catch (OperationCanceledException)
            {
                // Shutdown requested
                break;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error in background service");
            }
        }

        _logger.LogInformation("Service stopping");
    }

    private async Task DoWorkAsync(CancellationToken token)
    {
        _logger.LogInformation("Doing work...");
        // Your actual work here
        await Task.Delay(1000, token);
    }
}

पृष्ठभूमि सर्विसेशन इस्तेमाल करने के दौरान

प्रयोक्ता BackgroundService कब:

  • आपको लंबा- तय पृष्ठभूमि लूप की जरूरत है
  • आप सादा आवधिक चलाने के लिए चाहते हैं
  • आप पर ठीक नियंत्रण की जरूरत नहीं है StartAsync/StopAsync समय

प्रयोक्ता IHostedService कब:

  • आप वास्तव में क्या होता है नियंत्रण करने की जरूरत है StartAsync पृष्ठभूमि कार्य
  • आप एक सतत लूप के बजाय कार्यक्रम हैंडलर या पहरेदार स्थापित कर रहे हैं
  • स्टार्टअप के दौरान आपको अन्य सेवाओं के साथ निर्देशांक लगाने की आवश्यकता है

विस्तृत: शुरू - शुरू में बदलाव

कभी - कभी आपको एक दूसरे के लिए प्रतीक्षा करने के लिए सेवाओं की आवश्यकता होती है. उदाहरण के लिए, आप शायद चाहते हैं कि आपका अतिरिक्त सर्च इंडेक्सर तब तक इंतजार करें जब तक कि आपका निशान नीचे की प्रक्रिया ख़त्म न हो जाए.

यहाँ कोडिंग सेवा प्रारंभ करने का एक पैटर्न है:

public interface IStartupCoordinator
{
    void RegisterService(string serviceName);
    void SignalReady(string serviceName);
    bool IsServiceReady(string serviceName);
    Task WaitForServiceAsync(string serviceName, CancellationToken cancellationToken = default);
    Task WaitForAllServicesAsync(CancellationToken cancellationToken = default);
}

public class StartupCoordinator : IStartupCoordinator
{
    private readonly ConcurrentDictionary<string, TaskCompletionSource> _services = new();
    private readonly ILogger<StartupCoordinator> _logger;

    public void RegisterService(string serviceName)
    {
        _services.TryAdd(serviceName, new TaskCompletionSource());
    }

    public void SignalReady(string serviceName)
    {
        if (_services.TryGetValue(serviceName, out var tcs))
        {
            tcs.TrySetResult();
            _logger.LogInformation("{Service} is ready", serviceName);
        }
    }

    public async Task WaitForServiceAsync(string serviceName, CancellationToken ct = default)
    {
        if (_services.TryGetValue(serviceName, out var tcs))
        {
            await tcs.Task.WaitAsync(ct);
        }
    }

    public async Task WaitForAllServicesAsync(CancellationToken ct = default)
    {
        await Task.WhenAll(_services.Values.Select(tcs => tcs.Task)).WaitAsync(ct);
    }
}

सेवा में उपयोग में:

public class DependentService : IHostedService
{
    private readonly IStartupCoordinator _coordinator;
    private readonly ILogger<DependentService> _logger;

    public DependentService(
        IStartupCoordinator coordinator,
        ILogger<DependentService> logger)
    {
        _coordinator = coordinator;
        _logger = logger;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        // Wait for another service to be ready
        await _coordinator.WaitForServiceAsync("MarkdownProcessor", cancellationToken);

        _logger.LogInformation("Dependencies ready, starting work");

        // Do your work...

        // Signal you're ready for services that depend on you
        _coordinator.SignalReady("DependentService");
    }

    public Task StopAsync(CancellationToken cancellationToken)
        => Task.CompletedTask;
}

यह पैटर्न ख़ासकर उपयोगी होता है जब आपके पास अनेक पृष्ठभूमि सेवाओं के साथ एकीकरण होता है ।

रेडिस के साथ संधि

स्टार्टअप केकच एक एकल अनुप्रयोग उदाहरण के भीतर काम करता है. लेकिन जब आप अनेक उदाहरणों के लिए स्केल करते हैं तब क्या होता है? आप तीन उदाहरणों को एक साथ चलाने के लिए नहीं चाहते.

लाल एक सरल समाधान प्रदान करता है: फ्लैग (कीलिंग) का उपयोग ऐसे करने के लिए करें जो क्या करता है.

सादा नेता

public class DistributedBackgroundService : BackgroundService
{
    private readonly IConnectionMultiplexer _redis;
    private readonly ILogger<DistributedBackgroundService> _logger;
    private readonly string _instanceId = Guid.NewGuid().ToString();
    private const string LeaderKey = "background:newsletter:leader";

    public DistributedBackgroundService(
        IConnectionMultiplexer redis,
        ILogger<DistributedBackgroundService> logger)
    {
        _redis = redis;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var db = _redis.GetDatabase();

        while (!stoppingToken.IsCancellationRequested)
        {
            // Try to become the leader (SET NX with expiry)
            var acquired = await db.StringSetAsync(
                LeaderKey,
                _instanceId,
                TimeSpan.FromMinutes(5),
                When.NotExists);

            if (acquired)
            {
                _logger.LogInformation("This instance is the leader, running task");

                try
                {
                    await DoScheduledWorkAsync(stoppingToken);
                }
                finally
                {
                    // Release leadership
                    await db.KeyDeleteAsync(LeaderKey);
                }
            }
            else
            {
                var leader = await db.StringGetAsync(LeaderKey);
                _logger.LogDebug("Another instance ({Leader}) is the leader", leader);
            }

            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }
}

गंभीर खण्डों के लिए कतार में ताला लगाना

इस कार्य के लिए जिसे मौज़ूदा रूप से उदाहरणों में चलाया नहीं जाना चाहिए:

public async Task ProcessWithLockAsync(CancellationToken cancellationToken)
{
    var db = _redis.GetDatabase();
    var lockKey = "locks:critical-task";
    var lockValue = _instanceId;

    // Try to acquire lock
    if (await db.LockTakeAsync(lockKey, lockValue, TimeSpan.FromMinutes(10)))
    {
        try
        {
            _logger.LogInformation("Lock acquired, processing...");
            await DoCriticalWorkAsync(cancellationToken);
        }
        finally
        {
            await db.LockReleaseAsync(lockKey, lockValue);
        }
    }
    else
    {
        _logger.LogDebug("Could not acquire lock, another instance is processing");
    }
}

शांति कायम करने के लिए कब इस्तेमाल करें

  • सारिणीबद्ध किए गए कार्य - केवल एक उदाहरण को दैनिक पत्रिका भेजना चाहिए
  • अनुक्रम के साथ क़तार में लगाएँ - सुनिश्चित करें कि संदेश क्रम में किया गया है
  • संसाधन- इंटेनेंस संचालन -बहुत से बाहरी एपीआई से रोकें
  • डाटाबेस उत्प्रवासन - आरंभ पर केवल एक ही उदाहरण उत्प्रवासन को चलाना चाहिए

अधिक जटिल दृश्‍य के लिए (mult-ult- ups कार्य, निर्भर रूप से पुनः प्रारंभ करने के दौरान, हैस्ट आग जो इसके डाटाबेस बैकएण्ड के साथ स्वतः वितरित कर सकता है पर विचार करें.

पृष्ठभूमि सेवा इस्तेमाल करने के लिए कब नहीं

आप के बारे में बात करते हैं जब हम और अधिक जटिल उपकरणों में कटौती करने से पहले, चलो नहीं करना चाहिए अपने मुख्य वेब अनुप्रयोग में पृष्ठभूमि सेवाओं का उपयोग करें.

संकेत आपको अलग परियोजना में विभाजित करना चाहिए

आपके वेब अनुप्रयोग में चल रहे पृष्ठभूमि सेवा आपके HTTP निवेदन के साथ है. यह समस्या पैदा कर सकता है:

1. रिसोर्स सामग्री

समस्या: आपकी पृष्ठभूमि सेवा महत्वपूर्ण सीपीयू, स्मृति, या डाटाबेस कनेक्शन भस्म करती है.

// This will starve your web application
public class VideoTranscodingService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var video = await _queue.DequeueAsync();
            // This uses 100% of 4 CPU cores for 5 minutes
            await TranscodeVideoAsync(video);
        }
    }
}

जब वेब निवेदनबीना के दौरान आता है, वे धीमा हैं क्योंकि सीपीयू व्यस्त है.

समाधानः अलग सहकर्मी सेवा में खिसकाएँ:

# Your solution structure
/YourApp.Web          # ASP.NET Core web app - no background services
/YourApp.Worker       # .NET Worker Service - handles background work
/YourApp.Shared       # Shared models, interfaces

2. अलग - अलग स्केलिंग की माँग

समस्या: आपकी पृष्ठभूमि कार्य को आपके वेब टाई से भिन्न स्केलिंग की जरूरत है.

  • वेब टाईर: HTTP ट्रैफिक के लिए स्केल (दिन के दौरान 10 उदाहरणों की ज़रूरत होती है, 2 रात में)
  • पृष्ठभूमि टाईपः कतार गहराई के लिए स्केल (डिस्क को सामान्य रूप से 1 उदाहरण की जरूरत है, 20 जब मिटाया जा रहा हो)

वे एक ही प्रक्रिया में हैं, तो आप उन्हें स्वतंत्र रूप से स्केल नहीं कर सकते हैं।

उदाहरण के लिए:

09:00 - High web traffic, low background work → Need 10 web instances, 1 worker
14:00 - Newsletter time! Low web traffic, high background work → Need 2 web instances, 20 workers

आपके वेब ऐप में पृष्ठभूमि सेवा करने का मतलब है कि आपको 20 वेब उदाहरण चलाना होगा... ... सिर्फ चीज़ों को संभालने के लिए, संसाधनों को बर्बाद करने के लिए.

3 गलत कदम उठाने से आज़ादी मिलती है

समस्या: आप वेब परिवर्तनों को पृष्ठभूमि सेवा फिर से प्रारंभ करने के बिना तैनात करना चाहते हैं (या वापस लाना चाहते हैं).

// If this is in your web app, deploying a CSS change restarts the service
public class LongRunningImportService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // This import takes 2 hours
        await ImportMillionsOfRecordsAsync(stoppingToken);
    }
}

हर तैनाती आयात बाधित करता है. इसे एक अलग कर्मचारी सेवा के लिए ले जाएँ जो आप स्वतंत्र रूप से तैनात करते हैं.

4. भिन्न असफल डोमेन

समस्या: आपकी पृष्ठभूमि सेवा में एक बग जो सारे वेब अनुप्रयोग को क्रैश करता है.

// This null reference exception crashes your web app
public class BuggyBackgroundService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        string value = null;
        // Unhandled exception - takes down the whole app
        await ProcessAsync(value.Length);
    }
}

यदि पृष्ठभूमि कार्य एक अलग प्रक्रिया में है, यह क्रैश कर सकता है और वेब निवेदन को प्रभावित करने के बिना फिर आरंभ कर सकता है.

पृष्ठभूमि सेवा किस तरह लागू करें

जब आप विभाजित करने का निर्णय करते हैं, यहाँ दी गई संरचना है:

विकल्प 1:

कार्यर सेवा टैम्प्लेट के प्रयोग से एक नई परियोजना बनाएँ:

dotnet new worker -n YourApp.Worker

स्ट्रक्चर

/YourApp.Worker
  /Services
    VideoTranscodingService.cs
    EmailSenderService.cs
  /Program.cs
  /appsettings.json

प्रोग्राम.cs:

var builder = Host.CreateApplicationBuilder(args);

// Register your background services
builder.Services.AddHostedService<VideoTranscodingService>();
builder.Services.AddHostedService<EmailSenderService>();

// Share configuration with web app
builder.Services.Configure<VideoConfig>(
    builder.Configuration.GetSection("Video"));

// Share database context
builder.Services.AddDbContext<YourDbContext>(options =>
    options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));

var host = builder.Build();
host.Run();

डिग्रीली अलग:

# Web app on ports 80/443
/YourApp.Web → web-server-1, web-server-2, web-server-3

# Worker service doesn't listen on any port
/YourApp.Worker → worker-server-1, worker-server-2

विकल्प 2: साझेदारी क़तार के साथ अलग परियोजना

संदेश क़तार में क्लिक करें (U)

graph LR
    A[Web App] --> B[Message Queue]
    B --> C[Worker 1]
    B --> D[Worker 2]
    B --> E[Worker N]

    style A stroke:#059669,stroke-width:3px,color:#10b981
    style B stroke:#2563eb,stroke-width:3px,color:#3b82f6
    style C stroke:#7c3aed,stroke-width:3px,color:#8b5cf6
    style D stroke:#7c3aed,stroke-width:3px,color:#8b5cf6
    style E stroke:#7c3aed,stroke-width:3px,color:#8b5cf6

वेब एडी कतार कार्य:

// In your web controller
public class VideoController : ControllerBase
{
    private readonly IMessageQueue _queue;

    [HttpPost("upload")]
    public async Task<IActionResult> Upload(IFormFile video)
    {
        await _storage.SaveAsync(video);

        // Queue for processing - don't process in web app
        await _queue.PublishAsync(new VideoTranscodeJob
        {
            VideoId = video.Id,
            Priority = Priority.Normal
        });

        return Accepted(); // Return immediately
    }
}

क़तार से काम करनेवाले भस्म होते हैं:

// In your worker service
public class VideoWorker : BackgroundService
{
    private readonly IMessageQueue _queue;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await foreach (var job in _queue.SubscribeAsync<VideoTranscodeJob>(stoppingToken))
        {
            await TranscodeAsync(job);
        }
    }
}

लोकप्रिय संदेश क़तार विकल्प:

विकल्प ३: बहुत से विशेष कर्मचारी

जटिल तंत्रों के लिए, जिम्मेदारी द्वारा अलग किया गया:

/YourApp.Web              # HTTP requests only
/YourApp.EmailWorker      # Sends emails
/YourApp.VideoWorker      # Transcodes videos
/YourApp.ReportWorker     # Generates reports
/YourApp.Scheduler        # Runs scheduled jobs (Hangfire)

हर कारीगर कर सकता है:

  • स्वतंत्र रूप से स्केल करें
  • स्वतंत्र
  • भिन्न संसाधनों का उपयोग करें (कोशी की जरूरत है, वीडियो कार्यकर्ता को GUCKS की जरूरत है)
  • अलग नज़र रखिए और सतर्क रहिए

आपके वेब एप्ल में पृष्ठभूमि सेवा करते वक्‍त

ऊपर के बावजूद कुछ दृश्‍य पृष्ठभूमि सेवाओं के लिए पूरी तरह से ठीक हैं:

हल्के गतिवाले कार्य

// Fine to keep in web app
public class CacheWarmingService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            await _cache.WarmupAsync(); // Quick operation
            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }
}

घटना सुननेवाले

// Fine to keep in web app
public class FileWatcherService : IHostedService
{
    // Reacts to events, doesn't consume significant resources
    private FileSystemWatcher _watcher;

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _watcher = new FileSystemWatcher("/config");
        _watcher.Changed += OnConfigChanged;
        _watcher.EnableRaisingEvents = true;
        return Task.CompletedTask;
    }
}

चैनल- बेस्ड क़तार ( नॉन- कॉन्फ़िग कार्य के लिए)

// Fine to keep in web app if work is quick and not critical
public class EmailQueueService : BackgroundService
{
    // Sends emails in background, but each email takes < 1 second
    // If the app restarts, losing a few queued emails is acceptable
}

प्रारंभ कोरी

// Fine to keep in web app
public class WarmupService : IHostedService
{
    // Runs once at startup, then does nothing
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        await _database.WarmupConnectionPoolAsync();
        await _cache.LoadCriticalDataAsync();
    }
}

निर्णय मैट्रिक्स

[ पेज 12 पर बड़े अक्षरों में लेख की खास बात] |---------------|-----------------|------------------------| ATRS सीपीयू प्रयोग प्रति संक्रिया < 100ms > 1 सेकण्ड ICS मेमोरी प्रति संक्रिया < 10 MB > 100 MB सीजेके आवधिक आवृतिीय (मिनट/ घंटे) japan. kgm कड़ी की गंभीरता (m) मिलीलीटर अवधि (c) 2004- 2007 मिनटों को घंटे की अवधि _LGRWWWOWWWY कार्य कतार गहराई तक सीमित करता है WEGERELCKS कैश, विन्यास वीडियो प्रक्रिया को पुनः लोड करता है, बड़े पैमाने पर आयात करता है

वास्तविक विश्वव्यापी उदाहरण: ब्लॉग प्लेटफ़ॉर्म

ब्लॉग ब्लॉग में हम पार्ट 2:

वेब ए. में बंद करें

  • MarkdownDirectoryWatcherService - हल्के रंग की फ़ाइल पहरेदार
  • UmamiBackgroundSender - जल्द ही एक अजीब घटना
  • EmailSenderHostedService - छोटा आवाज़, नॉन- एमुलेटर
  • MarkdownReAddPostsService - केवल स्टार्टअप-, कॉन्फ़िगरेशन- पूर्ण

यदि हिसाब बढ़ जाए तो काम करने के लिए आगे बढ़ना चाहिए:

  • BrokenLinkCheckerBackgroundService - कई एचटीटीपी निवेदन बनाता है
  • SemanticIndexingBackgroundService - कॉलर बाहरी संग्रहक एपीआई

पहले से ही अलग सेवा में:

  • Mostlylucid.SchedulerService - हैंग फायरिंग और कोल भेज रहा है

जब आपके पास सबूत हो तो सरल शुरू कीजिए (इन प्रक्रियाों में) ।

मूल से आगे है: हैंग आग

स्पिन IHostedService और BackgroundService आप स्वयं और नियंत्रण के लिए उत्कृष्ट हैं, कभी कभी आप अधिक जटिल व्यवस्था की जरूरत है. कि जहाँ पुस्तकालय पसंद करते हैं हैंग फायर अंदर आओ.

हैंग आग प्रदान करता है:

  • क़तारबद्ध कार्य कतार - कार्य जारी है अनुप्रयोग फिर से प्रारंभ करता है
  • कार्य फिर से प्रारंभ करें - कैशेट
  • डैश बोर्ड यूआई - देखो क्या चल रहा है, क्या विफल रहा है, कार्य फिर कोशिश करें
  • वितरित किया गया कार्यान्वित किया गया - बहु सर्वर एक ही कार्य कतार की प्रक्रिया कर सकते हैं
  • स्वचालित फिर से लोड करें - असफल कार्य स्वतः हल करना वापस बैकऑफ के साथ पुनः लोड कर रहे हैं

यहाँ एक सरल उदाहरण है:

// In Program.cs
builder.Services.AddHangfire(config => config
    .UsePostgreSqlStorage(connectionString)
    .UseRecommendedSerializerSettings());

builder.Services.AddHangfireServer();

var app = builder.Build();

// Schedule recurring jobs
app.UseHangfireDashboard();
app.Services.GetRequiredService<IRecurringJobManager>()
    .AddOrUpdate<NewsletterService>(
        "send-daily-newsletter",
        x => x.SendDailyNewsletter(),
        Cron.Daily(17)); // 5 PM every day

आपकी सेवा बस एक सामान्य वर्ग है:

public class NewsletterService
{
    private readonly IEmailService _emailService;
    private readonly ISubscriberRepository _subscribers;

    public NewsletterService(
        IEmailService emailService,
        ISubscriberRepository subscribers)
    {
        _emailService = emailService;
        _subscribers = subscribers;
    }

    public async Task SendDailyNewsletter()
    {
        var subscribers = await _subscribers.GetDailySubscribersAsync();

        foreach (var subscriber in subscribers)
        {
            await _emailService.SendNewsletterAsync(subscriber);
        }
    }
}

हैंग फ़ायर संभालता है:

  • कार्य को निर्धारित समय पर चलाता है सुनिश्चित करने के लिए
  • यदि यह असफल हो तो फिर कोशिश करना
  • मारबल इतिहास भंडारित कर रहा है
  • सब कुछ मॉनीटर करने के लिए एक मलेशियाईबोर्ड देना
graph TD
    A[Hangfire Server] --> B{Check Schedule}
    B -->|Job Due| C[Dequeue Job]
    C --> D[Execute Job Method]
    D -->|Success| E[Mark Complete]
    D -->|Failure| F[Retry with Backoff]
    F --> G{Max Retries?}
    G -->|No| C
    G -->|Yes| H[Mark Failed]
    E --> I[Update Dashboard]
    H --> I
    I --> B

    style A stroke:#059669,stroke-width:3px,color:#10b981
    style D stroke:#2563eb,stroke-width:3px,color:#3b82f6
    style E stroke:#059669,stroke-width:3px,color:#10b981
    style H stroke:#dc2626,stroke-width:3px,color:#ef4444

जब हैंग आग इस्तेमाल किया जाए:

  • आपको कार्य कतारों की आवश्यकता है जो फिर से प्रारंभ होने से बच सकते हैं
  • आप मॉनीटर करने और दस्ती रूप से ट्रिगर कार्य करने के लिए डैश चाहते हैं
  • आपको अनेक सर्वरों पर वितरित कार्य प्रक्रिया की जरूरत है
  • आप निर्माण तर्क और विफलता हैंडल करना चाहते हैं
  • आपको बारंबार होने वाली कार्यों की टिक- रेखा की जरूरत है

जब IHHobed जंग/ पृष्ठभूमि- सेंटर के साथ जुड़े रहें:

  • जीवन - साथी की सेवा पर आपको बढ़िया नियंत्रण रखने की ज़रूरत है
  • आपकी सेवा को वास्तविक समय में घटनाओं के प्रति प्रतिक्रिया देने की जरूरत है
  • आप न्यूनतम निर्भरता चाहते हैं
  • आप एक साधारण आवधिक कार्य का निर्माण कर रहे हैं जो लगन की आवश्यकता नहीं है

अन्य विकल्प

वार्म्ट आग लोकप्रिय है, अन्य पुस्तकालयों पर विचार करने के योग्य हैं:

क्वार्ट्ज.नेटटी:

  • हैंग आग से ज़्यादा विशेषज्ञ तैयारी करने के लिए तैयार रहते हैं
  • रेगुलर एक्सप्रेशन तथा कैलेण्डर आधारित सारिणी को समर्थित करता है
  • बहुत से डाटाबेस में संलग्न किया जा सकता है
  • अधिक जटिल लेकिन अधिक शक्तिशाली

द्रव्यमान/एन- सर्विस- बस:

  • पूरी विशेषताओं से युक्त संदेश बस कार्यान्वयन
  • वितरित सिस्टम तथा माइक्रोफ़ॉर्मेट के लिए बेहतर
  • समर्थन सागा (लंबा कार्य प्रवाह)
  • जहाज सीखने वाला वक्र

कम्पेचर फंक्शन्स/एडबल्यूह लेडा:

  • यदि आप बादल में हैं, सर्वरों पर विचार करें
  • एक सेवा चल रही रखने के बजाय उसे निरन्तर चलाना पड़ता है
  • स्वचालित स्केलिंग
  • कुछ देर के लिए ठंड की शुरूआत

सारांश

भाग 1 में, हमने मूल सेवाओं के लिए मध्य सीमा पार की है.

  1. आई- होस्ट- सर्विस - नींव, अधिकतम रिसर्च
  2. पृष्ठभूमि सेवा लंबे सफरी लूपों के लिए कनेक्शनात्मक बेस क्लास
  3. स्टार्टअप समन्वय - सेवाओं को एक दूसरे के लिए इंतजार कर रहे हैं
  4. संपीडित समन्वय - बहुमुखी दृश्य के लिए रेडिस का उपयोग कर रहा है
  5. हैंग फायर - जब आपको मेहनत से काम करने की ज़रूरत है और जटिल योजना की जरूरत है

सबसे अहम सबक:

  • हमेशा चैनलों को पूरा करें तथा बन्द करने में संकेतों को रद्द करें
  • तय करें कि क्या प्रमोशन ब्लॉक करना चाहिए या तत्काल लौटाया जाए
  • ऑपरेशन किए बगैर किसी को माफ करना
  • कार्य का उपयोग करें. जब बन्द करने का कोई चिन्ह हो तो बन्द करने के लिए बन्द करें

में पार्ट 2, हम एक उत्पादन ब्लॉग मंच से असली विश्व कार्यान्वयन की जाँच करेंगे:

  • फ़ाइल तंत्र निरीक्षण करें जो कि सिंकिंग फ़ाइल को डाटाबेस में व्यवस्थित करता है
  • पासवर्ड तथा सर्किट ब्रेकर्स के साथ ई- मेल भेजने वाला प्रेषक
  • किसी भी तरह की घटना क़तार जो बैच निवेदन करती है
  • सीमेनिक सर्च इंडेक्सर जो अतुल्यकालिक रूप से प्रक्रिया करता है
  • टूटी लिंक चेक करें जो कि समय- समय पर बाहरी यूआरएल को वैध करता है

इन उदाहरणों से पता चलता है कि भाग 1 से पैटर्न, साथ ही शुरूआती तालमेल का नमूना और ठीक तरह से बंद हो जाना भी शामिल है ।

आगे पढ़ा जा रहा है

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.