.NET kullanarak Azure Kuyruk Depolamaya başlayın
Genel Bakış
Azure kuyruğu Depolama, uygulama bileşenleri arasında bulut iletileri sağlar. Uygulamalar ölçeklendirme için tasarlanırken uygulama bileşenleri genellikle birbirinden bağımsız olarak ölçeklendirilebilecek şekilde ayrılır. Queue Depolama, uygulama bileşenleri arasında, bulutta, masaüstünde, şirket içi sunucuda veya bir mobil cihazda çalışıyor olup olmadıkları gibi zaman uyumsuz mesajlaşma sağlar. sıra Depolama, zaman uyumsuz görevlerin yönetilmesini ve süreç iş akışlarının oluşturulmasını destekler.
Bu öğretici hakkında
bu öğreticide, Azure kuyruğu Depolama kullanarak bazı yaygın senaryolar için .net kodunun nasıl yazılacağı gösterilmektedir. Kapsanan senaryolara kuyruk oluşturma ve silme ile kuyruk iletileri ekleme, okuma ve silme dahildir.
Tahmini tamamlanma süresi: 45 dakika
Önkoşullar
Kuyruk depolama nedir?
Azure Kuyruk depolama, HTTP veya HTTPS kullanan kimlik doğrulaması yapılmış çağrılar aracılığıyla dünyanın her yerinden erişilebilen çok sayıda iletinin depolanması için bir hizmettir. Tek bir kuyruk iletisinin boyutu 64 KB’ye kadar olabilir ve bir kuyrukta, depolama hesabının toplam kapasite sınırına kadar milyonlarca ileti bulunabilir. Kuyruk depolama genellikle zaman uyumsuz olarak işlenecek iş biriktirme listesi oluşturmak için kullanılır.
Kuyruk hizmeti kavramlar
Azure Kuyruk hizmeti aşağıdaki bileşenleri içerir:

Depolama hesabı: Tüm Azure depolama erişimi bir depolama hesabı üzerinden yapılır. Depolama hesapları hakkında daha fazla bilgi için bkz. depolama hesabına genel bakış.
Kuyruk: Kuyrukta bir dizi ileti vardır. Tüm iletiler bir kuyrukta olmalıdır. Kuyruk adının tamamen küçük harfli olması gerektiğini unutmayın. Kuyrukların adlandırılması hakkında daha fazla bilgi için bkz. Kuyrukları ve Meta Verileri Adlandırma.
İleti: İleti, biçimi ne olursa olsun en çok 64 KB büyüklüktedir. Bir iletinin kuyrukta kalabileceği en uzun süre 7 gündür. Sürüm 2017-07-29 veya üzeri için, en fazla yaşam süresi herhangi bir pozitif sayı veya iletinin süresinin dolmadığını belirten-1 olabilir. Bu parametre atlanırsa, varsayılan yaşam süresi yedi gündür.
URL biçimi: Kuyruklar şu URL biçimi kullanılarak adreslenebilir: http://
<storage account>. Queue.Core.Windows.net/<queue>Aşağıdaki URL diyagramdaki bir kuyruğun adresini belirtir:
http://myaccount.queue.core.windows.net/incoming-orders
Azure depolama hesabı oluşturma
İlk Azure depolama hesabınızı oluşturmanın en kolay yolu Azure Portalı’nı kullanmaktır. Daha fazla bilgi için bkz. Depolama hesabı oluşturma.
Azure PowerShell, Azure CLI veya .NET için Azure Depolama Kaynak Sağlayıcısı’nı da kullanarak Azure depolama hesabı oluşturabilirsiniz.
Şu anda Azure 'da bir depolama hesabı oluşturmayı tercih ediyorsanız, kodunuzu yerel bir ortamda çalıştırmak ve test etmek için Azurite depolama öykünücüsünü da kullanabilirsiniz. Daha fazla bilgi için bkz. Yerel Azure depolama geliştirmesi Için Azurite öykünücüsünü kullanma.
Geliştirme ortamınızı kurma
Ardından, geliştirme ortamınızı Visual Studio’da ayarlayın; böylece bu kılavuzdaki kod örneklerini denemeye hazır olursunuz.
Windows konsol uygulaması projesi oluşturma
Visual Studio'da yeni bir Windows konsol uygulaması oluşturun. aşağıdaki adımlarda Visual Studio 2019 ' de bir konsol uygulamasının nasıl oluşturulacağı gösterilmektedir. Adımlar Visual Studio’nun diğer sürümlerinde de benzerdir.
- dosya > yeni > Project seçin
- Platform > Windows seçin
- Konsol uygulaması Seç (.NET Framework)
- İleri’yi seçin
- Project adı alanına uygulamanız için bir ad girin
- Oluştur’u seçin
Bu öğreticideki tüm kod örnekleri konsol uygulamanızın Program.cs dosyasındaki Main() yöntemine eklenebilir.
azure Depolama istemci kitaplıklarını, azure bulut hizmeti veya web uygulaması, masaüstü ve mobil uygulamalar dahil olmak üzere herhangi bir türde .net uygulamasında kullanabilirsiniz. Bu kılavuzda, sadeleştirmek için konsol uygulaması kullanmaktayız.
Gereken paketleri yüklemek için NuGet kullanma
Bu öğreticiyi tamamlayabilmeniz için projenizde aşağıdaki dört pakete başvurmanız gerekir:
- .Net Için Azure. Core kitaplığı: Bu paket, modern .net Azure SDK 'sı istemci kitaplıkları için paylaşılan temel elemanlar, soyutlamalar ve yardımcıları sağlar.
- Azure. Depolama. .net için ortak istemci kitaplığı: bu paket, diğer Azure Depolama istemci kitaplıkları tarafından paylaşılan altyapıyı sağlar.
- Azure. Depolama. .net için kuyruklar istemci kitaplığı: bu paket, bir istemci tarafından erişilebilecek iletileri depolamak için Azure kuyruğu Depolama birlikte çalışmaya izin verebilir.
- .Net Için System. Configuration. ConfigurationManager kitaplığı: Bu paket, istemci uygulamaları için yapılandırma dosyalarına erişim sağlar.
bu paketleri almak için NuGet kullanabilirsiniz. Şu adımları izleyin:
- Çözüm Gezgini' de projenize sağ tıklayın ve NuGet paketlerini yönet' i seçin.
- Gözatmayı Seç
- çevrimiçi olarak arayın
Azure.Storage.Queuesve Azure Depolama istemci kitaplığı 'nı ve bağımlılıklarını yüklemek için yüklemeyi seçin. Bu, Azure 'ı da yükler. Depolama. Sıra kitaplığının bağımlılıkları olan ortak ve Azure. Core kitaplıkları. - Çevrimiçi olarak arayın
System.Configuration.ConfigurationManagerve Configuration Manager yüklemek Için yüklemeyi seçin.
Hedef ortamınızı saptama
Bu kılavuzdaki örnekleri çalıştırmak için iki ortam seçeneğiniz vardır:
- Kodunuzu buluttaki bir Azure Storage hesabına karşı çalıştırabilirsiniz.
- Kodunuzun Azurite depolama öykünücüsünde karşı çalışmasını sağlayabilirsiniz. azurite, bulutta bir Azure Depolama hesabına öykünen yerel bir ortamdır. Azurite, uygulamanız geliştirme aşamasında olduğunda kodunuzda test ve hata ayıklama için ücretsiz bir seçenektir. Öykünücü iyi bilinen hesabı ve anahtarı kullanır. daha fazla bilgi için bkz. yerel Azure Depolama geliştirme ve test için azurite öykünücüsünü kullanma.
Not
Azure Storage ile ilişkili maliyetlerin oluşmasını önlemek için depolama öykünücüsünü hedefleyebilirsiniz. ancak, bulutta bir Azure Depolama hesabını hedeflemesini seçerseniz, bu öğreticiyi gerçekleştirmeye yönelik maliyetler göz ardı edilir.
Depolama Bağlantı dizenizi alın
depolama hizmetlerine erişim için uç noktaları ve kimlik bilgilerini yapılandırmak üzere bir depolama bağlantı dizesi kullanarak .net için Azure Depolama istemci kitaplıkları. Daha fazla bilgi için bkz. depolama hesabı erişim anahtarlarını yönetme.
Azure portalından kimlik bilgilerinizi kopyalama
Örnek kodun, depolama hesabınıza erişim için yetki vermesi gerekir. Yetki vermek için, depolama hesabınızın kimlik bilgilerini bir bağlantı dizesi şeklinde uygulamaya sağlarsınız. Depolama hesabınızın kimlik bilgilerini görüntülemek için:
Azure Portalgidin.
Depolama hesabınızı bulun.
Depolama hesabına genel bakışın Ayarlar bölümünde Erişim anahtarları’nı seçin. Hesap erişim anahtarlarınız ve her bir anahtar için tam bağlantı dizesi görüntülenir.
key1 bölümünde Bağlantı dizesi değerini bulun ve Kopyala düğmesine tıklayarak bağlantı dizesini kopyalayın. Sonraki adımda bir ortam değişkenine bağlantı dizesini ekleyeceksiniz.

bağlantı dizeleri hakkında daha fazla bilgi için bkz. Azure Depolama bağlantı dizesi yapılandırma.
Not
Depolama hesabı anahtarınız depolama hesabınızın kök parolasına benzer. Depolama hesabı anahtarınızı korumak için her zaman özen gösterin. Diğer kullanıcılara dağıtmaktan, sabit kodlamaktan ve başkalarının erişebileceği düz metin dosyasına kaydetmekten kaçının. Anahtarınızın tehlikede olduğunu düşünüyorsanız, Azure portalını kullanarak hesap anahtarınızı yeniden oluşturun.
Depolama bağlantı dizenizi korumanın en iyi yolu bir yapılandırma dosyasında tutmaktır. Bağlantı dizenizi yapılandırmak için, app.config dosyasını Visual Studio’daki Çözüm Gezgini'nden açın. <appSettings>Burada gösterilen öğenin içeriğini ekleyin. connection-stringPortaldaki depolama hesabınızdan kopyaladığınız değerle değiştirin:
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
<appSettings>
<add key="StorageConnectionString" value="connection-string" />
</appSettings>
</configuration>
Örneğin, yapılandırma ayarınız şunun gibi görünür:
<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=storagesample;AccountKey=GMuzNHjlB3S9itqZJHHCnRkrokLkcSyW7yK9BRbGp0ENePunLPwBgpxV1Z/pVo9zpem/2xSHXkMqTHHLcx8XRA==EndpointSuffix=core.windows.net" />
Azurite depolama öykünücüsünü hedeflemek için, iyi bilinen hesap adı ve anahtarıyla eşleşen bir kısayolu kullanabilirsiniz. Bu durumda, bağlantı dizesi ayarı şöyle olur:
<add key="StorageConnectionString" value="UseDevelopmentStorage=true" />
Using yönergeleri ekleme
Aşağıdaki using yönergelerini Program.cs dosyasının üst tarafına ekleyin:
using System; // Namespace for Console output
using System.Configuration; // Namespace for ConfigurationManager
using System.Threading.Tasks; // Namespace for Task
using Azure.Identity;
using Azure.Storage.Queues; // Namespace for Queue storage types
using Azure.Storage.Queues.Models; // Namespace for PeekedMessage
Depolama istemci kuyruğunu oluşturma
QueueClientsınıfı, Depolama sırada depolanan kuyrukları almanızı sağlar. Hizmet istemcisini oluşturma yöntemlerinden biri aşağıda verilmiştir:
//-------------------------------------------------
// Create the queue service client
//-------------------------------------------------
public void CreateQueueClient(string queueName)
{
// Get the connection string from app settings
string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
// Instantiate a QueueClient which will be used to create and manipulate the queue
QueueClient queueClient = new QueueClient(connectionString, queueName);
}
artık Depolama verileri okuyan ve kuyruğa veri yazan kodu yazmaya hazırsınız.
Bir kuyruk oluşturma
Bu örnek, bir sıranın nasıl oluşturulacağını gösterir:
//-------------------------------------------------
// Create a message queue
//-------------------------------------------------
public bool CreateQueue(string queueName)
{
try
{
// Get the connection string from app settings
string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
// Instantiate a QueueClient which will be used to create and manipulate the queue
QueueClient queueClient = new QueueClient(connectionString, queueName);
// Create the queue
queueClient.CreateIfNotExists();
if (queueClient.Exists())
{
Console.WriteLine($"Queue created: '{queueClient.Name}'");
return true;
}
else
{
Console.WriteLine($"Make sure the Azurite storage emulator running and try again.");
return false;
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}\n\n");
Console.WriteLine($"Make sure the Azurite storage emulator running and try again.");
return false;
}
}
Kuyruğa bir ileti yerleştirme
Mevcut bir sıraya bir ileti eklemek için SendMessage yöntemini çağırın. İleti bir dize (UTF-8 biçiminde) ya da bir bayt dizisi olabilir. Aşağıdaki kod bir sıra (yoksa) oluşturur ve bir ileti ekler:
//-------------------------------------------------
// Insert a message into a queue
//-------------------------------------------------
public void InsertMessage(string queueName, string message)
{
// Get the connection string from app settings
string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
// Instantiate a QueueClient which will be used to create and manipulate the queue
QueueClient queueClient = new QueueClient(connectionString, queueName);
// Create the queue if it doesn't already exist
queueClient.CreateIfNotExists();
if (queueClient.Exists())
{
// Send a message to the queue
queueClient.SendMessage(message);
}
Console.WriteLine($"Inserted: {message}");
}
Sonraki iletiye gözatın
Yöntemi çağırarak kuyruktaki iletilere, kuyruktan kaldırmadan göz atmayı sağlayabilirsiniz PeekMessages . Parametresi için bir değer geçirmezseniz maxMessages , varsayılan olarak bir ileti göz atın.
//-------------------------------------------------
// Peek at a message in the queue
//-------------------------------------------------
public void PeekMessage(string queueName)
{
// Get the connection string from app settings
string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
// Instantiate a QueueClient which will be used to manipulate the queue
QueueClient queueClient = new QueueClient(connectionString, queueName);
if (queueClient.Exists())
{
// Peek at the next message
PeekedMessage[] peekedMessage = queueClient.PeekMessages();
// Display the message
Console.WriteLine($"Peeked message: '{peekedMessage[0].Body}'");
}
}
Kuyruğa alınan iletinin içeriğini değiştirme
Kuyrukta yer alan bir iletinin içeriğini değiştirebilirsiniz. Eğer ileti bir iş görevini temsil ediyorsa, bu özelliği kullanarak iş görevinin durumunu güncelleştirebilirsiniz. Aşağıdaki kod kuyruk iletisini yeni içeriklerle güncelleştirir ve görünürlük zaman aşımını 60 saniye daha uzatır. Bu, ileti ile ilişkili işin durumunu kaydeder ve istemciye ileti üzerinde çalışmaya devam etmesi için bir dakika daha zaman verir. Bu tekniği, işlem adımı donanım veya yazılım arızası nedeniyle başarısız olursa baştan başlamak zorunda kalmadan, kuyruk iletilerinde çoklu adım iş akışlarını izlemek için kullanabilirsiniz. Genellikle bir yeniden deneme sayacı tutmanı gerekir ve bir ileti n seferden daha fazla yeniden denenirse, silebilirsiniz. Bu, her işlendiğinde bir uygulama hatası tetikleyen bir iletiye karşı koruma sağlar.
//-------------------------------------------------
// Update an existing message in the queue
//-------------------------------------------------
public void UpdateMessage(string queueName)
{
// Get the connection string from app settings
string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
// Instantiate a QueueClient which will be used to manipulate the queue
QueueClient queueClient = new QueueClient(connectionString, queueName);
if (queueClient.Exists())
{
// Get the message from the queue
QueueMessage[] message = queueClient.ReceiveMessages();
// Update the message contents
queueClient.UpdateMessage(message[0].MessageId,
message[0].PopReceipt,
"Updated contents",
TimeSpan.FromSeconds(60.0) // Make it invisible for another 60 seconds
);
}
}
Sonraki iletiyi sıradan çıkarma
İki adımda kuyruktan bir iletiyi sıradan çıkarma. ReceiveMessages' İ çağırdığınızda bir kuyruktaki sonraki iletiyi alırsınız. Öğesinden döndürülen bir ileti, ReceiveMessages Bu kuyruktan gelen diğer kod okuma iletileri için görünmez hale gelir. Varsayılan olarak bu ileti 30 saniye görünmez kalır. İletiyi kuyruktan kaldırma işleminin tamamlanabilmesi için, öğesini de çağırmanız gerekir DeleteMessage . Bir iletinin iki adımlı kaldırılma süreci, donanım veya yazılım arızasından dolayı kodunuzun bir iletiyi işleyememesi durumunda kodunuzun başka bir örneğinin aynı iletiyi alıp yeniden denemesini sağlar. DeleteMessageİleti işlendikten sonra kodunuz doğru şekilde çağırır.
//-------------------------------------------------
// Process and remove a message from the queue
//-------------------------------------------------
public void DequeueMessage(string queueName)
{
// Get the connection string from app settings
string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
// Instantiate a QueueClient which will be used to manipulate the queue
QueueClient queueClient = new QueueClient(connectionString, queueName);
if (queueClient.Exists())
{
// Get the next message
QueueMessage[] retrievedMessage = queueClient.ReceiveMessages();
// Process (i.e. print) the message in less than 30 seconds
Console.WriteLine($"Dequeued message: '{retrievedMessage[0].Body}'");
// Delete the message
queueClient.DeleteMessage(retrievedMessage[0].MessageId, retrievedMessage[0].PopReceipt);
}
}
Async-Await modelini ortak kuyruk Depolama apı 'leriyle kullanma
bu örnek, Async-Await deseninin ortak sıra Depolama apı 'leriyle nasıl kullanılacağını gösterir. Örnek, her yöntemin sonekine göre gösterildiği gibi, verilen yöntemlerin her birinin zaman uyumsuz sürümünü çağırır Async . Zaman uyumsuz bir yöntem kullanıldığında, Async-Await model, çağrı tamamlanana kadar yerel yürütmeyi askıya alır. Bu davranış geçerli iş parçacığının başka işler yapmasını sağlar ve böylece performans sorunlarını engellemeye yardımcı olur, uygulamanızın genel yanıt hızını iyileştirir. .NET ' te Async-Await deseninin kullanımı hakkında daha fazla bilgi için bkz. Async and await (C# and Visual Basic)
//-------------------------------------------------
// Perform queue operations asynchronously
//-------------------------------------------------
public async Task QueueAsync(string queueName)
{
// Get the connection string from app settings
string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
// Instantiate a QueueClient which will be used to manipulate the queue
QueueClient queueClient = new QueueClient(connectionString, queueName);
// Create the queue if it doesn't already exist
await queueClient.CreateIfNotExistsAsync();
if (await queueClient.ExistsAsync())
{
Console.WriteLine($"Queue '{queueClient.Name}' created");
}
else
{
Console.WriteLine($"Queue '{queueClient.Name}' exists");
}
// Async enqueue the message
await queueClient.SendMessageAsync("Hello, World");
Console.WriteLine($"Message added");
// Async receive the message
QueueMessage[] retrievedMessage = await queueClient.ReceiveMessagesAsync();
Console.WriteLine($"Retrieved message with content '{retrievedMessage[0].Body}'");
// Async delete the message
await queueClient.DeleteMessageAsync(retrievedMessage[0].MessageId, retrievedMessage[0].PopReceipt);
Console.WriteLine($"Deleted message: '{retrievedMessage[0].Body}'");
// Async delete the queue
await queueClient.DeleteAsync();
Console.WriteLine($"Deleted queue: '{queueClient.Name}'");
}
Dequeuing iletileri için ek seçenekler kullanma
İletilerin bir kuyruktan alınma şeklini iki yöntemle özelleştirebilirsiniz. İlk olarak toplu iletiler alabilirsiniz (en fazla 32). İkinci olarak daha uzun veya daha kısa bir görünmezlik süresi ayarlayarak kodunuzun her iletiyi tamamen işlemesi için daha az veya daha fazla zaman tanıyabilirsiniz.
Aşağıdaki kod örneği, ReceiveMessages tek bir çağrıda 20 ileti almak için yöntemini kullanır. Ardından, her iletiyi bir döngü kullanarak işler foreach . Ayrıca her ileti için görünmezlik zaman aşımı beş dakika olarak ayarlanır. Beş dakikalık tüm iletiler için aynı anda başlayacağını unutmayın. bu nedenle, çağrısından bu yana beş dakika geçtikten sonra ReceiveMessages , silinmemiş olan tüm iletiler yeniden görünür hale gelir.
//-----------------------------------------------------
// Process and remove multiple messages from the queue
//-----------------------------------------------------
public void DequeueMessages(string queueName)
{
// Get the connection string from app settings
string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
// Instantiate a QueueClient which will be used to manipulate the queue
QueueClient queueClient = new QueueClient(connectionString, queueName);
if (queueClient.Exists())
{
// Receive and process 20 messages
QueueMessage[] receivedMessages = queueClient.ReceiveMessages(20, TimeSpan.FromMinutes(5));
foreach (QueueMessage message in receivedMessages)
{
// Process (i.e. print) the messages in less than 5 minutes
Console.WriteLine($"De-queued message: '{message.Body}'");
// Delete the message
queueClient.DeleteMessage(message.MessageId, message.PopReceipt);
}
}
}
Kuyruk uzunluğu alma
Bir kuyruktaki ileti sayısı ile ilgili bir tahmin alabilirsiniz. GetPropertiesYöntemi, ileti sayısı dahil olmak üzere sıra özelliklerini döndürür. ApproximateMessagesCountÖzelliği kuyruktaki yaklaşık ileti sayısını içerir. Bu sayı kuyruktaki gerçek ileti sayısından daha düşük değil, ancak daha yüksek olabilir.
//-----------------------------------------------------
// Get the approximate number of messages in the queue
//-----------------------------------------------------
public void GetQueueLength(string queueName)
{
// Get the connection string from app settings
string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
// Instantiate a QueueClient which will be used to manipulate the queue
QueueClient queueClient = new QueueClient(connectionString, queueName);
if (queueClient.Exists())
{
QueueProperties properties = queueClient.GetProperties();
// Retrieve the cached approximate message count.
int cachedMessagesCount = properties.ApproximateMessagesCount;
// Display number of messages.
Console.WriteLine($"Number of messages in queue: {cachedMessagesCount}");
}
}
Bir kuyruk silme
Bir kuyruğu ve içerdiği tüm iletileri silmek için, Delete kuyruk nesnesi üzerinde yöntemini çağırın.
//-------------------------------------------------
// Delete the queue
//-------------------------------------------------
public void DeleteQueue(string queueName)
{
// Get the connection string from app settings
string connectionString = ConfigurationManager.AppSettings["StorageConnectionString"];
// Instantiate a QueueClient which will be used to manipulate the queue
QueueClient queueClient = new QueueClient(connectionString, queueName);
if (queueClient.Exists())
{
// Delete the queue
queueClient.Delete();
}
Console.WriteLine($"Queue deleted: '{queueClient.Name}'");
}
Sonraki adımlar
sıra Depolama temellerini öğrendiğinize göre, daha karmaşık depolama görevleri hakkında daha fazla bilgi edinmek için bu bağlantıları izleyin.
- kullanılabilir apı 'lerle ilgili tüm ayrıntılar için sırayı Depolama başvuru belgelerini görüntüleyin:
- Azure’da veri depolama ile ilgili ek seçenekler hakkında daha fazla bilgi edinmek için daha fazla özellik kılavuzu görüntüleyin.
- yapılandırılmış verileri depolamak için .net kullanarak Azure tablo Depolama kullanmaya başlayın.
- yapılandırılmamış verileri depolamak için .net kullanarak Azure Blob Depolama kullanmaya başlayın.
- İlişkisel verileri depolamak için .NET (C#) kullanarak SQL Veritabanı'na bağlanın.
- Azure WebJobs SDK kullanarak Azure Storage ile birlikte çalışmak üzere yazdığınız kodları nasıl sadeleştireceğinizi öğrenin.