https://www.buraksenyurt.com/Burak Selim Şenyurt - Azure2022-03-31T18:23:53+00:00Matematik Mühendisi Bir Bilgisayar Programcısının NotlarıBurak Selim SenyurtBlogEngine.Net Syndication Generatorhttps://www.buraksenyurt.com/opml.axdBurak Selim SenyurtMatematik Mühendisi Bir Bilgisayar Programcısının Notlarıtr-TRBurak Selim Şenyurt0.0000000.000000https://www.buraksenyurt.com/post/azure-signalr-servisini-kullanmakAzure SignalR Servisini Kullanmak2019-09-30T13:00:00+00:00bsenyurt<p><img style="float: right;" src="https://www.buraksenyurt.com/image.axd?picture=/2019/09/34/emcey.png" alt="" />Basketbolu neden bu kadar çok seviyorum diye düşündüm geçenlerde. Oturduğumuz sitenin basket sahasını futbol oynamak için kullanan onca çocuk ve genç gibi bir gerçek varken ben neden bu spora böylesine sevdalıydım. İnanılmaz enerjisi ve sürekli değiştirdiği NBA şapkaları ile rahmetli İsmet Badem mi sevdirmişti? Yoksa final serisi maçları sabahın kaçında olursa olsun uyanamayıp okula geç gitmeme neden olan majestelerinin maçları mı? Basketbolun tüm efsanelerini kendi kardeşiymiş gibi tanıyan ve maçları kendine has heyecanı ile anlatan Murat Murathanoğlu muydu yoksa?</p>
<p>Belki de Koraç kupasını alarak Avrupa'da bir ilke imza atan Efes'in Abdi İpekçi salonundaki Stefanel Milano maçına girmek için kuyrukta beklerken arabasından bizi seyreden yaşıtım Mirsad Türkcan'ın onca seyirciyi coşkuyla selamlamasıydı. Kim bilir belki de hücum süresi henüz otuz saniyeyken Peter Naumovski'nin eliyle tshirt'ünün sağ yakasını ağzına götürerek verdiği setin adıydı. Belki de zamanında her gün büyük bir iç motivasyonla gittiğim turuncu bankanın CBL<a href="http://www.cbl.com.tr/" target="_blank"><em>(corporate basketball league)</em></a> seçmelerinde koçun bana gelip "abi kusura bakma" dedikten sonra yaşımı öğrenip "sen ciddi misin abi? Ben bu kadar büyük olduğunu bilmiyordum. Çok daha genç duruyorsun. Basketbol sevgine hayran kaldım" söylemine rağmen takıma almayışı ve Bill Murray'ın Space Jam'de Larry Bird ile olan konuşmasında ona "You can't play" demesini hatırlayışım mıydı? İnanın hiç bilmiyorum. Ama çok sevip de hiç bir zaman beceremediğim bu oyunu mesleki çalışmalarımda kullanmaya bayılıyorum. İşte öyle bir çalışmanın girizgahındasın şu anda sevgili okur :)</p>
<p>O <a href="https://github.com/buraksenyurt/saturday-night-works" target="_blank">cumartesi gecesi çalışması</a>ndaki amacım Azure platformundaki SignalR hizmetini kullanarak abone programlara çeşitli tipte bildirimlerde bulunabilmekti. Normal SignalR senaryosundan farklı olarak istemciler ve tetikleyici arasındaki eş zamanlı iletişimi<em>(Real Time Communications)</em> Azure platformundaki bir SignalR servisi ile gerçekleştirmek istemiştim. Senaryoda bildirimleri gören en az bir istemci<em>(ki n tane olması daha anlamlı)</em>, local ortamda çalışan ve bildirim yayan bir Azure Function uygulaması ve Azure platformunda konuşlandırılan bir SignalR servisi olmasını planlamıştım. Ayrıca Azure üzerinde koşan bu SignalR servisini Serverless modda çalışacak şekilde ayarlamayı planlıyordum. Bir takım sonuçlara ulaşmayı başardım. Şimdi çalışmaya ait notları derleme zamanı. Öyleyse ne duruyoruz. Haydi başlayalım.</p>
<p>SignalR servisi tüm Azure fonskiyonları ile kullanılabilir. Örneğin Azure Cosmos DB'deki değişiklikleri SignalR servisi ile istemcilere yollayabiliriz. Benzer şeyi kuyruk mesajlarını veya HTTP taleplerini işleyen Azure fonksiyonları için de sağlayabiliriz. Kısacası Azure fonksiyonlarından yapılan tetiklemeler sonrasında SignalR servislerinden yararlanarak bağlı olan aboneleri bilgilendirebiliriz. Şimdi WestWorld'ün gereksinimlerini tamamlayaraktan örneğimizi geliştirmeye başlayalım.</p>
<h2>Ön Gereksinimler</h2>
<p>Azure platformunda SignalR servisini oluşturmadan önce WestWorld<em>(Ubuntu 18.04, 64bit)</em> tarafında Azure Function geliştirebilmek için gerekli kurulumları yapmam gerekiyordu. İlk olarak <a href="https://github.com/Azure/azure-functions-core-tools" target="_blank">Azure Functions Core Tools</a>'un yüklenmesi lazım. Aşağıdaki terminal komutları ile bunu gerçekleştirmek mümkün. Önce Microsoft ürün anahtarını Ubuntu ortamına kaydediyor ve sonrasında bir güncelleme yapıp devamında azure-functions-core-tools paketini yüklüyoruz.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg
sudo apt-get update
sudo apt-get install azure-functions-core-tools</pre>
<p>Kurulumdan sonra terminalden Azure Function projeleri oluşturmaya başlanabilir. Lakin bu işin Visual Studio Code tarafında daha kolay bir yolu var. O da <a href="https://code.visualstudio.com/docs/azure/extensions" target="_blank">Azure Functions isimli aracı</a> kullanmak.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/09/34/credit_1.png" alt="" /></p>
<p>Visual Studio Code'a gelen bu araçla kolayca Azure Function projeleri oluşturabiliriz.</p>
<h2>Azure SignalR Servisinin Hazırlanması</h2>
<p>Adım adım ilerlemeye çalışalım. Öncelikle Azure platformunda bir SignalR servisi oluşturmamız gerekiyor. Ben <a href="https://portal.azure.com" target="_blank">Azure Portal adresinden</a> SignalR Service öğesini aratarak işe başladım. Sonrasında aşağıdaki ekran görüntüsünde yer alan bilgiler ile servisi oluşturdum.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/09/34/credit_2.png" alt="" /></p>
<p>Free Tier planında, learning-rg Resource Group altında, basketcini.service.signalr.net isimli bir SignalR servisimiz var. Bu servisinin oluşması biraz zaman alabilir ki ben bir süre beklediğimi hatırlıyorum. Servis etkinleştikten sonra özelliklerine giderek Serverless modda çalışacak şekilde ayarlayabiliriz. Bunun için Service Mode özelliğini Serverless'a çekmek yeterli. Tabii ekran görüntüsünden de fark edeceğiniz üzere PREVIEW modunda. Kuvvetle muhtemel sizin denemelerinizi yapacağınız durumda son halini almış olabilir.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/09/34/credit_3.png" alt="" /></p>
<p>Bu SignalR servisi ile local makinede çalışacak ve tetikleyici görevini üstlenecek Azure Function uygulamasının haberleşebilmesi için, Key değerlerine ihtiyacımız olacak. Bu değerleri Azure Function uygulamasının local.settings.json dosyasında kullanmamız gerekiyor. O nedenle aşağıdaki ekran görüntüsündeki gibi ilgili değerleri kopyalayıp güvenli bir yerlerde saklayın.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/09/34/credit_4.png" alt="" /></p>
<h2>Azure Functions Projesinin Oluşturulması</h2>
<p>Yüklenen Azure Functions aracından <strong>Create New Project</strong> seçimini yaparak ilerleyebiliriz. Proje için bir klasör belirleyip<em>(Ben NotifierApp isimli klasörü kullandım)</em> dil olarak C#'ı tercih ederek devam edelim. Sonrasında <strong>Create Function</strong> seçeneği ile projeye Scorer isimli bir fonksiyon ekleyelim. Ben bu işlem sırasında sorulan sorulara aşağıdaki cevapları verdim. Siz kendi projenize özgün hareket ederseniz daha iyi olabilir. Özetle HTTP metodları ile tetiklenen bir fonksiyon söz konusu diyebiliriz.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">Fonksiyon Adı : Scorer
Klasör : NotifierApp
Tipi : Http Trigger
Namespace : Basketcini.Function
Erişim Yetkisi : Anonymous</pre>
<blockquote>
<p>Örnekte Table Storage seçeneği değerlendirilmiştir. Bunun için öncelikle Azure Portal üzerinde learningsignalrstorage isimli bir Storage Account oluşturdum ve Access Keys kısmında verilen Connection Strings bilgisini kullandım. Yani bildirimlerin depolanacağı Storage alanını sevgili Azure'a devrettim. Çünkü WestWorld'ün disk kapasitesi epeyce azalmış durumdaydı :P</p>
</blockquote>
<h3>Azure Functions Projesinde Yapılanlar</h3>
<p>Azure fonksiyonu oluşturulduktan sonra elbette biraz kodlama yapmamız gerekecek. Ama öncesinde bizim için gerekli nuget paketlerini yüklemeliyiz. Aşağıdaki terminal komutlarını NotifierApp klasöründe çalıştırarak devam edelim.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">dotnet add package Microsoft.Azure.WebJobs.Extensions.EventGrid
dotnet add package Microsoft.Azure.WebJobs.Extensions.SignalRService
dotnet add package Microsoft.Azure.WebJobs.Extensions.Storage</pre>
<p>Önemli değişikliklerden birisi local.settings.json dosyasında yer alıyor. Burada Azure SignalR servisine ait Connection String bilgisi ve CORS tanımı<em>(Senaryoya göre isimsiz tüm istemciler Azure Function Api'sini kullanabilecek)</em> eklemek lazım. Nasıl yapıldığını söylemek isterdim ama gitignore dosyasında bu json içeriğini dışarıda bırakmışım. Yani hatırlamıyorum :) Yani sizin keşfetmeniz gerekecek ;)</p>
<p>Bunun haricinde skor durumunu ve anlık olarak meydana gelen olay bilgisini tutan Timeline ve Action isimli sınıfları da aşağıdaki gibi kodlayabiliriz. Biliyorum henüz senaryo tam olarak şekillenmiş değil. Ama çalışma zamanına geldiğimizde ne olduğunu gayet iyi anlayacaksınız. Action sınıfı ile başlayalım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">namespace Basketcini.Function
{
/*
Table Storage'e yazılacak veri içeriğini temsil eden sınıftır.
Azure Table Storage'a aşağıdaki özellikler birer alan olarak açılacaktır.
*/
public class Action
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string Player { get; set; }
public string Summary { get; set; }
}
}</pre>
<p>ve Timeline sınıfımız;</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">namespace Basketcini.Function
{
/*
Abonelere döndürülecek veri içeriğini taşıyacan temsili sınıftır.
Kim, hangi olayı gerçekleştirdi bilgisini tutar.
*/
public class Timeline
{
public string Who { get; set; }
public string WhatHappend { get; set; }
}
}</pre>
<p>Scorer isimli Function sınıfında da üç metod bulunuyor. Birisi tetikleyici olarak yeni bir olay gerçekleştirmek için, birisi istemcinin kendisini SignalR Hub'ına bağlaması için<em>(negotiation aşaması)</em>, birisi de servisin istemciye olay bildirimlerini basması için<em>(push message aşaması) </em>Her zaman ki gibi kod içerisindeki yorum satırlarında anladıklarımı basitçe anlatmaya çalıştım.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace Basketcini.Function
{
public static class Scorer
{
/*
Scorer fonskiyonu HTTP Post tipinden tetiklemeleri karşılar.
Oluşan aksiyonları saklamak için Table Storage kullanılır. Actions isimli tablo Table niteliği ile bildirilmiştir.
Ayrıca gerçekleşen olaylar bir kuyruğa atılır(Queue niteliğinin olduğu kısım)
Console'a log yazdırmak için ILogger türevli log değişkeni kullanılır.
*/
[FunctionName("Scorer")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post")] Timeline timelineEvent,
[Table("Actions")]IAsyncCollector<Action> actions,
[Queue("new-action-notification")]IAsyncCollector<Timeline> actionNotifications,
ILogger log)
{
log.LogInformation("HTTP tetikleme gerçekleşti");
log.LogInformation($"{timelineEvent.Who} için {timelineEvent.WhatHappend} olayı");
/* HTTP Post metodu ile gelen timeline bilgilerini de kullanarak bir Action nesnesi
oluşturuyor ve bunu Table Storage'e atıyoruz.
Amaç, meydana gelen olaylarla ilgili gelen bilgileri bir tabloda kalıcı olarak saklamak.
Pek tabii bunun yerine farklı repository'ler de tercih edilebilir. Cosmos Db gibi örneğin.
*/
await actions.AddAsync(new Action
{
PartitionKey = "US",
RowKey = Guid.NewGuid().ToString(),
Player = timelineEvent.Who,
Summary = timelineEvent.WhatHappend
});
/*
new-action-notification ile ilintili olan kuyruğa gerçekleşen olay bilgilerini atıyoruz.
İstemci tarafını bu kuyruk içeriği ile besleyebiliriz.
*/
await actionNotifications.AddAsync(timelineEvent);
return new OkResult();
}
/*
Azure SignalR servisine bağlanmak için kullanılan metodumuz.
HTTP Post ile tetiklenir.
Fonksiyon bir SignalRConnectionInfo nesnesini döndürür.
Bu nesne Azure SignalR'a bağlanırken gerekli benzersiz id ve access token bilgisini içerir.
SignalR Hub-Name olarak notifications ismi kullanılır.
*/
[FunctionName("negotiate")]
public static SignalRConnectionInfo GetNotificationSignal(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")]HttpRequest request,
[SignalRConnectionInfo(HubName = "notifications")]SignalRConnectionInfo connection,
ILogger log
)
{
log.LogInformation("Negotiating...");
return connection;
}
/*
Abone olan tarafa veri göndermek (push) için kullanılan fonksiyondur.
QueueTrigger niteliğindeki isimlendirme ve tipin Scorer fonksiyonundaki ile aynı olduğuna dikkat edelim.
İstemciye mesaj taşıyan nesne bir SignalRMessage örneğidir.
Bu nesnenin Arguments özelliğinde timeline içeriği (yani gerçekleşen maç olayları) taşınır.
Peki aboneler buradaki olayları nasıl dinleyecek dersiniz? Bunun içinde Target özelliğine atanan içerik önem kazanır.
Örneğimizide aboneler 'actionHappend' isimli olayı dinleyerek mesajları yakalayacaktır.
*/
[FunctionName("PushTimelineNotification")]
public static async Task PushNofitication(
[QueueTrigger("new-action-notification")]Timeline timeline,
[SignalR(HubName = "notifications")]IAsyncCollector<SignalRMessage> message,
ILogger log
)
{
log.LogInformation($"{timeline.Who} için gerçekleşen olay bildirimi");
await message.AddAsync(
new SignalRMessage
{
Target = "actionHappend",
Arguments = new[] { timeline }
}
);
}
}
}</pre>
<h2>İstemci Uygulama Tarafı</h2>
<p>İstemci tarafı Node.js tabanlı basit bir Console uygulaması. Aslında web tabanlı bir arayüzü takip etmem gerekiyordu ancak amacım kısa yoldan SignalR servisinden akan verileri görmek olduğundan Node.js kullanmayı tercih ettim. Siz istemci tarafında tamamen özgünsünüz. SignalR tarafı ile rahat konuşabilmek için @aspnet/signalr isimli npm paketini kullanabiliriz. Terminalden aşağıdaki komutları kullanarak kobay istemcimizi oluşturalım.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">mkdir FollowerApp
cd FollowerApp
npm init
touch index.js
npm install @aspnet/signalr</pre>
<p>İstemci tarafında index.js ve package.json dosyalarını kodlayacağız. Aşağıda index sınıfına ait kod içeriğini bulabilirsiniz. Uygulama Hub'a bağlandıktan sonra bildirimleri dinler modda yaşamını sürdürecek diyebiliriz.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">const signalR = require("@aspnet/signalr"); // signalR istemci modülünü bildirdik
/*
Hub bağlantı bilgisini inşa ediyoruz.
withUrl parametresi Azure Function uygulamasının yayın yaptığı adrestir
*/
const connection = new signalR.HubConnectionBuilder()
.withUrl('http://localhost:4503/api')
.build();
console.log('Bağlantı sağlanıyor...');
/*
Bağlantıyı başlatıyoruz. Başarılı ise then metodunun içeriği,
bir hata oluşursa da catch metodunun içeriği çalışır.
*/
connection.start()
.then(() => console.log('Bağlantı sağlandı...'))
.catch(console.error);
/*
actionHappend olayını dinlemeye başladık.
Eğer SignalR servisi üzerinden bir push mesajı söz konusu olursa
bu olay üzerinden geçeceği için istemci tarafından yakalanıp
doSomething metodu çağırılacaktır.
doSomething'e gelen parametre Azure Function'daki
PushTimelineNotification fonksiyonundan dönen mesajın Arguments içeriğini taşır.
*/
connection.on("actionHappend", doSomething);
function doSomething(action) {
console.log(action);
}
connection.onclose(() => console.log('Bağlantı koparılıyor...'));</pre>
<p>ve package.json</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">{
"name": "followerapp",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "node index.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"@aspnet/signalr": "^1.1.2"
}
}</pre>
<h2>Çalışma Zamanı<em>(NotifierApp Uygulaması)</em></h2>
<p>Bu adam neler anlattı, neler yazdı diyor gibisiniz biliyorum. O nedenle çalışma zamanına geçmeden önce senaryodan bahsetmem çok doğru olacaktır. WestWorld üzerinde NotifierApp isimli Azure Function uygulaması ayağa kalkar. Bu, Azure SignalR servisi ile haberleşen programımız. Postman ile hayali olarak o anda oynanan bir basketbol maçından çeşitli bilgiler göndereceğiz. Sayı oldu, blok yapıldı vs gibi. Bu bilgiler Azure tarafındaki SignalR servisimiz tarafından karşılanacak ve Table Storage üstünde kuyruğa yazılacak. Yine WestWorld üzerinde çalışan bir başka uygulama<em>(Etkili bir görsellik için bir web sayfası ya da konuyu anlamak için bir console uygulaması olabilir)</em> Local ortamda çalışan Azure Function servisine bağlanıp <strong>actionHappend</strong> olaylarını dinleyecek. Postman üzerinden maça ait bir basketbol olayı gönderildikçe bu bilgilerin tamamının yer aldığı kuyruk içeriği abone olan istemcilere otomatik olarak dağıtılacak. Sonuçta canlı bir maçın gerçekleşen anlık olayları bu haber kanalını dinleyen istemcilerine eş zamanlı olarak basılmış olacak<em>(en azından senaryonun bu şekilde çalışmasını bekliyoruz)</em></p>
<p>Yazılan Azure Function uygulamasını çalıştırmak için terminalden aşağıdaki komutu vermek yeterli. Tabii bu komutu Azure Function projesinin olduğu klasörde icra etmeliyiz ;)</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">func host start</pre>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/09/34/credit_5.png" alt="" /></p>
<p>Function uygulamamız şu anda local ortamda çalışır durumda olmalı ve Azure SignalR ile haberleşmesi gerekli. En azından WestWorld üzerinde bu şekilde işledi. Şimdi Postman aracını kullanarak api/Scorer adresine bir HTTP Post talebi gönderebiliriz. Örneğin aşağıdaki gibi.</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">Url : http://localhost:4503/api/Scorer
Method : HTTP Post
Body : {
"Who":"Mitsiç",
"WhatHappend":"3 sayılık basket. Skor 33-21 Anadolu Efes önde"
}</pre>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/09/34/credit_6.png" alt="" /></p>
<p>Bir şeyleri doğru yazmış olmalıyım ki log mesajlarında istediğim hareketliliği gördüm. Hatta Azure Storage tarafında bir tablonun oluşturulduğunu ve gönderdiğim bilginin içerisine yazıldığını da fark ettim<em>(Tekrar eden bilgileri nasıl normalize etmek gerekir bunun yolunu bulmak lazım) </em>Şu aşamaya gelen okurlarım, umarım sizler de benzer sonuçları görmüşsünüzdür.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/09/34/credit_7.png" alt="" /></p>
<h2>Çalışma Zamanı<em>(İstemci/Abone olan taraf)</em></h2>
<p>Bildirim yapmayı başardık. Bildirimlerin kuyruğa gittiğini de gördük. Peki ya abonelerden ne haber? Senaryonun tam işlerliğini görmek için her iki uygulamayı da birlikte çalıştırmak lazım elbette. Node.js tabanlı FollowerApp için terminalden aşağıdaki komutu vermek yeterli.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">npm run dev</pre>
<p>İlk ekran görüntüsü istemci ile Azure SignalR servisinin, Azure Function uygulaması aracılığıyla el sıkışmasını gösteriyor.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/09/34/credit_8.png" alt="" /></p>
<p>Alt ekran görüntüsünde dikkat edileceği üzere Negotiation başarıyla sağlandıktan sonra bir id ve token bilgisinin üretildiği görülmekte. Buradaki çıktı, Azure Function uygulamasındaki negotiate sonrası döndürdüğümüz connection bilgisine ait. Dikkat çekici noktalardan birisi de Web Socket adresi. Görebildiniz mi?</p>
<p>İkinci ekran görüntüsünde http://localhost:4503/api/Scorer adresine HTTP Post talebi ile örnek bir olay bilgisi gönderilmekte. Bu talep sonrası uygulamalardaki log hareketliliklerine dikkat etmek lazım. Oluşan içerik bağlı olan istemciye yansımış olmalıdır. Bu yılın flaş takımı Anadolu Efes'ten 4 ve 5 numara pozisyonlarında oynayabilen ve üçlük yüzdesi de fena olmayan Moaerman epey ribaund toplamış sanki.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/09/34/credit_9.png" alt="" /></p>
<p>Üçüncü çalışma zamanı görüntüsünde ekrana ikinci bir istemci dahil etmekteyiz. Bu durumda push edilen bilgiler bağlı olan tüm abonelere gönderilecektir ki istediğimiz senaryolardan birisi de bu<em>(Bırayn Danstın mı? Yok artık Babi diksın mı? :D )</em></p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/09/34/credit_10.png" alt="" /></p>
<blockquote>
<p>Eğer bu senaryoda yaptığımız gibi bir maçın canlı anlatımını çevrimiçi tüm abonelere göndermek istiyorsak, sonradan dahil olanların maçın başından itibaren kaçırdıkları olayları da görmesini isteyebiliriz. Burada Table Storage veya benzeri bir depoda maç bazlı tutulacak verileri, istemci ilk bağlandığında ona nasıl yollayabiliriz doğrusu çok merak ediyorum. İşte size güzel bir TODO ;)</p>
</blockquote>
<h2>Ben Neler Öğrendim?</h2>
<p>Aslında hepsi bu. Temel bir kurgu ile Azure tarafındaki SignalR servisimizi kullanarak bir push notification sürecini deneyimledik diyebilirim. Her cumartesi gecesi çalışmasında olduğu gibi bu uygulamadan da bir şeyler öğrendim elbette. Bunları aşağıdaki gibi sıralayabilirim. Unutana kadar bendeler :)</p>
<ul>
<li>Azure tarafında bir SignalR Servisinin nasıl oluşturulacağını</li>
<li>Geliştirme ortamında bir Azure Function projesinin nasıl inşa edilebileceğini</li>
<li>SignalR üzerinden Hub dinleyicisi istemcilerde @aspnet/signalr npm paketinin nasıl kullanılabileceğini</li>
<li>Azure Storage oluşturmadan Function projesindeki Table Storage'ın kullanılamayacağını</li>
<li>SignalR servisini kullanan Azure Function projesinin herhangi bir istemci tarafından kullanılabilmesi için CORS tarafında '*' kullanılması gerektiğini<em>(Bunu makalede bulamayacaksınız sizin keşfetmeniz gerekebilir:( )</em></li>
<li>Azure Function tarafında abonelerin SignalR ile el sıkıştığı fonksiyon adının 'negotiate' olması gerektiğini<em>(Farklı bir isim kullanınca istemci tarafında HTTP 404 NotFound hatası aldım)</em></li>
<li>Benzer şekilde SignalR Hubname olarak <strong>notifications</strong> kullanılması gerektiğini<em>(Farklı bir isimlendirme kullanınca oluşan bilgilerin SignalR servisi tarafından yorumlandığını ama abonelere akmadığına şahit oldum)</em></li>
</ul>
<p>Böylece geldik doğduğum, yaşadığım ve asla kopamayacağım <a href="https://github.com/buraksenyurt/saturday-night-works/tree/master/No%2034%20-%20Using%20Azure%20SignalR" target="_blank">İstanbul plakalı cumartesi gecesi derlemesinin sonuna</a>. Umarım sizler için de yararlı bir çalışma olmuştur. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2019-09-30T13:00:00+00:00azuresignalrreal time communicationsnodejavascript.net corec#corsazure functionscloud computingtable storagewebjobsazure webjobswebsocketsbsenyurtAmacım Azure platformunda sunulan SignalR hizmetini kullanarak abonelere bildirimlerde bulunabilmek. Normal SignalR senaryosundan farklı olarak, istemciler ve tetikleyici arasındaki eş zamanlı iletişimi (Real Time Communications) Azure platformundaki bir SignalR servisi ile gerçekleştirmeye çalışacağım. Senaryomuzda bildirimleri gören en az bir istemci (ki n tane olması daha anlamlı), local ortamda çalışan ve bildirim yayan bir Azure Function uygulaması ve Azure platformunda konuşlandırılan bir SignalR servisi olacak. Azure üzerinde koşan SignalR servisi Serverless modda çalışacak şekilde ayarlanacak.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=30cdee94-57e9-4321-ac80-dd4061b2352a2https://www.buraksenyurt.com/trackback.axd?id=30cdee94-57e9-4321-ac80-dd4061b2352ahttps://www.buraksenyurt.com/post/azure-signalr-servisini-kullanmak#commenthttps://www.buraksenyurt.com/syndication.axd?post=30cdee94-57e9-4321-ac80-dd4061b2352ahttps://www.buraksenyurt.com/post/microsoft-custom-vision-servisini-python-ile-kullanmakMicrosoft Custom Vision Servisini Python ile Kullanmak2019-08-23T13:00:00+00:00bsenyurt<p><img style="float: right;" src="https://www.buraksenyurt.com/image.axd?picture=/2019/08/asset_12.png" alt="" />Yandaki resme baktığınızda aklınıza gelen ilk şey nedir? Bir surat? Belki de bir kurbağa. Kedi olabilir mi? Bu mürekkep baskısı gösterildiği kişide yarattığı algıyı anlamak için kullanılan Rorschach<em>(Roşa olarak okunuyormuş)</em> isimli psikolojik testten. Ünlü İsviçreli psikiyatrist Hermann Rorschach<em>(1884-1922)</em> tarafından geliştirilen test özellikle kişilik tahlili ve şizofreni vakalarında kullanılmakta. Sonuçların manipule edilmesinin zorluğu nedeniyle adli vakalarda ve hatta kariyerle ilgili kişilik testlerinde bile ele alınmakta. Hermann yandakine benzer kırk mürekkep baskısı tasarlamış. Kaynaklardan öğrendiğim kadarıyla doktorlar bu setteki kartların neredeyse yarısını kullanıp kişinin o anda nevrotik veya psikotik olup olmadığını anlayabiliyormuş. Tabi konunun uzmanı olmadığım için ancak giriş hikayemde kullanabilecek kadar bilgi aktarabiliyorum.</p>
<p>Filmlerde ve internette sıklıkla gördüğümüz bu mürekkep baskılarına az çok aşinayızdır. Peki bu fotoğrafa baktığında bir yapay zeka ne düşünür? Onun tamamen rasyonel olan dünyasında duygulara yer olmadığını varsayarsak tüm yapay zekalar için sonuç aynı mı olacaktır? Duygusal zeka ile donatılmış bir yapay zekanın tepkimeleri çeşitlilik gösterir mi? Sanırım onu bu resimlerle ilgili yeterince iyi eğitirsek düşüncelerini kolayca öğrenebiliriz. Tabii <a href="https://github.com/buraksenyurt/saturday-night-works" target="_blank">o cumartesi gecesi çalışması</a>nda ben Rorschach resimlerini sınıflandıracak bir yapa zeka servisi aramak yerine elimdeki Lego fotoğraflarını öğretebileceğim birisine bakıyordum. Sonunda Microsoft'un Custom Vision servisini incelemeye karar verdim. Öyleyse derlememize başlayalım.</p>
<p>Amacım, Microsoft Azure platformunda yer alan ve fotoğraf/nesne sınıflandırmaları için kullanılabilen Custom Vision servisini basit bir Python uygulaması ile deneyimlemek. Custom Vision API geliştircilere kendi fotoğraf/nesne sınıflandırma servislerini yazma imkanı sunuyor. Onu, imajları belli karakteristik özelliklerine göre çeşitli takılar<em>(tag)</em> altında sınıflandırıp sıralayan bir AI<em>(Artificial Intelligence)</em> servisi olarak düşünebiliriz.</p>
<p>Örnek çalışmada belli takılar için belli sayıda imajı sisteme öğretmeye çalışacağız<em>(Custom vision api için bu oran en az iki tag ve her bir tag için en az beş fotoğraf/nesne şeklinde)</em> Öğretiyi tamamladıktan sonra sisteme bir fotoğraf gösterip ne olduğunu tahmin etmesini isteyeceğiz. Sistem bizim öğrettiklerimize göre bir tahminlemede bulunacak ve yüzdesel değerler verecek. Son olarak bu sonuçları sınıfla birlikte tartışacağız :P</p>
<h2>Ön Hazırlıklar</h2>
<p>Her zaman olduğu gibi ben uygulamayı Python SDK'sini kullanarak WestWorld<em>(Ubuntu 18.04, 64bit)</em> üzerinde geliştiriyorum. Platform bağımsız olarak Python ve pip aracının sistemde yüklü olması gerekiyor. Python tarafından Custom Vision API hizmetini kullanabilmek için ilgili paketin yüklenmesi lazım. Aşağıdaki terminal komutu ile bunu yapabiliriz.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">pip install azure-cognitiveservices-vision-customvision</pre>
<h3>Custom Vision API için Credential Bilgilerinin Alınması</h3>
<p>Diğer pek çok 3ncü parti serviste olduğu gibi istemci tarafının ilgisi servisi kullanmasını sağlayacak bir ehliyete<em>(Credentials)</em> sahip olması gerekiyor. Bu nedenle servis için abone olmamız ve uygulama anahtarını almamız lazım. İlk olarak <a href="https://www.customvision.ai/" target="_blank">şu adrese gidip login olmalıyız</a>. Ardından <em>Create new project</em> sekmesini kullanarak yeni bir proje oluşturmalıyız. Ben buradaki ayarları varsayılan değerlerinde bırakıp CIA çakması bir proje oluşturdum. Buna göre projemiz sınıflandırma görevini üstleniyor. Sınıflandırılmaya tabi olan tipler birden fazla takıyla işaretlenebilir. Özel bir domain belirtmedik ama ihtiyaca göre bu seçenek general haricindekilerden birisi de olabilir.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/08/credit_1.png" alt="" /></p>
<p>Proje oluşturulduktan sonra özelliklerine ulaşıp bizim için üretilen <em>Training Key</em> ve <em>Prediction Key</em> değerlerini almamız gerekiyor. Bu bilgiler istemci tarafı için gerekli.</p>
<blockquote>
<p>Kodun çalışma dinamiklerini anlamadan önce Custom Vision API için ilk başta oluşturduğumuz projeyi tarayıcıdan denemenizi öneririm. Belirtildiği gibi elinizdeki imajları en az 2 farklı tag ile eşleşecek şekilde ayrıştırıp sisteme yükleyin. Sonra eğitim programını<em>(training kısmı)</em> başlatın ki bu işi Azure tarafı halledecek. Program işleyişini tamamlayınca bir kaç imaj yükleyip hangi takılardan yüzde kaç oranında karşılandığına bakın. Örnek kümesi zenginleştikçe tahminlerin doğruluk oranları da yükselecektir.</p>
</blockquote>
<h2>Kodlama ve Çalışma Zamanı</h2>
<p>Azure tarafından Vision servis için ehliyetimizi aldığımıza göre python tarafını kodlamaya başlayabiliriz. İki python dosyamız var. pgadget.py isimli olanı fotoğraf eğitimi için kullanıyoruz. client.py ise servisi tüketip sonuçları almak için çalıştırılıyor.</p>
<p>pgadget kodlarımıza gelince;</p>
<pre class="brush:py;auto-links:false;toolbar:false" contenteditable="false"># -*- coding: utf-8 -*-
# Custom Vision API'sini kullanabilemek için gerekli modüllerin bildirimi ile işe başladık
from azure.cognitiveservices.vision.customvision.training import CustomVisionTrainingClient
from azure.cognitiveservices.vision.customvision.training.models import ImageFileCreateEntry
# Eğitici servise ait endpoint bilgisi
apiEndpoint = "https://southcentralus.api.cognitive.microsoft.com"
# Bizim için üretiken traning ve prediction key değerleri
tKey = "c3a53a4fb5f24137a179f0bcaf7754a5"
pKey = "bf7571576405446782543f832b038891"
# Eğitmen istemci nesnesi tanımlanıyor. İlk parametre traning_key
# ikinci parametre Cognitive servis adresi
coach_Rives = CustomVisionTrainingClient(tKey, endpoint=apiEndpoint)
# Projeyi oluşturuyoruz
print("Lego projesi oluşturuluyor")
legoProject = coach_Rives.create_project("Agent_Leggooo") # projemizin adı
# Şimdi deneme amaçlı tag'ler oluşturup bu tag'lere çeşitli fotoğraflar yükleyeceğiz
technic = coach_Rives.create_tag(legoProject.id, "technic")
city = coach_Rives.create_tag(legoProject.id, "city")
# Aşağıdaki tag'ler şu anda yorum satırı. Bunları açıp, create_images_from_files metodlarındaki tag_ids dizisine ekleyebiliriz.
# Ancak Vision servisi her tag için en az beş adete fotoğraf olmasını istiyor. Bu kümeyi örnekleyemediğim için sadece iki tag ile ilerledim.
'''
helicopter = coach_Rives.create_tag(legoProject.id, "helicopter")
truck = coach_Rives.create_tag(legoProject.id, "truck")
yellow = coach_Rives.create_tag(legoProject.id, "yellow")
plane = coach_Rives.create_tag(legoProject.id, "plane")
car = coach_Rives.create_tag(legoProject.id, "car")
racecar = coach_Rives.create_tag(legoProject.id, "racecar")
f1car = coach_Rives.create_tag(legoProject.id, "f1car")
crane = coach_Rives.create_tag(legoProject.id, "train")
building = coach_Rives.create_tag(legoProject.id, "building")
station = coach_Rives.create_tag(legoProject.id, "station")
orange = coach_Rives.create_tag(legoProject.id, "orange")
'''
file_name = "Images/technic/choper.jpg"
with open(file_name, mode="rb") as image_contents:
coach_Rives.create_images_from_files(legoProject.id, [ImageFileCreateEntry(
name=file_name, contents=image_contents.read(), tag_ids=[technic.id])])
file_name = "Images/technic/f1car.jpg"
with open(file_name, mode="rb") as image_contents:
coach_Rives.create_images_from_files(legoProject.id, [ImageFileCreateEntry(
name=file_name, contents=image_contents.read(), tag_ids=[technic.id])])
file_name = "Images/technic/truck.jpg"
with open(file_name, mode="rb") as image_contents:
coach_Rives.create_images_from_files(legoProject.id, [ImageFileCreateEntry(
name=file_name, contents=image_contents.read(), tag_ids=[technic.id])])
file_name = "Images/technic/truck_2.jpg"
with open(file_name, mode="rb") as image_contents:
coach_Rives.create_images_from_files(legoProject.id, [ImageFileCreateEntry(
name=file_name, contents=image_contents.read(), tag_ids=[technic.id])])
file_name = "Images/technic/vinc.jpg"
with open(file_name, mode="rb") as image_contents:
coach_Rives.create_images_from_files(legoProject.id, [ImageFileCreateEntry(
name=file_name, contents=image_contents.read(), tag_ids=[technic.id])])
file_name = "Images/city/plane.jpg"
with open(file_name, mode="rb") as image_contents:
coach_Rives.create_images_from_files(legoProject.id, [ImageFileCreateEntry(
name=file_name, contents=image_contents.read(), tag_ids=[city.id])])
file_name = "Images/city/policestation.jpg"
with open(file_name, mode="rb") as image_contents:
coach_Rives.create_images_from_files(legoProject.id, [ImageFileCreateEntry(
name=file_name, contents=image_contents.read(), tag_ids=[city.id])])
file_name = "Images/city/porsche.jpg"
with open(file_name, mode="rb") as image_contents:
coach_Rives.create_images_from_files(legoProject.id, [ImageFileCreateEntry(
name=file_name, contents=image_contents.read(), tag_ids=[city.id])])
file_name = "Images/city/racecar.jpg"
with open(file_name, mode="rb") as image_contents:
coach_Rives.create_images_from_files(legoProject.id, [ImageFileCreateEntry(
name=file_name, contents=image_contents.read(), tag_ids=[city.id])])
file_name = "Images/city/snowmobile.jpg"
with open(file_name, mode="rb") as image_contents:
coach_Rives.create_images_from_files(legoProject.id, [ImageFileCreateEntry(
name=file_name, contents=image_contents.read(), tag_ids=[city.id])])
# Fotoğrafları çeşitli tag'ler ile ilişkilendirdiğimize göre öğretimi başlatabiliriz
print("lego fotoğraflarım için eğitim başlıyor")
iteration = coach_Rives.train_project(legoProject.id)
while (iteration.status != "Completed"):
iteration = coach_Rives.get_iteration(legoProject.id, iteration.id)
print("Durum..." + iteration.status)
coach_Rives.update_iteration(legoProject.id, iteration.id, is_default=True)
print("Eğitim tamamlandı...")
</pre>
<p>client.py</p>
<pre class="brush:py;auto-links:false;toolbar:false" contenteditable="false"># -*- coding: utf-8 -*-
# Bu kod ile test klasöründe yer alan imajları custom vision api servisine sorgulatıyoruz
import requests # HTTP Post talebini gönderirken kullanacağımız modül
import os # Klasördeki dosyaları okumak için kullandığımız modül
import filetype # Dosya tipi kontrolü için ekledik.
# tahminleme servisine ait endpoint
prediction_url = "https://southcentralus.api.cognitive.microsoft.com/customvision/v2.0/Prediction/334ee5e4-4fc8-4a5f-a209-a145ef857dcb/image"
# Servisi kullanabilmek için gerekli API Key
prediction_key = "bf7571576405446782543f832b038891"
# HTTP Post header bilgilerimiz
headers = {"Prediction-Key": prediction_key,
"content-type": "application/octet-stream"}
files = os.listdir('./Images/test') # test klasöründeki dosyaları alıyoruz
for f in files:
filepath = os.path.join('./Images/test', f)
extension = filetype.guess(filepath).extension # dosya tipini kontrol etmek için bakıyoruz
if extension == 'jpg': # sadece jpg tipinden dosyalarla çalışıyoruz
# sıradaki dosyayı binary olarak okuyoruz
fileData = open(filepath, 'rb').read()
# Post talebini gönderiyor ve cevabı response değişkenine atıyoruz
result = requests.post(url=prediction_url,
data=fileData, headers=headers)
print(f)
for i in range(0, 2): # taglerimize göre tahminleme bilgilerini okuyoruz
print(result.json()['predictions'][i]['tagName'])
print(result.json()['predictions'][i]['probability'])
</pre>
<p>İlk örnekte belli karakteristiklerine göre lego imajlarını sınıflandırmaya çalışıyoruz. Koddaki tag yapısı buna göre kurgulandı. Birinci örneği çalıştırmak için aşağıdaki terminal komutunu kullanabiliriz.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">python pgadget.py</pre>
<p>Local makinedeki sonuçlar şöyle olacaktır.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/08/credit_3.png" alt="" /></p>
<p>Azure projesine gidersek de aşağıdaki sınıflandırmalarla karşılaşırız.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/08/credit_4.png" alt="" /></p>
<p>Görüldüğü üzere fiziki depolama alanından seçilen fotoğraflar ilgili Azure projesine yüklendiler ve hatta iki kategori ile de tag bazında ilişkilendirildiler. Bu haliyle proje özetine baktığımızda şu sonuçları görürüz.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/08/credit_9.png" alt="" /></p>
<p>Artık servisimize bir fotoğraf gönderip ne olduğunu tahmin etmesini isteyebiliriz. Bu çok basit anlamda Postman gibi bir araçla da olabilir, tercih ettiğimiz programlama diliylede.</p>
<h3>Postman ile Test</h3>
<p>Oluşturduğumuz eğitmeni test etmek için bize açılan prediction API servisini kullanmak ve Postman üzerinden basit bir POST talebi göndermek yeterlidir<em>(Kendi örneğinizle ilgili servise ait adres bilgisini site ayarlarından bulabilirsiniz)</em></p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/08/credit_5.png" alt="" /></p>
<p>Postman ayarlarında Header kısmında ki bilgileri de aşağıdaki gibi doldurmalıyız. Sonuçta ehliyetimizi göstermemiz gerekiyor. Bu nedenle Prediction-Key değerini girmemiz şart.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/08/credit_7.png" alt="" /></p>
<p>Ben WestWorld'de bulunan bir imajı deneme amaçlı olarak göndermek istediğimden Body kısmında Binary seçeneğini kullandım.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/08/credit_8.png" alt="" /></p>
<p>Deneme olarak kullandığım fotoğraf ise şuydu. Hani şimdilerde almaya kalksak bir yıl öncesine göre neredeyse iki katından fazla para vermek zorunda olduğumuz bir kutu ne yazık ki :(</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/08/credit_6.jpg" alt="" /></p>
<p>Tahminleme servisim bu fotoğraf için aşağıdaki sonuçları verdi. %99 ihtimalle Lego City olduğunu ifade ediyor. Oldukça başarılı ;)</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">{
"id": "eb190c6e-57a9-404f-ab58-ca106afc895e",
"project": "334ee5e4-4fc8-4a5f-a209-a145ef857dcb",
"iteration": "82712433-8e16-4e1a-9c52-7d1dc108085f",
"created": "2019-02-25T11:12:04.3505905Z",
"predictions": [
{
"probability": 0.9998578,
"tagId": "0e0fe67a-9377-426b-88db-98081766c042",
"tagName": "city"
},
{
"probability": 0.0000050534627,
"tagId": "d6ea80b8-8f44-488a-9e18-20227ef70fd2",
"tagName": "technic"
}
]
}</pre>
<blockquote>
<p>Alakalı alakasız fotoğraflar ile örneği denemekte yarar var. Eğitmene ne kadar çok örnek anlatır ve tag kullanırsak tahminleme sonuçları da o oranda başarılı olacaktır. Tabi sistemi yanılgıya da düşürmeliyiz. Söz gelimi bir pırasa resmi göstersek ne yapar şu kıt bilgisiyle, sorarım?</p>
</blockquote>
<h3>Python Kodları ile Test Etmek</h3>
<p>Postman ile test yapmak işin kolay yollarından birisi. Diğer yandan client.py isimli uygulamayı çalıştıraraktan da denemeler yapabiliriz. Bu uygulama test klasörü altındaki imajları tarar ve her biri için POST talebi göndererek tahminleme sonuçlarını ekrana basar <em>(Eğitime tabii olan <a href="https://github.com/buraksenyurt/saturday-night-works/tree/master/No%2026%20-%20Custom%20Vision%20Service%20with%20Python/Images" target="_blank">örnek fotoğraflar images klasörü altında</a> yer alıyor. Github üzerinden alıp kullanabilirsiniz)</em></p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">python client.py</pre>
<p>Test klasöründeki imajlar için aşağıdaki sonuçlar elde edildi.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2019/08/credit_10.png" alt="" /></p>
<p>Einstein ve havadaki uçak için çok başarılı tahminlemeler yapılmadığını görebiliriz. Bunun sebebi eğitmeni sadece 10 imajla yetiştirmiş olmamızdır. Yani görüp gördüğü ve yorumladığı küme çok sığ. Örnek kümeyi ve tag yapısını ne kadar geniş tutarsak tahminleme oranlarında o kadar isabetli sonuçlar elde ederiz. Bunu sanırım üçüncü kez söyledim :S</p>
<blockquote>
<p>Bu arada dosya tip kontrolü için client.py'de filetype modülünü kullandık. Yüklemek için terminalden <em>pip install filetype</em> yazmamız yeterli.</p>
</blockquote>
<h2>Ben Neler Öğrendim?</h2>
<p>Doğruyu söylemek gerekirse böyle hazır Cognitive servislerle eğlencesine de olsa örnek çalışmalar yapmak son derece keyifli. Sonuçta kafam AI dünyasına basmadığı için sınıflandırma algoritmalarını yazmaya çalışmak yerine onu ele alan servisleri kullanmak daha cazip geliyor. Benim bu çalışmada torbama kattıklarımı ise şöyle özetleyebilirim.</p>
<ul>
<li>Vision API'ye bir fotoğrafı nasıl öğretebileceğimi</li>
<li>Python ile kod tarafında bunu nasıl yapabileceğimi</li>
<li>Temel olarak Azure Custom Vision Service'in AI çalışma mantığını <em>(bir takı için en az beş örnekten oluşan fotoğraf kümeleri oluştur. Ne kadar çok olursa o kadar iyi olur. Bu nedenle koddaki gibi imgeleri tek tek öğretmek yerine, bir klasör altına n tane imge koyup onları bir tag ile ilişkilendirmek daha mantıklı)</em></li>
<li>Oluşturulan servisin python tarafında nasıl tüketilebileceğini</li>
<li>Python tarafında request modülünü kullanarak HTTP Post talebinin nasıl yapılabileceğini</li>
<li>request modülü kullanılırken Header ve Data bilgilerinin nasıl eklendiğini</li>
<li>Bir klasördeki dosyaları nasıl dolaşabileceğimi</li>
</ul>
<p>Böylece geldik <a href="https://github.com/buraksenyurt/saturday-night-works/tree/master/No%2026%20-%20Custom%20Vision%20Service%20with%20Python" target="_blank">26 numaralı saturday-night-works derlemesi</a>nin sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2019-08-23T13:00:00+00:00pythonazurevisionApiaipipcustom vision apicognitive servicespredictionposthttppostmanubunturestbsenyurtAmacım, Microsoft Azure platformunda yer alan ve fotoğraf/nesne sınıflandırmaları için kullanılabilen Custom Vision servisini basit bir Python uygulaması ile deneyimlemek. Custom Vision API'si geliştircilere kendi fotoğraf/nesne sınıflandırma servislerini yazma imkanı tanımakta. Onu, imajları belli karakteristik özelliklerine göre çeşitli takılar (tag) altında sınıflandırıp sıralayan bir AI (Artificial Intelligence) servisi olarak düşünebiliriz.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=4159d417-917f-4e4f-8abc-6957b65b83d90https://www.buraksenyurt.com/trackback.axd?id=4159d417-917f-4e4f-8abc-6957b65b83d9https://www.buraksenyurt.com/post/microsoft-custom-vision-servisini-python-ile-kullanmak#commenthttps://www.buraksenyurt.com/syndication.axd?post=4159d417-917f-4e4f-8abc-6957b65b83d9https://www.buraksenyurt.com/post/Azure-Uzerinde-Redis-Cache-KullanımıAzure Üzerinde Redis Cache Kullanımı2018-09-01T07:02:00+00:00bsenyurt<p><img style="float: right;" src="https://www.buraksenyurt.com/image.axd?picture=/2018/06/aredisc_02.gif" alt="" />Merhaba Arkadaşlar,</p>
<p>Bir süredir kişisel becerilerimizle alakalı olarak karşımıza çıkan T-Shaped Person isimli bir konu var<em>(Aslında yıllardır var)</em> Daha yakın zamanda katıldığım Scrum eğitiminde tekrardan karşıma çıkan ve <a href="https://medium.com/@jchyip/why-t-shaped-people-e8706198e437" target="_blank">hatta şuradaki yazıyla</a> kısaca bilgilenebileceğiniz, özetle bir alanda gerçekten uzman ama bu alanla alakalı yan dallarda da bir şeyler yapabilen insan modelinden bahsediyorum.</p>
<p>Tek kişi için düşündüğümüzde bile çok yönlü bir birey geliyor aklımıza ama özellikle bir takımı bu tip insanlardan oluşturduğumuzda birbirlerinin açıklarını kapatabilen başarılı ekiplerin ortaya çıktığını görüyoruz. I-Shaped yerine T-Shaped olmak daha mühim bu nedenle. Özellike çevik ekiplerin başarısında önemli bir yere sahip.</p>
<blockquote>
<p>İşin aslı bir çok şekil var hayatımıza girmiş olan. I, T-shaped dışında M-Shaped, Comb-Shaped, Pi-Shaped, E-Shaped diye gidiyorlar araştırabildiğim kadarıyla. Üşenmeyin siz de araştırın. Bakın <a href="https://peoplecentre.wordpress.com/2017/06/19/the-m-shaped-employee/" target="_blank">burada da eneteresan bir yazı</a> var konu ile ilgili.</p>
</blockquote>
<p>Ben T-Shaped birisi olmaya çalışıyorum ama oldukça yavaş kaldığımı söyleyebilirim. Aslında kendi mesleğimle ilgilli pek çok konuda epey geriden geliyorum. Belki de sadece öğrensem ve yazmak için uğraşmasam şimdilerde daha ileri bir noktada olabilir T-Shaped forma biraz daha uyabilirdim. Ancak işin doğrusu öğrendiklerimi paylaşmayı seviyorum. En azından deneyimlerimi, konu ile ilgili başıma gelenleri aktarma fırsatı bulduğumu düşünüyorum.</p>
<p>Belki de bilmem kaç yüzünce kez karşılaştığınız bir konu ile karşınızdayım bu sebeple :) Özellikle NoSQL dünyasını tanıyanların yakından bildiği Redis ve Microsoft Azure platformuna konuk olacağız bu yazımıda. Amacımız bir <a href="https://redis.io/topics/introduction" target="_blank">Redis</a> Cache hizmetini devreye almak ve basit bir .Net Core istemcisinden yararlanarak kendisiyle konuşmak. Redis, bellek tabanlı çalışan en popüler veri deoplama sistemlerinden birisi. In-Memory Data Structure Store olarak ifade ediliyor hatta. String, Hash, List, Set, Sorted Set, Bitmap, HyporLogLog gibi çeşitli veri türlerinin tutulmasına olanak sağlıyor. Ağrılıklı olarak uygulamaların performans kazanımı gerektiren vakalarında değerlendiriliyor. Bu, özellikle back-end tarafı için önemli. Veriyi bellek üzerinden yapısal olarak anlamlı şekilde tutmak ve hatta dağıtılabilir olarak sunmak büyük ölçekte istenen bir kabiliyet.</p>
<p>Bu açılardan düşünüldüğünde bulut bilişim hizmetlerinin de olmazsa olmaz kalemlerinden birisi olarak karşımıza çıkıyor. Öyle ki, bulut üzerine aldığımız uygulamaların veriye hızlı erişmesi gerektiği durumlarda ciddi anlamda kullanılıyor. Sık sık ihtiyaç duyulan, sürekli olarak değişime uğramayan verilerin fiziki diskten çekilmesi yerine bellekten alınması elbette performans açısından daha iyi. Lakin bunu dağıtık sistemler, ölçeklenebilirlik, veri çeşitliliği gibi noktalardan düşündüğümüzde Redis gibi çözlümlere yönelmemiz gerekiyor.</p>
<p>Artık kurulumundan, duruma göre ölçeklendirme planlarının oluşturulmasına kadar pek çok yönetsel işlevin bulut sistemleri tarafından sağlanıyor olduğunu da görüyoruz. Microsoft Azure platformu bu anlamda bizlere önemli bir imkan sunuyor. Azure üzerindeki Redis Cache hizmetini kullanarak bu tip bir tesisatı oluşturmak son derece kolay. Nasıl mı? Haydi gelin bir "Nasıl Yapılır?" macerasına daha başlayalım.</p>
<h1>Redis Cache Kaynağını Oluşturmak</h1>
<p>İşe azure portal üzerinden redis cache araması yaparak başlayabiliriz(<em>Bu aşamada Microsoft Azure aboneliğinizin olduğunu varsayıyorum)</em></p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/06/aredisc_1.gif" alt="" /></p>
<p>Redis Cache öğesini bulduktan sonra tek yapmamız gereken yeni bir tane oluşturmak. Diğer pek çok hizmette olduğu gibi isim, kaynak grubu, lokasyon ve benzeri bilgileri girmemiz gerekiyor. Ben DNS adı olarak Gondor'u kullandım ve "Kullandıkça Öde" tipindeki aboneliği seçtim. Bu vakaya özel olmasını istediğim için gondor-redis-rg isimli yeni bir Resource Group belirttim. Lokasyon olarak da West Europe tarafındaki sunucu merkezini işaret ettim.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/06/aredisc_2.gif" alt="" /></p>
<p>Burada da bir fiyatlandırma söz konusu elbette :) Ücretsiz bir kullanımını bulamadım ancak geliştirme amacıyla C0 Basic isimli modeli değerlendirmemiz mümkün. Tabii başka modellerde var. "View Full Pricing Details" bağlantısına basarsak diğer seçenekleri görebiliriz.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/06/aredisc_3.gif" alt="" /></p>
<p>İhtiyaca yönelik olarak doğru modeli seçerek ilerlemek önemli. Gerekli bilgiler sonrası oluşturma işlemi başlatılabilir. Redis Cache bir kaç dakika içinde kullanıma hazır hale gelecektir. Başlangıç için tek Node'dan oluşan 250 MB kapasiteli, SSL desteği veren ve 256 bağlantıya kadar çıkabileceğimiz bir Redis Cache hizmeti söz konusu.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/06/aredisc_4.gif" alt="" /></p>
<p>Burası bir veri kaynağı olduğu için doğal olarak Connection String bilgisine de ihtiyacımız var. Show Access Keys kısmından bu bilgilere ulaşabiliriz. </p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/06/aredisc_5.gif" alt="" /></p>
<p>Primary Connection string içerisindeki bilgi .Net Core tarafnda StackExchange.Redis paketi için ele alınabilir formattadır.</p>
<h1>İstemci Uygulamanın Geliştirilmesi</h1>
<p>Aslında portal tarafındaki hazırlıklarımız tamamlanmış durumda. Şimdi basit bir istemci geliştirerek Redis Cache ile konuşmaya çalışalım. Her zaman ki gibi bir Console uygulaması üzerinden ilerleyeceğiz. Öncesinde terminal üzerinden yapmamız gereken bir kaç hazırlık var. Console projesinin oluşturulması, Redis ile konuşmamızı sağlayacak StackExchange.Redis ve JSON serileştirme işlemlerini kolaylaştıracak Newtonsoft.json paketlerinin eklenmesi. Bunun için terminalden aşağıdaki komutları işletebiliriz.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">dotnet new -o console TalkWithGondor
dotnet add package StackExchange.Redis
dotnet add package Newtonsoft.json
dotnet restore</pre>
<p>TalkWithGondor isimli bir Console uygulaması oluşturduk. Kodlara gelince,</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace TalkWithGondor
{
class Program
{
static void Main(string[] args)
{
string conStr = "gondor.redis.cache.windows.net,password=YWG04toTMb45VUTv7hcIcjbIQymuL7IaRp2Z7/5cNNU=,ssl=True,abortConnect=False";
var connection = ConnectionMultiplexer.Connect(conStr);
var db = connection.GetDatabase();
var pingResponse = db.Execute("ECHO","Nabersin?");
Console.WriteLine(pingResponse);
db.StringSet("Motto", "Yağmurlu bir Nisan akşamıydı...");
var mottoValue = db.StringGet("Motto");
Console.WriteLine(mottoValue);
Product box = new Product
{
Id = 10001,
Title = "Lego head box",
UnitPrice = 50
};
db.StringSet("LegoBox", JsonConvert.SerializeObject(box));
Product productFromCache = JsonConvert.DeserializeObject<Product>(db.StringGet("LegoBox"));
Console.WriteLine($"\t{productFromCache.Id}\t{productFromCache.Title}\t{productFromCache.UnitPrice}");
}
}
class Product
{
public int Id { get; set; }
public string Title { get; set; }
public double UnitPrice { get; set; }
}
}</pre>
<p>Neler yaptık kısaca inceleyelim. Redis ile kolayca haberleşebilmek için StackExchange.Redis isim alanındaki tiplerden yararlanıyoruz. ConnectionMultiplexer sınıfını kullanarak bir bağlantı açıyoruz. Connect metoduna parametre olarak Azure portalından aldığımız bağlantı bilgisini koyduğumuza dikkat edelim. Sonrasında GetDatabase metodu ile db isimli bir değişken örneklenmekte. Bu değişken üzerinden çeşitli Execute denemeleri icra etmekteyiz. Redis'in klasik PING ve ECHO gibi selamlaşma fonksiyonları var. İlk Execute işleminde ECHO komutunu kullanarak bir mesaj gönderiyoruz. Redis gönderdiğimiz mesajı bize aynen geri yollamalı. Bir nevi bağlantımızı test ettiğimizi ifade edebiliriz.</p>
<p>İzleyen satırda StringSet ve StringGet kullanımlarına ait örnekler var. StringSet ile tahmin edeceğiniz üzere Redis üzerinde bir key:value çifti oluşturulmasını sağlıyoruz. Veri tipi metinsel içerikten oluşmakta. StringGet ile de Motto anahtar adıyla yolladığımız içeriğin değerini getiriyoruz. Kodun son kısmında ise işimize daha çok yarayacak bir örnek yer alıyor. Bir sınıfa ait nesne örneğini Redis üzerinde nasıl tutabileceğimizi görüyoruz.</p>
<p>Aslında anahtar nokta içeriği JSON formatında saklamaktan ibaret. Sonuçta hangi platform olursa olsun JSON genel bir veri formatı standardı sunuyor. Örnekte yer alan Product nesne örneğinin verisini de bu şekilde tutmamız mümkün. Tabii serileştirme ve ters serileştirme noktasında JsonConvert sınıfının SerializeObject ve DeserializeObject metodlarından yararlanmaktayız. Kodlarımız görüldüğü üzere son derece basit. Zaten basit olması da gerekiyor. Neden işleri karmaşıklaştıralım ki?<em>(Yazar burada OverEngineering'cilere atıfta bulunuyor :P )</em></p>
<p>Uygulamayı çalıştırdığımızda aşağıdaki ekran görüntüsündekine benzer sonuçlar almamız lazım.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/06/aredisc_6.gif" alt="" /></p>
<p>Görüldüğü gibi ECHO ile gönderdiğimiz mesaj bize aynen geri gönderildi. Ayrıca Motto mesajının da başarılı bir şekilde aktarıldığını görmekteyiz. Benzer durum LegoBox anahtar değeri ile tutulan Product nesne örneği için de geçerli. Neredeyse her türden veriyi Redis Cache üzerine almamız mümkün.</p>
<p>Çalışmalar devam ettikçe Redis Cache üzerindeki harketlilikler de artacaktır. Portal üzerindeki Monitoring sekmesini kullanarak çeşitli metrikleri inceleyebiliriz<em>(Tahminimce Amazon Web Services'ler de olduğu gibi belli eşik değerlerine ulaşıldığında devreye girecek alarm mekanizmaları da kurulabiliyordur. Araştırmam lazım)</em> Ben yaptığımız ilk bir kaç deneme sonrası aşağıdaki sonuçlarla karşılaştım.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/06/aredisc_7.gif" alt="" /></p>
<p>Bağlantı sayıları, get ve set operasyon çağrıları, cache nesnelerinin durumları vs. Diğer kaynaklarda olduğu gibi oldukça geniş bir ölçümleme seti var. Biraz kurcalamak lazım. Bu adımları başarılı bir şekilde tamamladıysanız ve Redis Cache ile ilgili başka bir şey yapmayacaksanız size tavsiyem ilgili kaynak grubunu silmeniz olacaktır. Neme lazım arka planda unutulup da ilerleyen zamanlarda fiyatlandırma konusunda bize problem çıkartmasın değil mi?</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/06/aredisc_8.gif" alt="" /></p>
<p>Bu yazımızda Azure tarafından sunulan Redis Cache hizmetini nasıl kullanabileceğimize dair basit bir örnek yapmaya çalıştık. İstemci tarafında sadece .Net Core değil, Python, Node.Js, Java gibi diğer platformları da kullanabiliriz. Detaylı bilgi ve diğer öğretiler için <a href="https://docs.microsoft.com/en-us/azure/redis-cache/cache-overview" target="_blank">Microsoft'un resmi dokümanlarına</a> bir bakmanızı öneririm. İşi daha da ileri götürmek için istemci uygulamanızı da Azure üzerinde host etmeyi deneyibilirsiniz. Pekala bu bir Web uygulaması ya da Web API hizmeti olabilir. Bu uygulamayı App Service olarak host edip Redis Cache'den yararlanmaya çalışabilirsiniz. Hatta dağıtık bir önbellekleme stratejisinin mimari seviyede nasıl ele alınması gerektiğine dair <a href="https://docs.microsoft.com/en-us/azure/architecture/best-practices/caching?toc=%2Fazure%2Fredis-cache%2Ftoc.json" target="_blank">şu adresteki pratiğe</a> de bakabilirsiniz. Daha gerçekçi bir vaka çalışması olacağı kesin. Böylece geldik bir makalemizin daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2018-09-01T07:02:00+00:00azureredisredis cache.net corecloud computingnosqljsonmemory based databasesdatabaseconnection string conventionStackExchange.Redisnewtonsoftmonitoring cancellationresource groupazure portaldotnetconsole applicationhello worldbsenyurtBu yazımızda Azure üzerindeki minik maceralarımıza devam ediyoruz. Amacımız bir Redis Cache hizmetini devreye almak ve basit bir .Net Core istemcisinden yararlanarak kendisiyle konuşmak. Önce portaldan yararlanarak bir Redis Cache hizmetini devreye alacağız. Ardından StackExchange.Redis paketini kullanıp istemci tarafını geliştireceğiz. Redis'e ECHO komutu ile mesaj gönderecek ve kullanıcı tanımlı nesne örneklerini de nasıl kullanacağımız irdeleyeceğiz. Haydi gelin başlayalım.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=709417f1-1a48-49ae-8fad-cc12931109a51https://www.buraksenyurt.com/trackback.axd?id=709417f1-1a48-49ae-8fad-cc12931109a5https://www.buraksenyurt.com/post/Azure-Uzerinde-Redis-Cache-Kullan%C4%B1m%C4%B1#commenthttps://www.buraksenyurt.com/syndication.axd?post=709417f1-1a48-49ae-8fad-cc12931109a5https://www.buraksenyurt.com/post/azure-azure-bu-fotografta-ne-goruyorsunAzure, Azure! Bu Fotoğrafta Neler Görüyorsun?2018-05-25T04:46:00+00:00bsenyurt<p><img style="float: right;" src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/comvision_enter.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Yandaki fotoğrafa baktığınızda neler görüyorsunuz? Hatta neler hissediyorsunuz? Kalın lastikleri olan koyu yeşil renkte Toyota marka bir arazi aracı. Aracın içine bakabilmesi için ufaklığı kollarıyla kaldıran arkası dönük bir kadın. Arka tarafta turuncu kapısı görünen bir başka araç. Olayla pek ilgisi olmayan ilkokul çağında sarışın kıvırcık saçlı bir kız çocuğu. Bluzundaki sembollerden çıkartıldığı kadarıyla Minion'lar. Arka tarafta yükselene sıra dağlar ve diğerleri. İnsan gözüyle fotoğraf dikkatlice incelelendiğinde söyleyebileceklerimizden sadece bazıları. Hatta insani duygularla hareket ederek empati yaptığımızda göreceğimiz farklı detaylar da var öyle değil mi? Ufaklığın yüzündeki meraklı bakışa, aracın içini görmek istercesine annesinin kollarında yukarıya doğru yükselmeye çalışmasına bir baksanıza. Ya da cansız bile olsa aracın tekerlekleri ile ne kadar agresif göründüğüne. İşte bu farklılıkları ve detayları görmek belki de biz insanları makinelerden, düşünmeye çalışan robotlardan ayıran önemli bir özellik. </p>
<p>Ancak oyunun kuralları bildiğiniz gibi uzun süre önce değişmeye başladı. Önce dijital fotoğraf makinelerinin, kameraların gördükleri karelerdeki yüzleri ayırt edişlerine şahit olmaya başladık. Hatta hareket edenleri nasıl yakaladıklarını gördük. Sonra onların renklerini ayırt edebildiklerini ve az çok o fotoğraflarda neler bulunduğunu tahminlemeye çalıştıklarını. Halen daha da görmeye devam ediyoruz. Artık yapay zekanın, öğrenen makinelerin dünyasında olduğumuz için bu fotoğrafı yorumlayışımıza yakınlaşmaları çok da uzak değil gibi. Ne kadar yaklaşabileceklerini zaman içerisinde daha net göreceğiz ama bu çok uzak bir gelecek değil. Öğreniyorlar...</p>
<p>Bugünün gelecek teknolojilerini belirleyen büyük aktörlerin çoğu, bu tip tanıma/tanımlama operasyonlarını sunan servislere sahipler. Google, Amazon, Microsoft, IBM, Facebook ve diğerlerinin başı çektiği bir dünya var artık. Özellikle bulut hesaplamaları alanında hizmet verenlerin sahip olduğu avantajlar yukarıdaki gibi bir senaryonun saniyeler içerisinde gerçeklenmesine de olanak sağlamakta. Bende bu merakla bir şeyler araştırmayı başladım geçenlerde. Pluralsight sağolsun Azure konusundaki çalışmalarıma devam ediyorum. Ufak ufak öğretilerin üzerinden geçerken de neyin nasıl yapıldığını adım adım öğrenmeye çalışıyorum. Bu yazımızda ise uzun zamandır hepimizin varlığından haberdar olduğu Cognitive Services özelliklerinden birisine bakacağız. Azure'un yapay zeka destekli makine öğrenme hizmetlerinden olan Compture Vision enstrümanını kullanarak bir fotoğrafı bizim için nasıl yorumlayableceğini işleyeceğiz.</p>
<blockquote>
<p>Microsoft Cognitive Services temel olarak 5 ana kategoriye ayrılmıştır. Vision, Speech, Language, Knowledge ve Search. Her bir kategori başlığı altında bu alana özgü farklı fonksiyonellikler sunulmaktadır. İlgili listeye <a href="https://azure.microsoft.com/en-us/services/cognitive-services/directory/?v=18.05" target="_blank">şu adresten</a> bir bakmanızı öneririm.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/comvision_ex.gif" alt="" /></p>
</blockquote>
<p>İlk olarak Azure platformunda bu hizmeti kullanabilmek için gerekli hazırlıkları yapıp sonrasında örnek bir kod parçası ile bir kaç fotoğrafı yorumlatacağız. Bir nevi basit How To yazısı olduğunu belirtebilirim. </p>
<h1>Azure Plaforumundaki Hazırlıklar</h1>
<p>İşe Azure Platformu üzerindeki hazırlıklarla başlamamız gerekiyor. Bu aşamada sizlerin Azure hesaplarınız olduğunu kabul ediyorum. Yapmamız gereken arabirimi kullanarak Cognitive Services kısmına ulaşmak. All services penceresinden aratma usulü ile bulabiliriz.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/comvision_1.gif" alt="" /></p>
<p>Ardından Computer Vision enstrümanını bulmalıyız. </p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/comvision_2.gif" alt="" /></p>
<p>AI +Machine Learning kategorisinde yer alan hizmeti bulduktan sonra yeni bir örneğini oluşturabiliriz. Ben aşağıdaki ekran görüntüsünde yer alan bilgileri kullandım.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/comvision_3.gif" alt="" /></p>
<p>Burada söz konusu kaynak için girmemiz gereken bazı bilgiler yer alıyor. Kaynağın adı<em>(Name)</em>, lokasyonu<em>(Location/Data Center)</em>, dahil olacağı grup bilgisi<em>(Resource Group)</em> ve tabii fiyatlandırma seçeneği<em>(Pricing Tier)</em>. F0 planı ücretsiz olduğu ve sadece öğrenme amaçlı bir çalışma yaptığımız için yeterli. Bunun dışında bir seçeneğimiz daha var. S1'e göre 1000 çağrı başına 1 dolardan başlayan bir fiyatlandırma stratejisi söz konusu. Senaryoya göre farklı bir plana da ihtiyaç duyabiliriz. Bu tamamen işlemek istediğimiz kümenin büyüklüğü, ne kadar sık işleneceği ve benzeri kriterlerle alakalı bir konu. Örneğin S1 planı saniyede 10 çağrıma izin verirken Free plan için bu dakikada 20 çağrım ve ayda en fazla 5000 çağrım ile sınırlı <em>(Bu arada F0 planını seçtikten sonra tekrar ikinci bir Computer Vision için ikinci bir F0 planı seçemediğimi fark ettim. Ucuz etin yahnisi misali)</em></p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/comvision_6.gif" alt="" /></p>
<p>F0 seçiminden sonra kaynağı oluşturabiliriz.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/comvision_4.gif" alt="" /></p>
<p>İşlemler başarılı bir şekilde tamamlandığında read-this-photo isimli Computer Vision örneğinin başarılı bir şekilde oluşturulduğunu görmemiz gerekiyor. Kod tarafı için kritik olan iki bilgi de bu ekrandan alınacak. Bunlardan birisi servis adresi<em>(endpoint bilgisi)</em> Yani fotoğraf ile ilgili analizi gerçekleştirecek olan servisin kök adresi. Kök adresi diyorum çünkü duruma göre farklı bir operasyona da gidilebilir<em>(Örneğin fotoğraf için ünlüleri sorgulamak istiyorsak celebrities/model gibi bir operasyon eklememiz gerekir)</em> Diğeri ise iletişim sırasında gerekli olan anahtar<em>(Key 1)</em> değeridir. Anahtar bilgisini Manage Keys kısmından alabiliriz.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/comvision_5.gif" alt="" /></p>
<p>İstemci tarafından servise gelirken KEY 1 değerine ihtiyacımız olacak. </p>
<h1>İstemci Tarafının Geliştirilmesi</h1>
<p>Portal tarafındaki kaynak hazırlıklarımız artık tamamlanmış durumda. Şimdi basit bir istemci uygulaması ile söz konusu servisi deneyimleyebiliriz. Ben örnek kod parçasını Visual Studio Code üzerinde C# kullanarak yazacağım. Console tipinden bir program yeterli olacaktır. Ancak farklı programlama dillerini kullanmamız da mümkün. Ruby, Java, PHP ve diğer desteklenen dillerle geliştirme yapabiliriz. Öncelikle işe aşağıdaki komut satırı ile başlayalım.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">dotnet new console -o HowToComputerVision</pre>
<p>Gelelim program.cs içeriğine.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.IO;
using System.Net.Http;
namespace HowToComputerVision
{
class Program
{
const string endpointAddress = "https://eastus.api.cognitive.microsoft.com/vision/v1.0/analyze";
const string key1 = "c04df99b57b6475182748ebc47d22246";
static void Main(string[] args)
{
string[] samples = { "sample1.jpg", "sample2.jpg", "sample3.png"
, "sample4.jpg", "sample5.jpg","sample6.png" };
foreach (var sample in samples)
{
Analyze(sample);
}
Console.ReadLine();
}
public static async void Analyze(string photo)
{
using (var client = new HttpClient())
{
HttpResponseMessage response = null;
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key1);
var requestParameters = "visualFeatures=Categories,Description,Color,Tags&Language=en";
var uri = endpointAddress + "?" + requestParameters;
var fs = new FileStream(photo, FileMode.Open, FileAccess.Read);
var bReader = new BinaryReader(fs);
var photoData = bReader.ReadBytes((int)fs.Length);
using (var content = new ByteArrayContent(photoData))
{
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
response = await client.PostAsync(uri, content);
string contentStr = await response.Content.ReadAsStringAsync();
Console.WriteLine($"\n{contentStr}\n");
}
}
}
}
}</pre>
<p>Dilerseniz kodda neler yaptığımıza kısaca değinelim.</p>
<p>Aslında Computer Vision bir REST API servisi. Dolayısıyla uygun HTTP çağrılarını yapmamız servisten hizmet almak için yeterli. Tüm iş yükü Analyze metodunda. Fonksiyon içerisinde HTTP çağrısı için HttpClient nesnesinden yararlanılıyor. Servise giderken bazı gerekli bilgileri vermemiz lazım. Örneğin Ocp-Apim-Subscription-Key isimli bir Header değerinin, Azure üzerinde oluşturduğumuz Computer Vision API kaynağı için verilen KEY1 ile POST talebine eklenmesi gerekiyor. Bunu DefaultRequestHeaders.Add satırında yapıyoruz. Bunun dışında neler istediğimizi de söylememiz lazım. API için bu istekler visualFeatures parametresine atanan terimlerle belirlenmekte. Categories, Description, Color, Tags bizim örneğimizde kullanılanlar. Bunların İngilizce olarak yorumlanmasını bekliyoruz.</p>
<p>Kodun takip eden kısmında fotoğrafın byte tipinden içeriğine ihtiyacımız var. Nitekim servise bu içeriği göndermemiz gerekiyor. FileStream ve BinaryReader sınıflarından yararlanarak içeriği yakaladıktan sonra bir ByteArrayContent nesnesi örnekliyoruz. Bu nesnenin içerik tipini belirtmek önemli. Örnekte application/octet-stream türünden bir içerik kullanıldığı belirtilmekte. Talebi awaitable PostAsync metodu ile yolluyoruz. İlk parametre EndPoint ve ikinci parametrede fotoğraf içeriğini taşımakta. Sonuçlar response nesne örneği üzerinden ReadAsStringAsync fonksiyonu ile yakalanıp ekrana basılmakta. Analyze foksiyonu asnekron çalışan bir metod. Bu nedenle çalışma zamanında fotoğraflardan hangisi için cevap döndüyse ona ait JSON içeriği basılıyor. İşlemeye çalıştığımız sırada değil de bittikçe JSON çıktılarını alacağımızı ifade edebiliriz.</p>
<h1>Sonuçlar</h1>
<p>Hemen örnek fotoğrafların sonuçlarna bir bakalım. Çok heyecanlı değil mi? :) Öncelikli olarak bize deneme için bir kaç fotoğraf gerekiyor. Internetten test amaçlı farklı tiplerde fotoğraflar buldum. İlk sırada turuncu sakallı bir Lego manyağı var :P İkinci sırada masa başında bir çok insanın bulunduğu bir tartışma ortamı yer alıyor. Üçüncü sırada son Star Wars filminden sevdiğim bir kare var. Özellikle Computer Vision servisinin Yoda'yı nasıl yorumlayacağını çok merak ediyorum. Acaba ona usta yoda'yı öğretmişler midir? Devam eden fotoğraftaki beklentim ise park yapan araca yaslanmış bir şekilde ayakta duran kadının bulunup bulunamayacağı. 5nci fotoğrafı bilhassa koydum. Gerçek dünayadan olmayan bir çizgi. Bakalım karşı tarafın tepkisi ne olacak? Son fotoğrafımız ise başta konuştuğumuz içeriğe sahip.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/comvision_8.gif" alt="" /></p>
<p>Programın çalışma zamanı çıktısı aşağıdakine benzer olacaktır. Dikkat edileceği üzere fotoğraflar ile ilgili yorumlar servisten JSON fortamında dönmekte. İçerikte categories, description, tags ve color kök elementleri yer alıyor ki bunları istediğimizi endpoint sonuna eklediğimiz ifadelerle biz belirtmiştik.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/comvision_9.gif" alt="" /></p>
<p>Bu fotoğraflardan ilki için gelen JSON çıktısını kısaca değerlendirelim mi? Örneğin benim Boba Fett'in gemisinin Lego'su ile olan fotoğrafımla ilgili şöyle bir çıktı verildi<em>(Zaman ilerledikçe Computer Vision'un fotoğraf öğrenmesi sonucu daha farklı ve isabetli cevaplar vereceğini tahmin ediyorum)</em></p>
<pre class="brush:js;auto-links:false;toolbar:false;" contenteditable="false">{
"categories": [
{
"name": "others_",
"score": 0.0078125
},
{
"name": "outdoor_",
"score": 0.00390625,
"detail": {
"landmarks": [
]
}
},
{
"name": "people_",
"score": 0.42578125,
"detail": {
"celebrities": [
]
}
}
],
"tags": [
{
"name": "person",
"confidence": 0.99906939268112183
},
{
"name": "man",
"confidence": 0.990304172039032
},
{
"name": "indoor",
"confidence": 0.986711859703064
},
{
"name": "shelf",
"confidence": 0.63831371068954468
},
{
"name": "male",
"confidence": 0.1531662791967392
}
],
"description": {
"tags": [
"person",
"man",
"indoor",
"computer",
"holding",
"laptop",
"front",
"shelf",
"sitting",
"shirt",
"using",
"table",
"book",
"food",
"remote",
"young",
"dog",
"black",
"wearing",
"desk",
"control",
"large",
"room",
"keyboard",
"white",
"pizza",
"bed",
"video",
"standing"
],
"captions": [
{
"text": "a man holding a book shelf",
"confidence": 0.66441073283724139
}
]
},
"color": {
"dominantColorForeground": "Grey",
"dominantColorBackground": "Black",
"dominantColors": [
"Black",
"Grey",
"Brown"
],
"accentColor": "274562",
"isBwImg": false
},
"requestId": "4efc41fa-c421-4e70-bd20-c392306f6031",
"metadata": {
"height": 750,
"width": 1500,
"format": "Jpeg"
}
}</pre>
<p>categories kısmına baktığımızda en yüsek skor değeri insan'da görünüyor. Yani insan temalı bir fotoğraf olarak kategorilendirebiliriz. tags bölümünde önerilen takılar bulunmakta. Kapalı mekan olduğu, rafların bulunduğu, bir adamın yer aldığı belirtilmiş. confidence değeri yüksek olanlar tahmini olarak öne çıkan bilgiler. description kısmındaki takılarda fena değil aslında. Mekan ile ilgili az çok tutarlı anahtar kelimelere yer verilmiş. Her ne kadar ben pizza'nın nerede olduğunu pek çıkartamasamda fena sayılmazlar. Ağırlıklı renkler siyah, gri ve kahve olarak belirtilmiş <em>(Renkler aslında ön plan, arka plan ve tüm resim olarak ele alınmaktalar ve 12 dominant renk ele alınmakta; Siyah, mavi, kahverengi, gri, yeşil, turuncu, pembe, mor, kırmızı, deniz mavizi, beyaz ve sarı)</em> Fotoğraf için önerilen başlıkta hoş aslında ki benim Computer Vision'da en çok beğendiğim özelliklerden birisi de bu; "a man holding a book shelf" Diğer fotoğraflar için neler söylediğini bilmek ister misin?</p>
<ul>
<li>Örneğin bir grup insanın masa başında oturduğu ikinci fotoğraf için : "a group of people sitting at a table"</li>
<li>Master Yoda ve Luke Syk Walker'ın yanyana durduğu fotoğraf için : "a man and a woman looking at the camera" <em>(Biraz daha öğrenmesi gerekiyor nitekim hangisi için Woman dedi anlayamadım. Belki de para vermediğim için böyle dedi)</em></li>
<li>Kadının garaj kapısındaki arabanın önünde ayakta durduğu fotoğraf için : "a car parked on the side of a building"<em> (Kadını yakalayamamış belki ama tanımlayıcı tag'ler içerisinde woman kelimesi yer alıyor)</em></li>
<li>Ünlü animasyon filmi Cars'ın renkli afişi için : "a car parked in a parking lot" <em>(oldukça akıllı bir öneri değil mi? Gün gelecek karakterlerin isimlerini de tek tek söyleyecek)</em></li>
<li>ve son olarak Toyota marka arazi aracının olduğu fotoğraf için : "a group of people riding on the back of a truck"</li>
</ul>
<p>Servisin kullanımına ilişkin bir takım çalışma zamanı bilgilerini portal üzerinden de izleyebiliriz. Sonuç itibariyle yapılan işlemleri izlemek önemli. Ne kadar talep gitti, kaçı başarılı oldu, kaçında hata alında, geriye kalan kullanım haklarımız neler, ne kadar ödedik vs... Bu örnek senaryo için Overview kısmındaki grafikler başlangıç için yeterli bilgiler vermekte. Benim yaptığım az sayıda denemenin çıktısı aşağıdaki ekran görütüsündeki gibi oldu.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/comvision_7n.gif" alt="" /></p>
<p>Görüldüğü üzere Azure'un Cognitive servislerinden olan Computer Vision'ı kullanarak fotoğraflar ile ilgili bir takım bilgileri hesaplatmak oldukça kolay. Söz gelimi yoğun fotoğraf kullanan bir katalog sisteminde fotoğrafların tag bilgilerinin otomatik olarak çıkartılmasında bu hizmet pekala işe yarayabilir. Arka plandaki AI+ML işbirlikteliği daha da güçlendikçe fotoğrafların yorumlanması daha da iyileşecektir. Örneğin bir kamera görüntüsündeki olası saldırganın otomatik olarak tespit edildiğini bir düşünsenize<em>(Aslında ben bu cümleyi yazarken böyle bir şeyin yapılmadığından emin değilim. Yapılıyor da olabilir. Araştırmam lazım)</em> Azure tarafında Computer Vision servisinin pek çok gelişmiş fonksiyonu bulunuyor. Bu fonksiyonlarla resimlerin sınıflandırılması, tanımlanması, thumbnail formatlarının oluşturulması, taxonomy<em>(SEO tarafında önem arz eden bir konudur ve yazının hazırlandığı tarih itibariyle Microsoft 86 kategori başlığından bahsediyordu)</em> veya domain bazında kategorilendirilmesi, clip-art statüsünde olup olmadıklarının berlilenmesi, elle çizilip çizilmediklerinin anlaşılması, cinsel içerik içermediğinin tespit edilmesi ve daha bir çok şey mümkün. İlerleyen zamanlarda elbette yeni fonksiyonellikler de eklenecektir. Dilerseniz siz bu örnekten yararlanarak kendi fotoğraf albümlerinizden seçtiğiniz görüntüleri Computer Vision'a yorumlatmayı deneyebilirsiniz. Böylece geldik bir makalemizin daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<h3>Kaynaklar:</h3>
<p><a href="http://devnot.com/2017/microsoft-cognitive-services-computer-vision-api/" target="_blank">Cognitive Services (DevNot'tan)</a></p>
<p><a href="https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/quickstarts/csharp" target="_blank">Microsoft'un Quickstart Dokümanı</a></p>
<p><a href="https://docs.microsoft.com/en-us/azure/cognitive-services/computer-vision/vision-api-how-to-topics/howtocallvisionapi" target="_blank">Microsoft'un How To Call Vision Api Dokümanı</a></p>
<p><a href="https://www.microsoft.com/cognitive-services/en-us/computer-vision-api" target="_blank">Computer Vision API</a></p>2018-05-25T04:46:00+00:00azurecloud computingaicognitive servicesmachine learning.net.net corec#visual studio codecomputer visionmicrosoftrestbsenyurtPluralsight sağolsun Azure konusundaki çalışmalarıma devam ediyorum. Ufak ufak öğretilerin üzerinden geçerken de neyin nasıl yapıldığını adım adım öğrenmeye çalışıyorum. Bu yazımızda zun zamandır hepimizin varlığından haberdar olduğu Cognitive Services özelliklerinden birisine bakacağız. Azure'un yapay zeka destekli makine öğrenme hizmetlerinden olan Compture Vision enstrümanını kullanarak bir fotoğrafı bizim için nasıl yorumlayableceğini göreceğiz.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=cdce6fd7-7bd1-440b-baad-2c41dfc9dd632https://www.buraksenyurt.com/trackback.axd?id=cdce6fd7-7bd1-440b-baad-2c41dfc9dd63https://www.buraksenyurt.com/post/azure-azure-bu-fotografta-ne-goruyorsun#commenthttps://www.buraksenyurt.com/syndication.axd?post=cdce6fd7-7bd1-440b-baad-2c41dfc9dd63https://www.buraksenyurt.com/post/azure-ile-ilk-maceram-app-service-ve-nodejsAzure ile İlk Maceram (App Service)2018-05-18T18:15:00+00:00bsenyurt<p><img style="float: right;" src="https://www.buraksenyurt.com/image.axd?picture=/2018/04/nonazure_0.gif" alt="" />Merhaba Arkadaşlar,</p>
<p>Sinema tarihinin en araştırmacı en gözüpek en maceraperest arkeoloğu kimdir desek herhalde aklımıza tek bir isim gelir; Indiana Jones. Geçenlerde DVD arşivimden şöyle yanında patlamış mısırla izleyeceğim güzel bir macera filmi bakıyordum. Bu yaşıma kadar aslında bir çok ünlü seriyi arşivime eklemiştim. Baba, Matrix, Mad Max, Star Wars, Back to the Future, Terminator, Lord of the Rings ve diğerleri. Derken Indiana Jones çıktı karşıma ve gecenin izlencesi belli oldu. Mısırlar patlatıldı, naneli limonatalar hazırlandı, DVD takıldı, perdeler indirildi ve seyir başladı. Pek tabii Indiana Jones'un bir profesör olmasından çok atıldığı maceralardı seyirciyi ekrana bağlayan. Ona can veren Harrison Ford'un ince esprileri de cabasıydı. Filmi büyük bir keyifle tamamladıktan sonra geçtim West-World'ün başına. Bir Indiana Jones değildim ama benim de kendi çapımda minik maceralarım vardı. Sıradaki serüven yüksek tepelerin ardında, ihtişamlı bulutları ile göz kamaştıran Azure hanedanlığına doğru olacaktı.</p>
<p>Bulut bilişim dünyasının başrol oyuncularını düşündüğümüzde karşımıza Amazon Web Services, Google Cloud Platform ve Microsoft Azure çıkıyor<em>(İlk harflere göre sıralayarak yazdım :P)</em> Neredeyse hepsinin benzer amaç, araç ve sunduğu hizmetler var<em>(Düşünsenize hepsinde mutlaka tarayıcı üzerinde çalıştırabildiğimiz terminal konsolları bulunuyor)</em> Bu nedenle herhangi birinde deneyimlediğimiz tecrübeleri diğerlerinde tatbik etmek de mümkün<em>.</em> Bu platformlarda hayatın nasıl işlediğini anlamak için sundukları dokümanlardan yararlanmaksa en mantıklısı. </p>
<p>Bu geceki maceramızda Azure'un App Service olarak isimlendirilen ürününü kullanarak node.js ile yazılmış bir web uygulamasını bulut üzerinde yayınlamaya çalışacağız. Daha önceden benzer senaryoları AWS üzerinde deneyimleme fırsatım olmuştu. Son zamanlarda da Plursalsight'tan Azure konulu eğitimleri izlemekteyim. Tüm bunlar beni bu yazıya itmiş durumda diyebilirim.</p>
<blockquote>
<p>Bu yazıdaki örneği kendiniz denemek isterseniz <a href="https://portal.azure.com" target="_blank">Azure</a>'da bir aboneliğinizin olması gerektiğini hatırlatmak isterim. Ben, Free Tier adı verilen ücretsiz hesap ile söz konusu örneği geliştirmekteyim. Sizde ilk deneyimleriniz için bu planı değerlendirebilirsiniz.</p>
</blockquote>
<p>Yazımızdaki temel amacımız Azure'un desteklediği dillerden birisini kullanarak geliştirilen uygulamayı buluta alıp yayınlamaktan ibaret. Konuyu araştırdığım tarih itibariyle PHP, Java, Ruby, Go ve pek tabii .Net Core için destek sunuluyor. Ben elimin bir süredir de sıcak durduğu Node.js dilini seçtim. Azure'un App Service hizmetini Linux tabanlı bir ortam üzerinde deneyimleyeceğiz. Aslında App Service bir web hosting hizmeti olarak düşünülebilir. Kurulumu oldukça kolaydır ve bir plana bağlandığında dağıtım gibi işlemlerde basittir. App Service üzerine Azure'da bir çok hazır şablon bulunmaktadır. Mobil uygulamalardan, medya hizmetlerine, Joomla menşeli blog alt yapısından Asp.Net başlangıç paketine kadar bir çok kullanıma hazır uygulama servisini bu kaynak altında bulabiliriz. Bu detayları bir kenara bırakarak devam edelim.</p>
<p>Gelelim işlemlerimizi nerede yapacağımıza? Evet son derece aptal bir web uygulamamız olacak ama işin en önemli kısmını Azure üzerinde yapacağız. Buradaki operasyonel işlemler için portal üzerindeki Cloud Shell isimli terminalden faydalanacağız. Kodun kendisi local makinemizde yazacağız<em>(Benim için West-World oluyor)</em> Yerel bilgisayardaki web uygulamasını Azure App Service üzerine kuracağımız ortama alınması içinse git'ten yararlanacağız.</p>
<p>Sıralı bir şekilde gittiğimiz takdirde çok da kafa karıştırıcı olmayan basit işlemler icra edeceğimizi ifade edebilirim. Haydi gelin işe Cloud Shell'i açarak başlayalım. Tabii öncelikle portal'a girmemiz ve geçerli bir abonelik üzerinden oturum açmamız gerekiyor. Cloud Shell iki seçenek sunan bir terminal arabirimi. Bash Shell veya Windows Powershell kullanabiliriz. Ben Bash Shell seçeneğini tercih ettim. Bu durumda aşağıdaki ekran görüntüsü ile karşılaşmalıyız.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_1.gif" alt="" /></p>
<blockquote>
<p>Yazıdaki işlemlerimizi Cloud Shell aracılığı ile yapacağız ama Azure'un komut satırı aracını yerel makine üzerinden de kullanabiliriz. Bunun için <a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest">şu adrese</a> uğramanızı önerebilirim.</p>
</blockquote>
<p>Operasyonel işlemlerimiz için az isimli komut satırı programından yararlanacağız. Yazıyı hazırladığım tarih itibariyle Common Language Interface'in 2.0 sürümü kullanılıyordu. Cloud Shell tahmin edeceğiniz üzere online bir terminal ve üzerinde çalışmakta olduğumuz sanal bir makine. Yapacağımız ilk iş aşağıdaki terminal komutunu vererek dağıtım operasyonunu üstlenecek bir kullanıcı oluşturmak.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">az webapp deployment user set --user-name abi-wan-kenobi --password <<buraya okkalı bir şifre girin>></pre>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_2.gif" alt="" /></p>
<p>Bir deployment kullanıcısı oluşturduk. Bu kullanıcı yerel makinedeki kodları Azure platformuna git üzerinden aktarırken gerekli olacak. Ancak tek yol git kullanmak da değil. FTP bağlantısı yaparak da uygulamamızı taşımamız mümkün ki bu senaryoda da yukarıda oluşturulan kullanıcıya ihtiyacımız olacaktır. Diğer platformlardakine benzer olarak kullanıcılar ve kullanıcıların yetkileri önemli. Gerçekten belli işler için sadece yapacaklarına ait yetkileri taşıyan kullanıcılar oluşturma alışkanlığını kazanmak gerekiyor. Kısacası her şeyi yapabilen tek bir süper kullanıcı kullanmamalıyız. İşte bu yüzden senaryomuza sadece dağıtım işinden sorumlu olacak bir kullanıcı tanımı ile başladık. </p>
<p>Sıradaki adımda bir Resource Group oluşturacağız. Bunu n sayıda kaynağı barındıran bir taşıyıcı<em>(container)</em> olarak düşünebiliriz. Azure üzerindeki enstrümanlar birer kaynak<em>(resource)</em> olarak tanımlanmakta. Sanal makine<em>(Virtual Machine)</em>, veri tabanı<em>(database)</em>, dağıtım yapan kullanıcı<em>(deployment user)</em>, depolama alanı<em>(disk storage)</em>, bu örnekteki web uygulaması<em>(web app)</em> vs. Senaryoya göre bir kaynak grubunun tanımlanmasının yönetimsel açıdan avantajları bulunuyor. Kaynak gruplarını tanımlarken önemli kriterlerden birisi da lokasyon. Aslında kaynaklar Microsoft'un dünya üzerindeki farklı lokasyonlarında konuşlandırılmış da olabilirler. Dolayısıyla bir Resource Group farklı lokasyonlarda duran kaynakları içerebilir. Aslında içermekten kasıt sadece metadata'sında bu kaynakların bilgilerini tutmasıdır. Sonuçta Resource Group'un sahip olduğu metadata'nın da bir yerlerde duruyor olması gerekir. Kullanılabilecek lokasyonların listesini Cloud Shell'den öğrenmek de mümkün. Örneğin aşağıdaki terminal komutunun çıktısı olarak Linux tabanlı ve App Service desteği sunan lokasyonları görebiliriz.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">az appservice list-locations --sku S1 --linux-workers-enabled</pre>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_3.gif" alt="" /></p>
<p>Ben East US 2 bölgesinde milano-rg isimli bir kaynak grubu tanımlamaya karar verdim. Bunun için aşağıdaki terminal komutundan yararlanılabilir.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">az group create --name milano-rg --location "East US 2"</pre>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_4.gif" alt="" /></p>
<p><em>(az terminal komutlarının çıktısı dikkat edeceğiniz üzere JSON formatında)</em> Son terminal komutundaki çıktya göre provisioningState için Succeeded değeri dönüldü. Yani grup başarılı bir şekilde oluşturuldu.</p>
<p>Şimdi App Service için bir plan oluşturacağız. Bir plan içerisinde genellikle ücretlendirme modeli ve taşıyıcı tipi gibi bilgiler bulunur. Söz gelimi bu örnek kapsamında en uygun ve ucuz olan kiralama modeline sahip sanal makineyi seçip taşıyıcı tipi olarak da Linux çekirdekli bir ortamı tercih etmek istersek aşağıdaki terminal komutunu çalıştırmamız yeterli olacaktır.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">az appservice plan create --name milano-app-plan --resource-group milano-rg --sku S1 --is-linux</pre>
<p>--sku S1 ile S1 kodlu fiyatlandırma modelini kullanacağımızı, --is-linux ile de Linux Container üzerinde çalışacağımızı belirtmiş olduk. Özellikle planları oluştururken gereken ücretlendirme modellerine bakmakta yarar var. <a href="https://azure.microsoft.com/en-us/pricing/details/app-service/" target="_blank">Şu adresten</a> gerekli bilgilere ulaşabilirsiniz. Bir çok plan söz konusudur. Planlarda belirtilen sku'larda makinenin çekirdek sayısı, günlük yedek alma miktarı, kaç Gb Ram'e sahip olacağı, disk kapasitesi, hangi diğer uygulama servislerini sunacağı<em>(SQL, Biztalk vs), </em>eş zamanlı instance değerleri gibi özellikler tanımlıdır. App Service ile App Service Plan arasında kritik bir ilişki de vardır. Birden fazla App Service'in aynı App Service Plan'a bağlanması mümkündür. Yani farklı uygulamaları barındıran farklı App Service örneklerini aynı servis planı ile ilişkilendirebiliriz. Bunun ölçeklemelerde önemli bir artısı vardır. Tek bir planı yukarı<em>(Scale Up)</em> veya aşağı<em>(Scale Down)</em> çekerek kendisine bağlı olan tüm uygulamaların bu ölçeklemeden aynı anda yararlanmasını sağlayabiliriz. Burada ister yukarı ister aşağı yönlü ölçekleme olsun, ilgili App Service Plan'a ait makine örneklerinin sayısının arttırılması veya azaltılması durumu söz konusudur.</p>
<blockquote>
<p>Araya bir ekran görüntüsü koyarsam sanırım daha anlaşılır olabilir. Node.js Starter Kit tipinden bir App Service ve buna bağlı bir plan seçerken Azure...</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_13.gif" alt="" /></p>
</blockquote>
<p>Planımızı komut satırından oluşturarak devam edelim.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_5.gif" alt="" /></p>
<p>Şu ana kadar bir Deployment User, Resource Group ve App Service Plan oluşturduk. Peki uygulama nerede? Öncelikle Node.js'in çalışabileceği bir imaja ihityacımız var. Aslında Azure'casını ifade edersek bir Web App üretmemiz gerekiyor. Dilersek Azure'un desteklediği Linux tabanlı Web App çalışma zamanlarına bakabiliriz. Bunun için aşağıdaki terminal komutunu kullanmamız yeterli.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">az webapp list-runtimes --linux</pre>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_6.gif" alt="" /></p>
<p>Ruby, Node'un epey bir sürümü, PHP, .Net Core <em>(2.1 olmaması yazıyı hazırladığım tarih itibariyle ilginçti)</em>, Java ve Go...Node'un 9.4 versiyonunu destekleyecek bir Web App oluşturmak için terminalden aşağıdaki komutu vermek yeterli.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">az webapp create --resource-group milano-rg --plan milano-app-plan --name FishingServices --runtime "NODE|9.4" --deployment-local-git</pre>
<p>Komutta kullandığımız bir kaç parametre var. Resource Group, App Service Plan, uygulamanın adı<em>(ki örneğimizde FishingServices olarak geçiyor)</em>, çalışma zamanı<em>(node 9.4'ü seçtik)</em> ve deployment seçeneği<em>(bu da Git olarak belirtildi)</em> Buna göre uzun bir JSON çıktısına sahip olacağız ancak içerisinde bizim için önemli bilgiler var. </p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_7.gif" alt="" /></p>
<p>Birisi deploymentLocalGitUrl ve diğeri de defaultHostName. Çıktıya dikkat edilecek olursa fishingservices.azurewebsites.net isimli bir adres yer alıyor. Eğer bu adrese gidersek aşağıdakine benzer bir çıktı ile karşılaşmamız olası<em>(İlk talepte cevap süresi biraz uzun olabilir)</em></p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_8.gif" alt="" /></p>
<p>Bu hazır bir şablon ancak şu noktada Azure App Service üzerinden bir Web Hosting işlemi gerçekleştirdiğimizi söyleyebiliriz. Tabii amacımız buraya kendi yazdığımız Node.js uygulamasını taşımak. Ben bunun için West-World'deki Visual Studio Code'u kullanarak aşağıdaki içeriğe sahip basit bir index.js dosyası oluşturdum. Internet'te konu ile ilgili örnek dokümanlara baktığınızda da benzer kod parçaları ile karşılaşabilirsiniz. Temel amacımızın Azure tarafı olduğunu hatırlatalım.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">var http = require('http');
var server = http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/html"});
response.end("<h1>Fishing services and utilities.</h1><p>Under Construction</p>");
});
var port = process.env.PORT || 4454;
server.listen(port);
console.log("Server is online http://localhost:%d", port);</pre>
<p>Ekrana çok düz bir HTML içeriği basılıyor. Bunun için createServer metoduna alınan callback fonksiyonundan yararlanılmakta. writeHead ile istemciye HTTP 200 bilgisini döndürüyoruz. Yani her şey yolunda. end fonksiyonunun içerisindeyse tahmin edeceğiniz üzere HTML içeriğimiz yer alıyor. Oluşturulan server nesnesinin listen fonksiyonu ile de 4454 numaralı porttan yayın hizmet vereceğimizi ifade ediyoruz. Tabii bu lokal makine için geçerli. Uygulamayı Azure ortamına taşıdığımızda port bilgisi process.env.PORT üzerinden otomatik olarak elde edilecek. Bu arada kodun olduğu klasörde</p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false">npm init</pre>
<p>ile package.json dosyasını oluşturup içeriğini aşağıdaki gibi düzenleyebiliriz.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">{
"name": "fishing-app",
"version": "1.0.0",
"description": "sample azure app",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
},
"author": "burak selim senyurt",
"license": "ISC"
}</pre>
<p>start elementini elle eklememiz gerekiyor. Bildiğiniz gibi bu sayede söz konusu uygulamayı terminalde</p>
<pre class="brush:html;auto-links:false;toolbar:false" contenteditable="false">npm start</pre>
<p>komutunu vererek başlatabiliyoruz ki bu aynı zamanda Azure tarafındaki ortam için de gerekli.</p>
<p>Peki şimdi ne olacak? Azure'a uygulamayı taşıyabileceğimiz bir Web App ekledik. Lokal makinemizde de Node.js ile yazılmış ve çalışan bir programımız var. Hazırlıklarımıza göre geliştirici makinesindeki uygulamayı git ile Azure'a alabiliriz. Bunun için ilk adım olarak yerel git deposunu Azure'a bağlamamız gerekiyor. West-World için bu işlem aşağıdaki terminal komutu ile sağlanabildi.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">git remote add milano https://abi-wan-kenobi@fishingservices.scm.azurewebsites.net/FishingServices.git</pre>
<p>Azure tarafında üretilen git adresini uzak bir bağlantı seçeneği olarak ekliyoruz. Bundan sonra tek yapılması gerekense </p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">git push milano master</pre>
<p>komutunu çalıştırmak. Yani kodlarımızı milano olarak isimlendirdiğimiz uzak adrese doğru aktarmak. Bu işlem sırasında bir şifre de sorulacaktır. Bilin bakalım bu şifreyi ne zaman ve nerede belirledik :) </p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_9.gif" alt="" /></p>
<p>Dağıtım işlemi başarılı bir şekilde tamamlandıktan sonra fishingservices.azurewebsites.net adresine tekrar gidersek içeriğin değiştiğini ve Node.js ile yazdığımız uygulamanın çalıştığını rahatlıkla görebiliriz.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_10.gif" alt="" /></p>
<p>Uygulama kodunda değişiklikler yapacak olursak standart commit işlemini uygulayıp ardından tekrardan push ile dağıtımı yapmamız gerektiğini hatırlatayım<em>(Bunu bir deneyin derim. Hatta uygulamayı bir Web API servisi haline getirip commit'lemeyi ve bu şekilde dağıtmayı deneyebilirsiniz. Özellikle bu durumda uygulamanın bağımlı olduğu npm paketleri varsa bunları karşı tarafa da aktarmak gerekebilir. Acaba burada nasıl bir yol izlenmelidir?)</em> Bu arada eğer portal üzerinden kaynaklara gidersek FishingServices isimli App Service örneğimizi de görebiliriz. Aşağıdaki ekran görüntüsünde kendi hesabımdaki anlık durum yer alıyor. </p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_11.gif" alt="" width="664" height="548" /></p>
<p>Sonuçlar oldukça tatmin edici öyle değil mi? Biraz fazla terminal komutu kullandık ama adım adım oluşumu anladık diye düşünüyorum<em>(En azından benim kafamda biraz daha netleşti)</em> Pek tabii bu ücretsiz planlar bir süre sonra başa dert olabilirler. O nedenle oluşturduklarımızı silersek iyi olabilir ki Microsoft'un kendi öğreti dokümanlarında da bu önerilmekte. İşte bu nokta bir Resource Group oluşturmanın faydasını da göreceğiz. Aşağıdaki terminal komutunu Cloud Shell'den çalıştırdığımızda milano-rg ile ilişkili olarak oluşturulan ne kadar kaynak varsa otomatik olarak silinecek.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">az group delete --name milano-rg</pre>
<p>Ve tabii Fishing Services isimli balıkçı malzemeleri hizmeti veren firmanın sitesi de aşağıdaki hale gelecek.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2018/05/nonazure_12.gif" alt="" width="634" height="345" /></p>
<p>Hepsi bu kadar :)</p>
<p>Bu yazımızda kendi bilgisayarımızdaki bir Node.js uygulamasının Azure App Service üzerine nasıl dağıtılabileceğini incelemeye çalıştık. Ağırlıklı olarak<em>(hatta tamamen)</em> terminal komutlarından yararlandık. Önce dağıtım işlemini üstlenen bir kullanıcı oluşturduk. Kaynaklara ait bilgileri içeren bir Resource Group tanımlaması ile devam ettik. App Service için gerekli planımızı belirledik ve bir Web App oluşturulmasını sağladık. Son olarak yazdığımız basit Node.js uygulamasını taşımak için git aracından faydalandık. Pekala aynı işlemleri Azure Portal üzerinden görsel olarak da gerçekleştirebiliriz. Sizler örneği çok daha uç noktalara taşıyabilirsiniz. Eğer kendi bilgisayarınızda geliştirdiğiniz güzel bir web uygulamanız varsa<em>(hatta bloğunuz)</em> bunu Azure üzerinde konuşlandırmanız son derece kolay. Ama bunu yaparken kiralama modellerine(planlara) bakmayı da ihmal etmeyin. Böylece geldik bir makalemizin daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2018-05-18T18:15:00+00:00azurecloud computingnode.jslinuxapp servicemicrosoftgitweb appbash shellazure app servicebsenyurtYazımızdaki temel amacımız Azure'un desteklediği dillerinden birisini kullanarak geliştirilen uygulamayı buluta alıp, yayınlamaktan ibaret. Konuyu araştırdığım tarih itibariyle PHP, Java, Ruby, Go ve pek tabii .Net Core için destek sunuluyor. Ben elimin bir süredir de sıcak durduğu Node.js dilini seçtim. Azure'un App Service hizmetini Linux tabanlı bir ortam üzerinde deneyimlemeyeceğiz. Aslında App Service bir web hosting hizmeti olarak düşünülebilir. Kurulumu oldukça kolaydır ve bir plana bağlandığında dağıtım gibi işlemlerde basittir. Gelelim işlemlerimizi nerede yapacağımıza?https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=9bc0dfb2-578f-4ad8-b30a-27325476524d0https://www.buraksenyurt.com/trackback.axd?id=9bc0dfb2-578f-4ad8-b30a-27325476524dhttps://www.buraksenyurt.com/post/azure-ile-ilk-maceram-app-service-ve-nodejs#commenthttps://www.buraksenyurt.com/syndication.axd?post=9bc0dfb2-578f-4ad8-b30a-27325476524d