Back to "Δημιουργία συστήματος ροής εργασίας με HTMX και ASP.NET Core - Μέρος ΜSK2 Hangfire Integration and Automation"

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 Background Jobs Hangfire Workflow

Δημιουργία συστήματος ροής εργασίας με HTMX και ASP.NET Core - Μέρος ΜSK2 Hangfire Integration and Automation

Wednesday, 15 January 2025

Εισαγωγή

Σε ΕΠΙΧΕΙΡΗΣΗ 3, χτίσαμε έναν όμορφο οπτικό επεξεργαστήΜSK1 Αλλά οι ροές εργασίας μας τρέχουν μόνο όταν τις ξεκινάμε με μη αυτόματο τρόπο. Σε αυτή την τελική ανάρτησηM SK3 θα κάνουμε τις ροές εργασιών πραγματικά αυτόνομες χρησιμοποιώντας το Hangfire για

  • Προγραμματισμένη εκτέλεση - Εκτελέστε ροές εργασίας σε ένα χρονοδιάγραμμα
  • Πυροσβεστική - Παρακολούθηση εξωτερικών APIs και εκτόξευση αλλαγών
  • Δημόσια Διοίκηση - Track trigger κράτη σε εκτελέσεις
  • Δασκάλα - Παρακολούθηση όλων των εργασιών υπόβαθρου

Γιατί το Hangfire?

Το Hangfire είναι ιδανικό για τις ανάγκες μας επειδή ανταποκρίνεται στις επιχειρήσεις της :

  • Αποθηκεύει θέσεις εργασίας στη υπάρχουσα βάση δεδομένων PostgreSQL
  • Παρέχει ένα ενσωματωμένο- διατηρήστε το dashboard
  • Υποστηρίζει επαναλαμβανόμενες θέσεις εργασίας
  • Έχει αυτόματη λογική απόσυρσης
  • Οριζόντια κλίμακα

Το μοντέλο του κράτους Trigger

Πρώτα , ας ΜSK1 καταλαβαίνει την κρατική οντότητα εκκίνηση μας Η (καλύπτει ήδη αυτό στο Μέρος Αυτή η 2):

[Table("workflow_trigger_states")]
public class WorkflowTriggerStateEntity
{
    public int Id { get; set; }
    public int WorkflowDefinitionId { get; set; }

    // Type: "Schedule", "ApiPoll", "Webhook"
    public string TriggerType { get; set; } = string.Empty;

    // Configuration as JSON
    public string ConfigJson { get; set; } = "{}";

    // Current state as JSON (stores last poll time, content hash, etc.)
    public string StateJson { get; set; } = "{}";

    public bool IsEnabled { get; set; } = true;
    public DateTime? LastCheckedAt { get; set; }
    public DateTime? LastFiredAt { get; set; }
    public int FireCount { get; set; } = 0;
    public string? LastError { get; set; }
}

Αυτή η οντότητα παρακολουθεί τα πάντα για έναν εκκίνηση ροής εργασίας:

  • Όταν το τελευταίο τρέχει
  • Ποια είναι η διαμόρφωσή της
  • Τι δηλώνει ότι το ' αναφέρει σε ( αναφέρουν σταθερούς εκκρεμότες για τα ) αναφέρουν
  • Κάθε λάθος που συνέβη

Προγραμματισμένες ροές εργασίας

Μοντέλο διαμόρφωσης

public class ScheduleTriggerConfig
{
    public string IntervalType { get; set; } = "minutes"; // minutes, hours, days
    public int IntervalValue { get; set; } = 60;
    public Dictionary<string, object>? InputData { get; set; }
}

Η εργασία του προγραμματιστή

public class WorkflowSchedulerJob
{
    private readonly MostlylucidDbContext _context;
    private readonly WorkflowExecutionService _executionService;
    private readonly ILogger<WorkflowSchedulerJob> _logger;

    [AutomaticRetry(Attempts = 3)]
    public async Task ExecuteScheduledWorkflowsAsync()
    {
        _logger.LogInformation("Checking for scheduled workflows");

        // Get all enabled schedule triggers
        var triggers = await _context.WorkflowTriggerStates
            .Include(t => t.WorkflowDefinition)
            .Where(t => t.IsEnabled && t.TriggerType == "Schedule")
            .ToListAsync();

        foreach (var trigger in triggers)
        {
            try
            {
                var config = JsonSerializer.Deserialize<ScheduleTriggerConfig>(
                    trigger.ConfigJson);

                if (config == null) continue;

                // Check if it's time to run
                if (!ShouldRunScheduledWorkflow(trigger, config))
                    continue;

                _logger.LogInformation(
                    "Executing scheduled workflow {WorkflowId}",
                    trigger.WorkflowDefinition.WorkflowId);

                // Execute the workflow
                await _executionService.ExecuteWorkflowAsync(
                    trigger.WorkflowDefinition.WorkflowId,
                    config.InputData,
                    "Scheduler");

                // Update trigger state
                trigger.LastCheckedAt = DateTime.UtcNow;
                trigger.LastFiredAt = DateTime.UtcNow;
                trigger.FireCount++;

                var state = JsonSerializer.Deserialize<Dictionary<string, object>>(
                                trigger.StateJson) ?? new();
                state["lastRun"] = DateTime.UtcNow.ToString("O");
                trigger.StateJson = JsonSerializer.Serialize(state);

                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex,
                    "Error executing scheduled workflow {TriggerId}",
                    trigger.Id);
                trigger.LastError = ex.Message;
                await _context.SaveChangesAsync();
            }
        }
    }

    private bool ShouldRunScheduledWorkflow(
        WorkflowTriggerStateEntity trigger,
        ScheduleTriggerConfig config)
    {
        // First run?
        if (!trigger.LastFiredAt.HasValue)
            return true;

        var timeSinceLastRun = DateTime.UtcNow - trigger.LastFiredAt.Value;

        return config.IntervalType.ToLower() switch
        {
            "minutes" => timeSinceLastRun.TotalMinutes >= config.IntervalValue,
            "hours" => timeSinceLastRun.TotalHours >= config.IntervalValue,
            "days" => timeSinceLastRun.TotalDays >= config.IntervalValue,
            _ => false
        };
    }
}

Πώς λειτουργεί το :

  1. Κάθε δευτερόλεπτο ικανοποιεί τις κλήσεις Hangfire ExecuteScheduledWorkflowsAsync()
  2. Ζητάμε επιτρέψιμο χρονοδιάγραμμα triggers
  3. Για κάθε εκκίνηση εγγυάται το , να ελέγξει εάν έχει περάσει αρκετός χρόνος
  4. Εάν ναι, το , μπορεί να εκτελέσει τη ροή εργασίας
  5. Ενημερώστε την κατάσταση εκκίνηση με τον τελευταίο χρόνο λειτουργίας

Πυρκαγιά

Η έρευνα API είναι πιο ενδιαφέρουσα - παρακολουθούμε εξωτερικές APIs και εκτοξεύουμε ροές εργασίας όταν το περιεχόμενο αλλάζειΜSK1

Μοντέλο διαμόρφωσης

public class ApiPollTriggerConfig
{
    public string Url { get; set; } = string.Empty;
    public int IntervalSeconds { get; set; } = 300; // 5 minutes
    public bool AlwaysTrigger { get; set; } = false;
    public Dictionary<string, string>? Headers { get; set; }
}

Η ΠΟΛΙΤΙΚΗ ΔΟΥΛΕΙΑ

[AutomaticRetry(Attempts = 3)]
public async Task PollApiTriggersAsync()
{
    _logger.LogInformation("Polling API triggers");

    var triggers = await _context.WorkflowTriggerStates
        .Include(t => t.WorkflowDefinition)
        .Where(t => t.IsEnabled && t.TriggerType == "ApiPoll")
        .ToListAsync();

    foreach (var trigger in triggers)
    {
        try
        {
            var config = JsonSerializer.Deserialize<ApiPollTriggerConfig>(
                trigger.ConfigJson);

            if (config == null) continue;

            // Check if it's time to poll
            if (trigger.LastCheckedAt.HasValue)
            {
                var timeSinceLastCheck = DateTime.UtcNow - trigger.LastCheckedAt.Value;
                if (timeSinceLastCheck.TotalSeconds < config.IntervalSeconds)
                    continue;
            }

            _logger.LogInformation("Polling API for workflow {WorkflowId}",
                trigger.WorkflowDefinition.WorkflowId);

            // Poll the API
            using var httpClient = new HttpClient();
            var response = await httpClient.GetAsync(config.Url);
            var content = await response.Content.ReadAsStringAsync();

            // Get previous state
            var state = JsonSerializer.Deserialize<Dictionary<string, object>>(
                            trigger.StateJson) ?? new();

            var previousHash = state.GetValueOrDefault("contentHash")?.ToString();
            var currentHash = ComputeHash(content);

            // Has content changed?
            if (previousHash != currentHash || config.AlwaysTrigger)
            {
                _logger.LogInformation(
                    "API content changed, triggering workflow {WorkflowId}",
                    trigger.WorkflowDefinition.WorkflowId);

                // Pass response as input to workflow
                var inputData = new Dictionary<string, object>
                {
                    ["apiResponse"] = content,
                    ["statusCode"] = (int)response.StatusCode,
                    ["previousHash"] = previousHash ?? string.Empty,
                    ["currentHash"] = currentHash
                };

                // Execute the workflow
                await _executionService.ExecuteWorkflowAsync(
                    trigger.WorkflowDefinition.WorkflowId,
                    inputData,
                    $"ApiPoll:{config.Url}");

                trigger.LastFiredAt = DateTime.UtcNow;
                trigger.FireCount++;

                // Update state
                state["contentHash"] = currentHash;
                state["lastContent"] = content.Length > 1000
                    ? content.Substring(0, 1000)
                    : content;
                state["lastPoll"] = DateTime.UtcNow.ToString("O");
            }

            trigger.LastCheckedAt = DateTime.UtcNow;
            trigger.StateJson = JsonSerializer.Serialize(state);
            trigger.LastError = null;

            await _context.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error polling API trigger {TriggerId}",
                trigger.Id);
            trigger.LastError = ex.Message;
            trigger.LastCheckedAt = DateTime.UtcNow;
            await _context.SaveChangesAsync();
        }
    }
}

private string ComputeHash(string content)
{
    using var sha256 = System.Security.Cryptography.SHA256.Create();
    var bytes = System.Text.Encoding.UTF8.GetBytes(content);
    var hash = sha256.ComputeHash(bytes);
    return Convert.ToBase64String(hash);
}

Πώς λειτουργεί το :

  1. Κάθε λεπτό εγγυάται το , να ελέγχει όλες τις εκκρεμότητες της έρευνας API
  2. Για κάθε εκκίνηση αναμένεται να ελέγξετε αν έχει περάσει αρκετός χρόνος από την τελευταία έρευνα
  3. Πληκτρολογήστε το επιλεγμένο URL
  4. Συγκρίνετε ένα hash του περιεχομένου της απάντησης
  5. Συγκρίνετε με το προηγούμενο hash που αποθηκεύτηκε στο κράτος
  6. Εάν αλλάξει (or AlwaysTrigger Είναι αλήθεια), εκτελεί τη ροή εργασίας
  7. Μεταφέρετε την απάντηση API ως δεδομένα εισόδου στη ροή εργασίας
  8. Αναβαθμίστε την κατάσταση με νέο hash

Χρησιμοποιήστε το παράδειγμα

Παρακολούθηση GitHub Releases:

{
  "triggerType": "ApiPoll",
  "config": {
    "url": "https://api.github.com/repos/dotnet/aspnetcore/releases/latest",
    "intervalSeconds": 3600,
    "alwaysTrigger": false
  }
}

Αυτό διερευνά το GitHub API κάθε ώρα.Όταν δημοσιεύεται μια νέα έκδοση, η αλλαγή περιεχομένου hashM SK2 και η ροή εργασίας εκτελεί με τα δεδομένα κυκλοφορίαςMK3

Εγγραφή θέσεων εργασίας Hangfire

Στον δικό σου Program.cs ή αρχική διαμόρφωση:

// Add Hangfire services
builder.Services.AddHangfire(config =>
{
    config.UsePostgreSqlStorage(
        builder.Configuration.GetConnectionString("DefaultConnection"));
});

builder.Services.AddHangfireServer();

// Register our job
builder.Services.AddScoped<WorkflowSchedulerJob>();

Στη συνέχεια ανακοινώθηκε, μετά την έναρξη της εφαρμογής ανακοινωθεί, αναμένεται να καταχωρηθούν επαναλαμβανόμενες θέσεις εργασίας

app.UseHangfireDashboard("/hangfire");

// Register recurring jobs
RecurringJob.AddOrUpdate<WorkflowSchedulerJob>(
    "scheduled-workflows",
    job => job.ExecuteScheduledWorkflowsAsync(),
    Cron.Minutely);

RecurringJob.AddOrUpdate<WorkflowSchedulerJob>(
    "api-poll-triggers",
    job => job.PollApiTriggersAsync(),
    Cron.Minutely);

Το Hangfire Dashboard

Hangfire περιλαμβάνει ένα ενσωματωμένο- διατηρήστε το dashboard προσβάσιμο στο /hangfire:

  • Εργασίες: Δείτε όλα τα quuedΜSK1 επεξεργασίας, και ολοκληρωμένες θέσεις εργασίας
  • επαναλαμβανόμενες θέσεις εργασίας: Διαχειριστείτε τους προγραμματιστές ροής εργασίας μας
  • Αποχώρηση: Δείτε και αποσύρετε ανεπιτυχείς θέσεις εργασίας
  • Υπηρεσίες: Παρακολούθηση διακομιστών Hangfire

Εξασφάλιση του Dashboard

app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[]
    {
        new HangfireAuthorizationFilter()
    }
});

public class HangfireAuthorizationFilter : IDashboardAuthorizationFilter
{
    public bool Authorize(DashboardContext context)
    {
        var httpContext = context.GetHttpContext();

        // Only allow authenticated users
        return httpContext.User.Identity?.IsAuthenticated == true;
    }
}

Διαχείριση Triggers μέσω UI

Ας βεβαιωθείτε ότι οι εταιρείες ' προσθέτουν UI για τη δημιουργία και διαχείριση των εκκρεμόμενων εταιρειών :

[HttpPost("workflow/{id}/triggers")]
public async Task<IActionResult> CreateTrigger(
    string id,
    [FromBody] TriggerCreateRequest request)
{
    var workflow = await _context.WorkflowDefinitions
        .FirstOrDefaultAsync(w => w.WorkflowId == id);

    if (workflow == null)
        return NotFound();

    var trigger = new WorkflowTriggerStateEntity
    {
        WorkflowDefinitionId = workflow.Id,
        TriggerType = request.Type,
        ConfigJson = JsonSerializer.Serialize(request.Config),
        StateJson = "{}",
        IsEnabled = true
    };

    await _context.WorkflowTriggerStates.AddAsync(trigger);
    await _context.SaveChangesAsync();

    return Json(new { success = true, triggerId = trigger.Id });
}

public class TriggerCreateRequest
{
    public string Type { get; set; } = string.Empty; // Schedule, ApiPoll
    public object Config { get; set; } = new();
}

ΟΙ ΣΥΜΜΕΤΟΧΕΣ

<div class="card bg-base-100 shadow-xl">
    <div class="card-body">
        <h2 class="card-title">⏰ Add Trigger</h2>

        <div class="form-control">
            <label class="label">Trigger Type</label>
            <select class="select select-bordered" x-model="triggerType">
                <option value="Schedule">Schedule</option>
                <option value="ApiPoll">API Poll</option>
            </select>
        </div>

        <!-- Schedule Config -->
        <template x-if="triggerType === 'Schedule'">
            <div class="space-y-4">
                <div class="form-control">
                    <label class="label">Interval</label>
                    <div class="flex gap-2">
                        <input type="number"
                               x-model="scheduleConfig.intervalValue"
                               class="input input-bordered flex-1" />
                        <select x-model="scheduleConfig.intervalType"
                                class="select select-bordered">
                            <option value="minutes">Minutes</option>
                            <option value="hours">Hours</option>
                            <option value="days">Days</option>
                        </select>
                    </div>
                </div>
            </div>
        </template>

        <!-- API Poll Config -->
        <template x-if="triggerType === 'ApiPoll'">
            <div class="space-y-4">
                <div class="form-control">
                    <label class="label">API URL</label>
                    <input type="url"
                           x-model="apiConfig.url"
                           class="input input-bordered"
                           placeholder="https://api.example.com/data" />
                </div>

                <div class="form-control">
                    <label class="label">Poll Interval (seconds)</label>
                    <input type="number"
                           x-model="apiConfig.intervalSeconds"
                           class="input input-bordered"
                           value="300" />
                </div>
            </div>
        </template>

        <button @click="createTrigger()" class="btn btn-primary mt-4">
            Create Trigger
        </button>
    </div>
</div>

Ρεάλ-World Example Workflow

Ας εξασφαλίσουμε ότι το ' επιχειρεί να δημιουργήσει μια πλήρη αυτόματη ροή εργασίας που λειτουργεί στο :

  1. Παίρνει το GitHub API για νέες εκδόσεις
  2. Ελέγξτε αν η έκδοση είναι νεότερη από ό, τι είδαμε
  3. Εισάγετε ένα μήνυμα
  4. (Μπορεί να στείλει ένα μήνυμα ηλεκτρονικού ταχυδρομείουM SK1 σε Slack, κλπMK3

Βήμα 1: Δημιουργήστε τη ροή εργασίας

{
  "name": "GitHub Release Monitor",
  "startNodeId": "parse-data",
  "nodes": [
    {
      "id": "parse-data",
      "type": "Transform",
      "name": "Extract Version",
      "inputs": {
        "operation": "json_parse",
        "data": "{{apiResponse}}"
      }
    },
    {
      "id": "log-release",
      "type": "Log",
      "name": "Log New Release",
      "inputs": {
        "message": "New release: {{tag_name}} - {{name}}",
        "level": "info"
      }
    }
  ],
  "connections": [
    {
      "sourceNodeId": "parse-data",
      "targetNodeId": "log-release"
    }
  ]
}

Βήμα 2: Δημιουργήστε το API Poll Trigger

{
  "type": "ApiPoll",
  "config": {
    "url": "https://api.github.com/repos/dotnet/aspnetcore/releases/latest",
    "intervalSeconds": 3600
  }
}

Τώρα αναμένεται, σχεδόν κάθε ώρα αναμένω, Hangfire θα αναμένει:

  1. Ανακαλύψτε το GitHub API
  2. Συγκρίνετε το hash περιεχομένου με την προηγούμενη έρευνα
  3. Εάν αλλάξει, μπορεί να εκτελέσει την ροή εργασίας
  4. Η ροή εργασίας διακόπτει το JSON και καταγράφει τις πληροφορίες κυκλοφορίας

Παρακολούθηση και παρατήρηση

Λόγκινγκ

Όλες οι εκτελέσεις ροής εργασίας είναι συνδεδεμένες:

_logger.LogInformation(
    "Workflow {WorkflowId} execution {ExecutionId} completed in {Duration}ms with status {Status}",
    execution.WorkflowId,
    execution.Id,
    execution.DurationMs,
    execution.Status);

Μετρητά

Μπορούμε να προσθέσουμε μετρήσεις Prometheus:

private static readonly Counter WorkflowExecutions = Metrics
    .CreateCounter("workflow_executions_total",
        "Total workflow executions",
        new CounterConfiguration
        {
            LabelNames = new[] { "workflow_id", "status" }
        });

// In execution service
WorkflowExecutions
    .WithLabels(workflow.Id, execution.Status.ToString())
    .Inc();

Προειδοποιήσεις

Δημιουργία προειδοποιήσεων για: επιχειρήσεις

  • Αποτυχημένες ροές εργασίας (Στάση ΜSK1Αποτυχημένη)
  • Οι ροές εργασίας διαρκούν πολύ
  • Η αποτυχία της έρευνας API
  • Πυροβολισμοί που έχουν φτάσει στο ' ανέφεραν ότι εκτοξεύτηκαν σε αναμενόμενο χρονικό διάστημα

Αξιολόγηση των επιδόσεων

Βάση δεδομένων Load

Με πολλές ροές εργασίας που διερευνώνται συχνά, το φορτίο δεδομένων , μπορεί να είναι σημαντικό για το :

Λύση: Batch queries

// Instead of querying per trigger
var triggers = await _context.WorkflowTriggerStates
    .Include(t => t.WorkflowDefinition)
    .Where(t => t.IsEnabled && t.TriggerType == "ApiPoll")
    .AsNoTracking() // Read-only
    .ToListAsync();

Περιορισμός ρυθμού API

Όταν η έρευνα εξωτερικών APIs:

Λύση: Exponential backoff

catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.TooManyRequests)
{
    // Back off
    var retryAfter = response.Headers.RetryAfter?.Delta ?? TimeSpan.FromMinutes(5);
    state["backoffUntil"] = DateTime.UtcNow.Add(retryAfter).ToString("O");
}

Προχωρημένα χαρακτηριστικά

Προϋπολογιστικά Triggers

Εκτοξεύεται μόνο εάν πληρούνται ορισμένες προϋποθέσεις:

public class ConditionalTriggerConfig : ApiPollTriggerConfig
{
    public string? Condition { get; set; } // e.g., "{{stars}} > 1000"
}

Τρίγκερ εξάρτηση

Η εκκίνηση της αλυσίδας - μία ροή εργασίαςΗ ' ολοκλήρωση προκάλεσε άλληη :

// After workflow completes
if (execution.Status == WorkflowExecutionStatus.Completed)
{
    var dependentTriggers = await _context.WorkflowTriggerStates
        .Where(t => t.TriggerType == "WorkflowComplete" &&
                    t.ConfigJson.Contains(execution.WorkflowId))
        .ToListAsync();

    foreach (var trigger in dependentTriggers)
    {
        await _executionService.ExecuteWorkflowAsync(
            trigger.WorkflowDefinition.WorkflowId,
            execution.OutputData,
            $"Triggered by {execution.WorkflowId}");
    }
}

Δοκιμή θέσεων εργασίας Hangfire

Μονάδα δοκιμής θέσεις εργασίας σας:

[Fact]
public async Task ExecuteScheduledWorkflows_ShouldExecuteWhenIntervalPassed()
{
    // Arrange
    var mockContext = CreateMockContext();
    var mockExecutionService = new Mock<IWorkflowExecutionService>();
    var job = new WorkflowSchedulerJob(mockContext.Object,
        mockExecutionService.Object, Mock.Of<ILogger>());

    // Act
    await job.ExecuteScheduledWorkflowsAsync();

    // Assert
    mockExecutionService.Verify(s => s.ExecuteWorkflowAsync(
        It.IsAny<string>(),
        It.IsAny<Dictionary<string, object>>(),
        "Scheduler",
        It.IsAny<CancellationToken>()), Times.Once);
}

Συμπεράσματα

Έχουμε κατασκευάσει ένα ολοκληρωμένο σύστημα αυτοματισμού για το ! και οι ροές εργασίας μας μπορούν τώρα να φτάσουν στο :.

✅ Λειτουργεί με τα χρονοδιαγράμματα - HourlyΜSK1 καθημερινά, ή προσαρμοσμένα διαστήματα ✅ ΠΟΛΛ ΑΠΙ - Παρακολούθηση εξωτερικών υπηρεσιών για αλλαγές ✅ Track Κράτος - Θυμηθείτε τι είδαμε πριν από το ' ✅ ΑΥΤΟΚΙΝΗΤΙΚΟ ΠΡΟΓΡΑΜΜΑ 0 - Αντιμετώπιση μεταβατικών αποτυχιών ✅ Παρακολούθηση - Dashboard για όλες τις εργασίες ✅ κλίμακα - Hangfire χειρίζεται την ισορροπία φορτίου

Η πλήρης σειρά

Έχουμε κατασκευάσει ένα σύστημα ροής εργασίας - από το Scratch : επιχειρήσεις

  • ΕΠΙΧΕΙΡΗΣΗ 1: Εισαγωγή και αρχιτεκτονική
  • ΕΠΙΧΕΙΡΗΣΗ 2: Κεντρικός κινητήρας ροής εργασίας
  • ΕΠΙΧΕΙΡΗΣΗ 3: Επεξεργαστής Visual Workflow
  • ΕΠΙΧΕΙΡΗΣΗ 4: Ενσωμάτωση Hangfire ΜSK1Αυτή η ανάρτηση)

Εσείς τώρα είστε ευχαριστημένοι:

  • Ένας ισχυρός κινητήρας ροής εργασίας
  • Ένας όμορφος οπτικός συντάκτης
  • Αυτόματη εκτέλεση
  • Παρακολούθηση API
  • Πλήρης παρατηρητικότητα

Επόμενο άρθροΤι κάνει η ?

Ενδεχόμενη βελτίωση των επιχειρήσεων :

  • Webhooks: επισημαίνει τις ροές εργασίας του Trigger μέσω των τελικών σημείων HTTP
  • Ηλεκτρονικό κόμβο: Στείλτε μηνύματα ηλεκτρονικού ταχυδρομείου από ροές εργασίας
  • Κόντρα βάσης δεδομένων: βάσεις δεδομένων αναζήτησης
  • Οι κόμβοι: ενσωματώνονται με LLMs
  • Υπό- ραδιοτηλεοπτικές ροές: Συνδυάστε τις ροές εργασίας μαζί
  • Η αγορά ροής εργασίας: Μοιραστείτε πρότυπα ροής εργασίας

Κωδικός πηγής

Ολόκληρος ο κωδικός είναι διαθέσιμος στο αποθεματικό:

  • ΕΠΙΧΕΙΡΕΥΣΗ ΣΤΗΝ ΑΓΟΡΑ ΠΟΥ ΔΙΑΔΙΚΑΣΤΕ Mostlylucid.SchedulerService/Jobs/
  • Μοντέλα ροής εργασίας: Mostlylucid.Workflow.Shared/
  • Ο κινητήρας ροής εργασίας: Mostlylucid.Workflow.Engine/

Σας ευχαριστώ για την παρακολούθηση αυτής της σειράς! Ευτυχισμένη οικοδόμηση ροής εργασίαςΜSK1

logo

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