Navážeme na projekt, který vznikl v LAB1.
-
Založíme a nasadíme Azure WebJob:
- V Solution Exploreru přidejte do solution nový projekt typu Console Application (.NET6).
- Klikneme v Solution Exploreru pravým tlačítkem na nový projekt a vybereme volbu Publish.
- Zvolíme jako target Azure.
- V dalším kroku zvolíme specific target Azure WebJobs:
- V dalším kroku vybereme Azure App Service, kterou jsme si vytvořili pro website z LAB1.
- Dokončíme průvodce, vznikne publish-profile pro nasazování web-jobu:
- V podrobném nastavení profilu změníme typ jobu na Continuous:
-
Založíme Azure SQL databázi.
SQL Databázi by bylo možné přidat k publish-profilu WebJobu jako Service Dependency, kde by nám to připravilo i konfigurační volby do hostující Azure App Service, nicméně my si nyní vyzkoušíme další z cest zakládání Azure resources, a to přímo z Azure Portalu:
- Jdeme na Azure Portal (https://portal.azure.com) a najdeme si resource group, do které jsme nasadili naší website.
- Vytvoříme nový resource typu Azure SQL (v UI pod názvem SQL Database). Zvolíme nejlevnější Compute + Storage variantu Basic 5 DTU (4.21€/mo).
- V rámci zakládání databáze potřebujeme založit nový SQL server.
- V dalších krocích průvodce si prostudujte možné volby při zakládání SQL, všude můžete ponechat výchozí nastavení, aktivujeme pouze volbu Allow Azure services and resources to access this server (kdo by nyní zapomněl, najde ji později v sekci Firewalls and virtual networks v nastavení založeného Azure SQL Serveru).
-
Připojíme se k Azure SQL z Visual Studia a připravíme schéma databáze:
- Ve Visual Studiu otevřete SQL Server Object Explorer (najdete např. přes Quick Search).
- Přidejte si mezi registrované servery server právě vyvořený:
- V průběhu bude potřeba přidat firewall pravidlo:
- Otevřte si New Query vůči založené databázi a spusťte následující skript pro založení tabulky EmailQueue:
CREATE TABLE dbo.EmailQueue ( ID int PRIMARY KEY NOT NULL IDENTITY (1, 1), Recipient nvarchar(MAX) NOT NULL, Subject nvarchar(400) NOT NULL, Body nvarchar(MAX) NOT NULL, Created datetime NOT NULL, Sent datetime NULL )
-
WebJobu dáme úkol hlídat obsah emailové fronty v SQL databázi a posílat maily, které se tam objeví
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Data.SqlClient;
using System.Net;
using System.Net.Mail;
public class Program
{
static async Task Main(string[] args)
{
// Setup Host
var host = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration(app =>
{
app.AddJsonFile("appsettings.json");
app.AddEnvironmentVariables();
})
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.Build();
await host.RunAsync();
}
}
public class Worker : IHostedService, IDisposable
{
private PeriodicTimer? timer;
private readonly IConfiguration configuration;
public Worker(IConfiguration configuration)
{
this.configuration = configuration;
}
public async Task StartAsync(CancellationToken stoppingToken)
{
Console.WriteLine("Worker started...");
timer = new PeriodicTimer(TimeSpan.FromSeconds(10));
do
{
await ProcessEmailQueueAsync(stoppingToken);
Console.WriteLine("Waiting for next tick...");
}
while (await timer.WaitForNextTickAsync(stoppingToken));
}
private async Task ProcessEmailQueueAsync(CancellationToken stoppingToken)
{
Console.WriteLine("Checking for new e-mails to be sent...");
using var conn = new SqlConnection(configuration.GetConnectionString("MyDatabase"));
await conn.OpenAsync(stoppingToken);
var cmd = new SqlCommand("SELECT * FROM EmailQueue WHERE Sent IS NULL", conn);
using var reader = await cmd.ExecuteReaderAsync(stoppingToken);
while (await reader.ReadAsync(stoppingToken))
{
Console.WriteLine($"Email ID:{reader["ID"]} found...");
string recipients = reader["Recipient"].ToString() ?? throw new InvalidOperationException("Recipient not provided");
string? subject = reader["Subject"].ToString();
string? body = reader["Body"].ToString();
int mailId = Convert.ToInt32(reader["ID"]);
await SendMailAsync(recipients, subject, body, mailId, stoppingToken);
}
}
private async Task SendMailAsync(string recipients, string? subject, string? body, int mailId, CancellationToken stoppingToken)
{
using var smtpClient = new SmtpClient();
smtpClient.Host = configuration.GetValue<string>("MailSettings:SmtpHost");
smtpClient.Credentials = new NetworkCredential(configuration.GetValue<string>("MailSettings:SmtpUsername"), configuration.GetValue<string>("MailSettings:SmtpPassword"));
await smtpClient.SendMailAsync(
from: configuration.GetValue<string>("MailSettings:SmtpFrom"),
recipients: recipients,
subject: subject,
body: body,
stoppingToken);
// mark mail as sent
using var conn = new SqlConnection(configuration.GetConnectionString("MyDatabase"));
await conn.OpenAsync(stoppingToken);
var cmd = new SqlCommand("UPDATE EmailQueue SET Sent = GETDATE() WHERE Id = @ID", conn);
cmd.Parameters.AddWithValue("@Id", mailId);
await cmd.ExecuteNonQueryAsync();
Console.WriteLine($"Email ID:{mailId} sent...");
}
public Task StopAsync(CancellationToken stoppingToken)
{
return Task.CompletedTask;
}
public void Dispose()
{
timer?.Dispose();
}
}
Výše uvedený kód je psán formou čisté konzolové aplikace (s využitím IHost
). Prostuduj možnosti využití Azure WebJobs SDK: https://docs.microsoft.com/en-us/azure/app-service/webjobs-sdk-how-to
Zdrojové kódy WebJobu se odkazují na konfigurační volby, které potřebujeme aplikaci poskytnout.
-
Pro lokální testování a vývoj je možné použít přímo
appSettings.json
soubor v projektu. Příklad:{ "ConnectionStrings": { "MyDatabase": "Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;" }, "MailSettings": { "SmtpHost": "mail.havit.local", "SmtpFrom": "[email protected]", "SmtpUsername": null, "SmtpPassword": null } }
-
Abychom nemuseli produkční konfiguraci udržovat v repozitáři zdrojových kódů nebo jinak zařizovat při nasazování (např. skrze
appSettings.Production.json
), využijeme možnost injectování konfigurace přímo v Azure App Service v sekci Configuration (první záložka Application Settings):Na některé z pozdějších lekcí uvidíte ještě možnosti služby Azure Key Vault, která slouží k bezpečnému uchovávání secrets pro aplikace.
- Nyní můžeme WebJob vypublikovat (pravým tlačítkem na project v Solution Exploreru, volba Publish).
- WebJob si můžeme zkontrolovat v Azure Portal v cílové Azure App Service, v sekci WebJobs:
- Pro vložení testovacího záznamu do DB můžeme použít Query Exploreru v Azure Portale, který je u Azure SQL Database k dispozici:
- Za pozozrnost stojí volba Logs, kde si můžete prohlížet konzolový výstup (stdout) WebJob aplikace.
- Sofistikovanější dashboard WebJobů je možné získat při použití WebJobs SDK, popř. lze využít i alternativní job-schedullery (Hangfire, Quartz.NET, atp.)