Azure Functions Time Trigger İle SqlDB’yi Monitor Etme
Selamlar,
Bu makalenin konusu, birçok developer’ın ihtiyaç duyabileceği zamanlı yapılan işlerin yönetimidir. Örnek amaçlı, Sql Customer bir DB’yi dakikada bir dinleyen ve yeni gelen kayıtları onaylayan bir senaryo ele alıncaktır. Bunun için yapılabilecek bir çok yöntem vardır. Örneğin Localde yapılacak bir windows servisin, ayrıca yönetim, bakım, güvenlik ve yedekleme gibi farklı maliyetleri olacaktır. Azure Timer Trigger functions ile kullandığın kadar öde mantığı ve serverless denilen, cloud’a at unut yaklaşımı, tam bir developer dostudur. Bu makalede Visual Studio 2017 15.9.6’ı ile Azure function kullanılcaktır.
New Project / Cloud / Azure Functions seçilerek yeni bir proje oluşturulur.
Açılan menüden, Azure Functions templatelerinden Timer trigger aşağıdaki gibi seçilir.
Buradaki Schedule “0*/5****” => Her 5 dakikada bir çalış anlamına gelmektedir. Gelin CRON expressions olarak geçen bu tanımlamayı, daha detaylı olarak inceleyelim.
- *{second} *{minute} *{hour} *{day} *{month} *{day of the week} : Herbir yıldızın anlamı yanda görüldüğü gibidir.
- “*/30 * * * * *” =Z Her 15saniyede bir çalışır. (08:00:00; 08:00:15; 08:00:30; … 09:03:30; 09:03:45; 09:04:00)
- “0 * * * * *” => Her dakika çalışır. (07:00:00; 07:01:00; 07:02:00)
- “0 0 * * * *” => Her saat çalışır. (07:00:00; 08:00:00; 09:00:00)
- “0 0 */3* * *” => Her 3 saatte bir çalışır. (06:00:00; 9:00:00; 12:00:00)
- “0 0 9 -17 * * *” => Saat 8 ile 17 arası her saat çalışır. (09:00:00; 10:00:00; … 17:00:00; 09:00:00)
- “0 0 0 * * *” => Günde 1 defa çalışır. (Şubat 1, 2019 00:00:00; Şubat 2, 2019 00:00:00)
- “0 0 18 * * *” => Her gun saat 18’de çalışır.(Şubat 1, 2019 18:00:00; Şubat 2, 2019 18:00:00)
- “0 0 * * * 1-5” => Hafta ici çalışır.
- “0 0 6 * * MON” => Her pazartesi saat 6’da çalışır. (Şubat 6 (PZR), 2019 06:00:00; Şubat 13 (PZR), 2019 06:00:00)
- “0 0 0 * * SAT,SUN” => Her Cumartesi-Pazar çalışır.
Uygulama oluşturulduğu zaman, karşımıza aşağıdaki gibi bir ekran gelir. Aşağıdaki uygulama, dakikada bir çalışmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using System; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Host; namespace CustomerDbListen { public static class Function1 { [FunctionName("checkCustomerDB")] log.Info($"Customer Check Timer trigger function executing at: {DateTime.Now}"); { log.Info($"C# Timer trigger function executed at: {DateTime.Now}"); } } } |
Şimdi sıra geldi ilgili Database’i dinlemeye. Bunun için “System.Data.SqlClient” kütüphanesi Nuget’den indirilir.
Proje üzerindeki “Dependencies” sağ tıklanıp, “Add Reference…” denildikten sonra, “System.Configuration” kütüphanesi projeye eklenir.
Ayrıca, projede yok ise “System.Threading.Tasks” kütüphanesi de eklenmelidir.
local.settings.json: Bir çeşit App.config dosyasıdır. “SqlConnection” keyword’ü, aşağıdaki gibi eklenir. Böylece Local Sql connection tanımlanmış olunur.
1 2 3 4 5 6 7 8 |
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "AzureWebJobsDashboard": "UseDevelopmentStorage=true", "SqlConnection": "Data Source=.; Initial Catalog=Customer; Integrated Security=True; MultipleActiveResultSets=True;" } } |
“Customer” adındaki Sql database içinde, yukarıda görülen “Candidate” tablosu, aşağıdaki script ile oluşturulur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
USE [Customer] GO /****** Object: Table [dbo].[Candidate] Script Date: 3.02.2019 16:39:14 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[Candidate]( [Id] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](50) NOT NULL, [Surname] [nvarchar](50) NOT NULL, [Age] [int] NOT NULL, [Gender] [nchar](10) NOT NULL, [isApprove] [bit] NOT NULL ) ON [PRIMARY] GO ALTER TABLE [dbo].[Candidate] ADD CONSTRAINT [DF_Candidate_isApprove] DEFAULT ((0)) FOR [isApprove] GO |
Azure functions çalıştırıldığında, aşağıdaki gibi bir ekran ile karşılaşılır.
Run() methodunda connection string aşağıdaki gibi alınır.
1 2 |
var str = ConfigurationManager .AppSettings["SqlConnection"]; |
DB’deki onaylanmamış tüm kayıtlar, aşağıdaki gibi asenkron olarak çekilir. Ve ekrana şimdilik olduğu gibi basılır.
- “await cmd.ExecuteReaderAsync()” : Aşağıda görüldüğü gibi “select query” asenkron olarak çalıştırılmaktadır.
- “while (await reader.ReadAsync())” : Yine herbir kayıt, asenkron olarak tek tek okunmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using (SqlConnection conn = new SqlConnection(str)) { conn.Open(); var query = "SELECT * FROM candidate WHERE isApprove = 0"; using (SqlCommand cmd = new SqlCommand(query, conn)) { var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { log.Info($"{reader["Id"]}-{reader["Name"]}-{reader["SurName"]}-{reader["Age"]}-{reader["Gender"]} will approve"); } } } |
Not: While iteration anında, çekilen herbir kaydın üzerinde güncelleme işleminin yapılabilmesi için, connection stringde “MultipleActiveResultSets=True;” tanımlamasının, yapılması gerekmektedir. Aksi takdirde “System.Data: There is already an open DataReader associated with this Command which must be closed first.” hatası alınmaktadır.
- “var queryUpdate = $”Update candidate Set isApprove=1 where Id={reader[“Id”]}”” : Onaylanmamış herbir kaydı, Id’sine göre güncelleyen sql query tanımlanır.
- “var rows = await cmdUpdate.ExecuteNonQueryAsync()” : Herbir kayıt asenkron olarak güncellenerek onaylanır.
- “log.Info($”{reader[“Name”]}-{reader[“SurName”]} is updated!”)” : Güncellenen kayıt ekrana basılır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using (SqlCommand cmd = new SqlCommand(query, conn)) { var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { log.Info($"{reader["Id"]}-{reader["Name"]}-{reader["SurName"]}-{reader["Age"]}-{reader["Gender"]} will approve"); var queryUpdate = $"Update candidate Set isApprove=1 where Id={reader["Id"]}"; using (SqlCommand cmdUpdate = new SqlCommand(queryUpdate, conn)) { var rows = await cmdUpdate.ExecuteNonQueryAsync(); log.Info($"{reader["Name"]}-{reader["SurName"]} is updated!"); } } } |
DB’ye örnek amaçlı 2 kayıt eklenmiştir:
Proje çalıştırıldıktan sonra aşağıdaki gibi bir ekran ile ilgili kayıtlar onaylanır.
DB’de son durum aşağıdaki gibidir:
Function1.cs(Tümü): Kodun son hali aşağıdaki gibidir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
using System; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Host; using System.Configuration; using System.Data.SqlClient; using System.Threading.Tasks; namespace CustomerDbListen { public static class Function1 { [FunctionName("checkCustomerDB")] public static async Task Run([TimerTrigger("*/10 * * * * *")]TimerInfo myTimer, TraceWriter log) { try { log.Info($"Customer Check Timer trigger function executing at: {DateTime.Now}"); var str = ConfigurationManager .AppSettings["SqlConnection"]; using (SqlConnection conn = new SqlConnection(str)) { conn.Open(); var query = "SELECT * FROM candidate WHERE isApprove = 0"; using (SqlCommand cmd = new SqlCommand(query, conn)) { var reader = await cmd.ExecuteReaderAsync(); while (await reader.ReadAsync()) { log.Info($"{reader["Id"]}-{reader["Name"]}-{reader["SurName"]}-{reader["Age"]}-{reader["Gender"]} will approve"); var queryUpdate = $"Update candidate Set isApprove=1 where Id={reader["Id"]}"; using (SqlCommand cmdUpdate = new SqlCommand(queryUpdate, conn)) { var rows = await cmdUpdate.ExecuteNonQueryAsync(); log.Info($"{reader["Name"]}-{reader["SurName"]} is updated!"); } } } } } catch (Exception ex) { log.Info("Error:" + ex.Message); } } } } |
Sıra geldi tüm projeyi ve SqlDb’yi, Azure’a publish etmeye.
Azure üzerinde bir Azure Functions’ın kurulabilmesi için, Azure Storage Account’a ihtiyaç vardır. All resource’dan Storage Account aşağıdaki gibi aranır.
Azure üzerinde Storage Account Oluşturma:
- All services/Storage Account seçilir.
- Storage Accounts penceresinde Add seçilir.
- Create new’a basılarak isim verilir.
- Daha sonra yukarıda görüldüğü gibi Storage account name, Location (Türkiyeye en yakın) North Europe, Performance, Account kind ve Replication alanları seçilir.
Sıra geldi SqlDb’nin yaratılmasına
Azure üzerinde SqlDB Oluşturma:
1-)Azure Serviceslerden Sql database aşağıdaki gibi seçilir.
2-)Açılan ekrandan Create sql database butonuna tıklanır.
3-) Aşağıda görüldüğü gibi ilgili alanlar doldurulur.
- Database name: Adı
- Resource group: Demin yarattığımız Storage Account burada seçilir.
- Select source: Oluşturma şekli boş.
- Server: Yeni bir Sql server yaratılır.
- Server name: Adı (blogbora)
- Admin ve Password tanımlanır.
- Location: Konumu (North Europe).
- Allow Azure service to access server. Azure üzerindeki servislerin bu DB’ye erişmesine izin verilir. Normalde uzaktan erişimde IP tanımlaması yapılması gerekmektedir.
- Fiyatlama ve Collation(fiyatlama) seçildikten sonra DB oluşturulur.
DB oluştuktan sonra, aşağıdaki gibi bir ekran ile karşılaşılır.
İlgili DB’ye evden erişebilmek için:
1-) DB(Customer) detayda “Set server firewall” ayarlarına girilir.
2-) Add client IP tuşuna basılarak, bulunulan local IP’ye izin verilir.
3-) Save tuşuna basılarak, kaydedilir.
Sql DB’ye uzaktan bağlanma:
Customer DB detayına gidilip, “Show database connection strings” butonuna basılarak, connection string alınır ve Localdeki Sql Server Manager ile bağlanılır.
Azure üzerindeki Sql server’a Local’den aşağıdaki gibi bağlanılır:
Şimdi sıra geldi ilgili tabloyu Azure üzerinde de oluşturmaya. Makalenin başında tanımlanan tablo script’i, Azure Server üzerinde çalıştırılarak, ilgili tablo aşağıda görüldüğü gibi oluşturulur.
Azure Customer DB’si üzerinde, Candidate tablosu sağ tıklanarakseçilip, örnek 2 kaydın girilmesi unutulmamalıdır :)
Local’de çalışan Azure Function’ın connection string’inin değiştirilerek, Azure üzerindeki SqlDb’nin yolunun girilmesi gerekmektedir.
local.settings.json: Aşağıdaki gibi değiştirilir.
1 2 3 4 5 6 7 8 9 |
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "AzureWebJobsDashboard": "UseDevelopmentStorage=true", //"SqlConnection": "Data Source=.; Initial Catalog=Customer; Integrated Security=True; MultipleActiveResultSets=True;" "SqlConnection": "Data Source=tcp:blogbora.database.windows.net,1433; Initial Catalog=Customer; User ID=****; Password=****; MultipleActiveResultSets=True;" } } |
Şu anki hali ile, Localde çalışan bir Timer Trigger Azure Functions’ı, Azure üzerinde bulunan Sql bir DB’ye bağlanılarak, çalıştırıldığında aşağıdaki gibi bir ekranı ile karşılaşılır:
Geldik bir makalenin daha sonuna. Bu makalede, iş hayatında bolca karşınıza çıkabilecek olan, belli bir zamanda çalışacak joblar, ele alınmıştır. Bu gibi durumlara, klasik yöntemler olan, Windows Service ya da Console Applicationlar ile değil, Azure üzerinde çalışan Timer Trigger bir Functions ile çözüm aranmıştır. Azure tarafında eğer bu tarz servisler oluşturulmasa idi, çalışacak Windows Servis için ayrı bir VM makinanın oluşturulması ve bu neden ile maliyetlerin artması beklenecekti.
Bir sonraki makalede, Azure Time Trigger Function’ı Azure’a publish etmeyi, monitoring’i ve bir takım dikkat edilmesi gereken durumları hep beraber inceleyeceğiz.
Makalenin devamında görüşmek üzere hepinize hoşçakalın.
Source :
Son Yorumlar