https://buraksenyurt.com/Burak Selim Şenyurt - Node.js2020-12-06T16:16:07+00:00Matematik Mühendisi Bir Bilgisayar Programcısının NotlarıBurak Selim SenyurtBlogEngine.Net Syndication Generatorhttps://buraksenyurt.com/opml.axdBurak Selim SenyurtMatematik Mühendisi Bir Bilgisayar Programcısının Notlarıtr-TRBurak Selim Şenyurt0.0000000.000000https://buraksenyurt.com/post/mountebank-ile-mock-servis-destegi-sunmakMountebank ile Mock Servis Desteği Sunmak2020-12-07T14:00:00+00:00bsenyurt<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/34/mb.jpg" alt="" align="right" />Mountebank, ne zamandır merak ettiğim ve denemek istediğim araçlardan birisiydi. Test senaryolarında kullanmak isteyeceğimiz mock servislerini kolayca inşa edebilmemize olanak sağlayan bir araç olarak tanımlayabilirim. Örneğin test kodumuz arka tarafta belki bir veritabanına bağlanan belki başka bir servis zincirini çağıran ya da farklı bağımlıkları olan bir servisi kullanmak zorunda olabilir. Normal şartlarda bu servisin ayakta olması zorunludur ki testimiz yürüsün. Ancak o anki test vakasının ilerleyen adımlarının çalışması için illaki bu servisin vereceği çıktıya ihtiyacımız yoktur. Test vakası adımlarının devamı için o servisin vereceği çıktının sanki verilmiş gibi yapılarak ilerlenilmesi tercih edilen yöntemlerdendir.</p>
<p>Üstelik kullandığı servisin hep aynı veri setini kullanarak çalışan bir testin, veri değişikliklerinden etkilenmemesi de istenebilir. Böyle durumlarda asıl servismiş gibi hareket eden<em>(Sahtekar/Taklitçi gibi isimlendirebiliriz bunları)</em> ama testin ihtiyacı olup asıl vakayı bozmayacak şekilde kullanılabilen servisleri test senaryosu içerisine monte edebiliriz. Yani bir mock servis ile teste devam edelim diyebiliriz.</p>
<p>İşte Mountebank, mock servislerin host edilmesi noktasında oldukça kullanışlı bir araç olarak karşımıza çıkıyor. Mountebank kendisi ile iletişim için REST API arayüzü sunuyor. Bu API'yi kullanarak Mountebank'a mock servisler eklenebiliyor. Yani bir mock servis ihtiyacımız varsa bunu Mountebank'a yüklemek için HTTP Post çağrısı ile bir şeyler göndermemiz<em>(Stub'lardan oluşan Imposter aktörleri)</em> yeterli oluyor. Mountebank'ın CI/CD hatlarına da entegre edilebildiği ifade ediliyor<em>(ki henüz gözümle görme şansım olmadı)</em> Bu çalışmamın amacı Heimdall<em>(Ubuntu-20.04)</em> üstünde onu deneyimlemek ve nasıl çalıştığını, ne gibi bir çözüm sunduğunu anlayabilmek.</p>
<h2>Senaryo</h2>
<p>Mountebank sunucu uygulamasını ayağa kaldırırken kendisine otomatik olarak en az iki mock servisi kayıt edeceğiz. Bu servisleri imposter olarak görebilmeli ve curl, postman veya herhangibir tarayıcıdan tüketebilmeliyiz. Ayrıca Mountebank sunucusu ayakta iken yine Postman gibi bir aracı kullanıp yeni bir mock servis bildirimini gönderebilmemiz gerekiyor. Mountebank uygulaması ve ilgili servisler ayakta iken elbette bir birim test üzerinden de bu servislerin tüketimini ele almalıyız.</p>
<h2>Ön Hazırlıklar</h2>
<p>Öncelikle Mountebank'ı sistem yükleyerek işe başlamamız gerekiyor. Bunu bir NodeJs uygulaması üzerinden icra edeceğiz. Aşağıdaki adımları izleyerek devam edelim. </p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">mkdir asgard
cd asgard
npm init --yes
# Mountebank paketini npm aracı ile yüklüyoruz
# Birde yazacağımız mock servisleri Mountebank sunucusuna bildirmek için
# node-fetch paketinden yararlanacağız. Dolayısıyla onu da ekliyoruz.
npm i --save mountebank node-fetch
mkdir src
cd src
# port bilgilerini tutacağımız bir konfigurasyon dosyası ile
# Bir Mountebank sunucusunu ayağa kaldırmaktan sorumlu index dosyasını oluşturuyoruz
# Bunlar src dizini altında konuşlanabilirler
touch ports.js index.js
# ve ilk Mock Service'imiz için aşağıdaki dosyayı kullanabiliriz
# yine src altında olabilir
touch ping-service.js
# İkinci servisimizde herhangi bir şehir bilgisini getirmek için kullanacağımız bir mock servis
# Şehir bilgileri normalde bir veritabanında tutuluyor ve primary key değerine göre çekiliyor.
# Ancak test senaryomuzda zaten belli şehirleri alıp ilerlememiz mümkün. Yine de bunu bir servis üstünde
# yapmamız lazım. İşte mock servis bu noktada devreye giriyor (şehir bilgilerini cities.csv dosyasında tutuyoruz)
touch city-service.js</pre>
<p>Kod içeriklerini sırasıyla yazarak ilerleyelim.</p>
<p><strong>ports.js;</strong></p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">module.exports = {
server: 5500, // Mountebank uygulamasının ana servis adresidir
ping_service: 5501, // ping-pong servisinin kullanacağı adrestir
city_service: 5502, // bu çalışacağımız şehir bilgilerini getiren servise ait bir adres
};</pre>
<p><strong>index.js;</strong></p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">// mountebank ve kendi yazdığımız ports modülünü ekledik
const mb = require('mountebank');
const ports = require('./ports');
/*
Mountebank uygulamasını ayağa kaldırdığımızda, yazdığımız mock servislerin de
etkinleştirilmesini sağlayabiliriz.
then fonksiyonuna odaklanın.
*/
const pingService = require('./ping-service');
const cityService = require('./city-service');
// Yeni bir mountebank örneği oluşturuyoruz
mb.create({
port: ports.server,
pidfile: '../mb.pid',
logfile: '../mb.log', // Bir üst klasörde tutacağımız log dosyası bildirimi
protofile: '../protofile.json',
ipWhitelist: ['*']
}).then(function () {
pingService.register(); // pingService'i
cityService.register(); // ve cityService'i register ediyoruz
});</pre>
<p><strong>ping-service.js;</strong></p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">/*
Bu bir Hello World mock servisi.
Mountebank'a register ediliyor.
Mountebank tarafına register edilen bir mock servis imposter olarak tanımlanır.
Imposter içerisinde stub tanımlaları yer alır. Birden fazla stub tanımı olabilir.
Stub'larda ne tür talepler için ne tür cevaplar verileceğinin tanımlandığı yer olarak düşünülebilir.
Örneğin aşağıdaki stub tanımında, JSON formatında bir sözleşme(contract) mevcuttur.
Predicates ile hangi route ve metod için talep alınacağı ifade edilir.
Response kısmında da bu talep için nasıl bir cevap dönüleceği. Örnekte HTTP 200 OK durum bilgisi ile birlikte basit bir JSON cevap verilmektedir.
Yani bu sayede mock servisin talebe karşılık ne döndüreceğini tanımlamış oluruz.
imposter kısmında ise mock servis ile nasıl bir protokol üstünden iletişim kurulacağı,
hangi porttan yayın yapacağı ve stub sözleşmesinde nelerin yer alacağın dair bilgilere toplanır.
Örnekte HTTP protokolünün kullanılacağı ifade edilmektedir.
*/
const fetch = require('node-fetch'); // Mountebank servisine Post işlemini kolaylaştıracak
const ports = require('./ports');
function register() {
const stub = [
{
predicates: [{
equals: {
method: "GET",
"path": "/ping"
}
}],
responses: [
{
is: {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ message: "Pong!" })
}
}
]
}
];
const imposter = {
port: ports.ping_service,
protocol: 'http',
stubs: stub
};
/*
Aşağıdaki kod parçasında Mountebank'ın imposters API'sine HTTP Post ile bir talep gönderme işlemi yer alıyor.
body parametresine yukarırdaki imposter'ın JSON formatına serileştirilen halini gönderdiğimize dikkat edelim.
Böylece bu mock servisini Mountebank sunucusuna kayıt etmiş ve kullanıma açmıl olacağız.
*/
const url = `http://127.0.0.1:${ports.server}/imposters`;
//console.log(JSON.stringify(imposter));
return fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(imposter)
});
}
module.exports = { register };</pre>
<p><strong>city-service.js;</strong></p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">/*
Mountebank ile ilgili yaygın imposter senaryolarından birisi de CSV gibi içeriklerden okunan veriyi döndürmek.
Örneğin bir veritabanından gelen id değerine göre şehir bilgisi döndüren bir servisimiz olduğunu düşünelim.
Test senaryomuzda asıl servis yerine onu taklit eden bir servis kullanmak istiyoruz.
Aşağıdaki gibi bir stub yapısı kullanılabilir.
Cities/1 gibi bir HTTP talebi olursa,
fromDataSource kısmında belirtilen CSV dosysını regex ile sorguluyoruz.
Desenimiz city_id alanını index kabul ederek içeriden bu alana ait satırı buluyor.
Bulunan satır row değişkenine alınıyor ve body kısmındaki map tekniği ile bir JSON sonuç üretiliyor.
Test senaryosu böylece gerçekte veritabanına gitmeyen ama ihtiyacımız olan şehir bilgisi döndürecek taklitçi ile akışını devam ettirebilir.
*/
const ports = require('./ports');
const fetch = require('node-fetch');
function register() {
const stub = [
{
predicates: [{
and: [
{ equals: { method: "GET" } },
{ startsWith: { "path": "/cities/" } }
]
}],
responses: [
{
is: {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: '{ "cityName": "${row}[name]", "cityCode": "${row}[code]" }'
},
_behaviors: {
lookup: [
{
"key": {
"from": "path",
"using": { "method": "regex", "selector": "/cities/(.*)$" },
"index": 1
},
"fromDataSource": {
"csv": {
"path": "src/data/cities.csv",
"keyColumn": "city_id"
}
},
"into": "${row}"
}
]
}
}
]
}
];
const imposter = {
port: ports.city_service,
protocol: 'http',
stubs: stub
};
const url = `http://127.0.0.1:${ports.server}/imposters`;
//console.log(JSON.stringify(imposter));
return fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(imposter)
});
}
module.exports = { register };</pre>
<h2>Çalışma Zamanı</h2>
<p>Uygulamanın çalışma zamanı için aşağıdaki adımları takip etmemiz yeterli. Mountebank server'ını ayağa kaldırmak için asgard klasörü altında aşağıdaki komutu vermek yeterli. Bunun işletilmesi içinse package.json'a start komutunu ekledik. Normal olarak src klasörü altındaki index.js dosyasını çalıştırıyor. Çalıştırılan komut sonrası sunucunun ayakta olup olmadığını anlamak için pekala http://localhost:5500 adresine gidebiliriz<em>(Bir JSON içeriği görmemiz lazım)</em> Hatta gelen json'da belirtilen adreslere giderek yüklenen imposter'ları, servis hareketlerine ait log içeriklerini ve konfigurasyonu da görebiliriz.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">npm start</pre>
<p>Mountebank server'ı npm start ile ayağa kaldırdığımızda 5500 portundan gelecek olan json içeriği aşağıdaki ekran görüntüsündeki gibi olacaktır.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/34/Screenshot_01.png" alt="" /></p>
<p>Mock Servis örneklerini ekledikçe, imposter sözleşmelerinde belirtilen route tanımlarına gidilerek kayıt edilen servislerin çalışıp çalışmadığı kontrol edilmelidir. Örneğin ping-service'i Mountebank'a ekledikten sonra http://localhost:5501/ping adresine talete bulunup, stub->response kısmında belirtiğimiz pong cevabını almamız gerekir.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/34/Screenshot_02.png" alt="" /></p>
<p>Ayrıca birden fazla servisi Mountebank'a ekledikten sonra<em>(ister kod yoluyla ister Postman gibi araçlarla Post ederek olsun)</em> http://localhost:5500/imposters gibi adresten bunları izleyebilir ve gelen talep sayılarına bakabiliriz. Ben ikinci servisi de ekledikten sonra aşağıdaki ekran görüntüsünde olduğu gibi bu durumu gözlemleyebildim.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/34/Screenshot_03.png" alt="" /></p>
<p>Mountebank uygulaması ayakta iken Postman veya muadili bir araçla aşağıdaki çıktıyı gönderdiğimizde de söz konusu servisin imposter olarak eklendiğini görürüz. Yani ille de uygulama içerisinde kod yoluyla servis yüklenmesi mecburi değildir. Mountebank, REST Api şeklinde bir arabirim sunduğundan ekleme, silme vb işlemleri doğru içerikten oluşan talepler ile sağlayabiliriz. Tabii mountebank sunucusu kapandığında bu mock servisler de ömürlerini tamamlayacaktır.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">Kullandığım adres : http://localhost:5500/imposters
Http metodu : POST
Body tipi : raw/json
Body içeriği :
{
"port": 5503,
"protocol": "http",
"stubs": [
{
"predicates": [
{
"equals": {
"method": "POST",
"path": "/creditrisk/check/12345678"
}
}
],
"responses": [
{
"is": {
"statusCode": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"available": "no"
}
}
}
]
}
]
}</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/34/Screenshot_04.png" alt="" /></p>
<h2>Testler</h2>
<p>Mock servisleri yazdık. İyi güzel de bunları Nodejs tarafındaki testlerde nasıl kullanacağız? İşin içerisine Mocha ve Chai paketlerini katsak pek bir güzel olur sanki ;) Hatta Mock servis çağrılarını gerçekleştirmek için axios paketi en ideali. Asgard ile paralel yeni bir proje açıp devam örneğimize edelim.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false"># Asgard klasöründe önce gerekli test ve servis haberleşme paketlerimizi yükleyelim
# Mocha : Belki en popüler test framework'lerinden birisi
# Chai : Behavioral Driven Design'ın TDD üstünde başarılı bir uyarlaması
# Axios : Mountebank servis çağrıları için kullanacağımız modül
npm i --save axios mocha chai
# Sonra yine asgard klasörü içerisindeyken test isimli bir klasör açalım.
# ve içerisine test dosyamızı koyalım
# Ayrıca asgard'a ait package.json içerisinde de gerekli test komutunu vermemiz gerekiyor
mkdir test
touch ./test/index.test.js</pre>
<p><strong>index.test.js;</strong></p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">const expect = require('chai').expect;
const axios = require('axios');
describe('Asgard Mock Servis testleri', () => {
it('Herhangi bir şehirden en az bir kullanıcı bilgisi gelmeli', () => {
var city;
return axios
.get(`http://localhost:5502/cities/3`)
.then(res => res.data)
.catch(error => console.log(error))
.then(response => {
/*
Beklentilerimizi yazıyoruz.
Mock servisinin dönüşü bir object olmalı,
cityName özelliği bulunmalı ve değeri Istanbul olmalı
ayrıca cityCode özelliğinin değeri de 340 gelmeli
*/
expect(typeof response).to.equal('object');
expect(response.cityName).to.equal('Istanbul')
expect(response.cityCode).to.equal("340")
city = response.cityName; // sonraki işlemler için değişkeni sakladım sadece
}).then(() => {
// Burada başka bir test operasyonu icra edilebilir
// console.log(city, "ile ilgili başka testler");
});
});
it('Ping mesajıma karşılık Pong denmeli ve oyun başlamalı', () => {
return axios
.get(`http://localhost:5501/ping`) // servis adresini bozup Fail durumunu da test edebiliriz
.then(res => res.data)
.catch(error => console.log(error))
.then(response => {
expect(typeof response).to.equal('object');
expect(response.message).to.equal('Pong!')
});
});
});</pre>
<p>Test kodlarını tamamladıktan sonra yine iki terminal üzerinden örnekleri denemek lazım. İlk terminalde Mountebank sunucusunu ayağa kaldırıp mock servisleri devreye sokmamız gerekiyor. İkinci terminalde ise yine asgard klasörü altında aşağıdaki komutu işletmeliyiz.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">npm test</pre>
<p>Yazılan iki testin de başarılı olma haline ait bir görüntüyü aşağıda görebilirsiniz.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/34/Screenshot_05.png" alt="" /></p>
<p>Mountebank servisleri ayakta değilken ki durum ise aşağıdaki gibi olacaktır.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/34/Screenshot_06.png" alt="" /></p>
<p>Temel olarak Mountebank'ın nasıl kullanıldığını az çok anladık diye düşünüyorum. Şimdi bunu kendi projelerinizde kullanmayı deneyebilirsiniz. Konu ile ilgili not olarak aldığım birkaç soruyu buradaya da bırakayım.</p>
<ul>
<li>Mountebank uygulamasına bir mock servis sözleşmesini<em>(imposter)</em> NodeJs harici bir uygulamadan da<em>(Örneğin bir .Net Core uygulaması)</em> yollayabilir miyiz?</li>
<li>Bir imposter dosyasına birden fazla stub yüklenebilir mi?</li>
<li>Peki bir stub içerisinde n sayıda prediction ve response çifti bulunabilir mi?</li>
<li>Eklenen bir imposter'ı nasıl silebiliriz?</li>
</ul>
<p>Böylece geldik bir skynet derlememizin daha sonuna. Örneğin tamamına <a href="https://github.com/buraksenyurt/skynet/tree/master/No%2034%20-%20Mountebank" target="_blank">github reposu</a> üzerinden erişebilirsiniz. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2020-12-07T14:00:00+00:00testnodejsmountebankci/cdstubmockingnpmmochachaiaxiosrestbsenyurtMountebank, ne zamandır merak ettiğim ve denemek istediğim araçlardan birisiydi. Test senaryolarında kullanmak isteyeceğimiz mock servislerini kolayca inşa edebilmemize olanak sağlayan bir araç olarak tanımlayabilirim. Örneğin test kodumuz arka tarafta belki bir veritabanına bağlanan belki başka bir servis zincirini çağıran ya da farklı bağımlıkları olan bir servisi kullanmak zorunda olabilir. Normal şartlarda bu servisin ayakta olması zorunludur ki testimiz yürüsün. Ancak o anki test vakasının ilerleyen adımlarının çalışması için illaki bu servisin vereceği çıktıya ihtiyacımız yoktur. Test vakası adımlarının devamı için o servisin vereceği çıktının sanki verilmiş gibi yapılarak ilerlenilmesi tercih edilen yöntemlerdendir.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=d8a46026-1d2f-4723-972b-a7fade0d84eb0https://buraksenyurt.com/trackback.axd?id=d8a46026-1d2f-4723-972b-a7fade0d84ebhttps://buraksenyurt.com/post/mountebank-ile-mock-servis-destegi-sunmak#commenthttps://buraksenyurt.com/syndication.axd?post=d8a46026-1d2f-4723-972b-a7fade0d84ebhttps://buraksenyurt.com/post/nginx-kurgulu-hafif-bir-load-balancer-senaryosuNginx Kurgulu Hafif Bir Load Balancer Senaryosu2020-06-17T09:20:00+00:00bsenyurt<p>Dünyanın en popüler ve hızlı proxy serverlarıdan birisi sanıyorum ki Nginx. Daha önce birçok kez onun üzerinde .Net Core tabanlı servisleri konuşlandırmıştım. Ancak bu defa Nginx'in talepleri dengeli bir şekilde dağıtacak şekilde<em>(Load Balancer olarak)</em> nasıl kullanacağımızı öğrenmeye çalışacağız. Senaryo gereği NodeJS ile yazılmış basit bir dummy servisin birkaç kopyasını çalıştıracağız. Aynı makinede farklı port adresleri üzerinden hizmet verecek bu servislere gelen taleplerin dağıtımını, Load Balancer görevini üstlenecek olan Nginx sunucusu üstlenecek.</p>
<p>Ben bu kurguyu her zaman yaptığım gibi Heimdall<em>(Ubuntu-20.04)</em> üzerinde icra ediyorum. Ancak teorik olarak farklı platformlarda da benzer şekilde ilerleyebilirsiniz. Sisteminizde docker ve NodeJs'in yüklü olması şu an için yeterli. Nginx sunucusunu makineye kurmaktansa Docker imajı ile çalışmak çok daha mantıklı olacaktır. Hatta bu kurgu özelinde kendi docker imajımızı kullanıp Nginx konfigurasyonlarını bu Container içerisinde yapacağız. Öylese işe Dummy servisimizi yazarak başlayabiliriz.</p>
<h2>Dummy Service ve Diğer NPM Kurulumları</h2>
<p>Express paketini kullanan dummy NodeJs servisini aşağıdaki terminal komutları ile oluşturarak senaryomuza başlayalım. Söz konusu servis sevdiğim birkaç özlü sözün listesini istemciye göndermekte. Kobay bir servis olduğundan mümkün mertebe basit olmasında yarar var.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">mkdir DailyQuoteApi
cd DailQuoteApi
# Node proje açılışı
npm init --y
# REST Servis özellikleri için express, HTTP Request loglama için morgan paketlerinin yüklenmesi
npm i --save express morgan
touch index.js
# Global paket olarak Process Manager'ın eklenmesi (servisin birden fazla örneğini çalıştırmak için işimizi kolaylaştıracak)
sudo npm i --g pm2
# versiyon kontrolü
pm2 -v </pre>
<p>index.js içeriğini de aşağıdaki gibi yazabiliriz.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">var express = require('express');
var morgan = require('morgan');
var app = express();
app.use(morgan('combined')); // HTTP Loglama middleware bildirimi
// Tipik bir HTTP Get talebi karşılıyoruz
// quotes olarak gelen yönlendirmelerde çalışacak
app.get('/quotes', function (req, res) {
res.send(quotes);
})
// Varsayılan HTTP Get Talepleri
app.get('/', function (req, res) {
res.send('pong!');
})
// listen çağrısı ile servis uygulaması ayağa kalkıyor
// Birden fazla port seçeneği olabilir.
// Process Manager olan PM2 aracı argv[2] üstünden gelen port ile uygulamayı ayağa kaldırabilir
app.listen(process.argv[2] || process.env.PORT || 4500, () => {
console.log(`Uygulama ayakta ve ${process.argv[2] || process.env.PORT || 4500} nolu porttan dinlemede.`);
});
// Sembolik bir JSON içeriği
// Birkaç özlü söz yer alıyor
var quotes = [
{ "id": 1, "owner": "Nelson Mandela", "content": "The greatest glory in living lies not in never falling, but in rising every time we fall." },
{ "id": 2, "owner": "John Lennon", "content": "Life is what happens when you're busy making other plans." },
{ "id": 3, "owner": "Aristotle", "content": "It is during our darkest moments that we must focus to see the light." },
{ "id": 4, "owner": "Marilyn Monroe", "content": "Keep smiling, because life is a beautiful thing and there's so much to smile about." },
{ "id": 5, "owner": "Oprah Winfrey", "content": "You know you are on the road to success if you would do your job and not be paid for it." },
];</pre>
<p>Dikkatinizi çekmiştir sisteme pm2 isimli bir araç yükledik. Process Manager isimli bu aracı kobay servisimizin birkaç farklı örneğini çalıştırmak için kullanıyoruz. Yeri gelmişken pm2 ile ilgili faydalı birkaç komuta da bakabiliriz. Nitekim Node servisleri arka planda çalıştırırken PM2<em>(Process Manager)</em> aracı epey işe yarıyor. Örneğin,</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false"># --name ile process'i isimlendirelim ki tanımamız kolay olsun
# -f ile Obi Van Kenobi gücü kullanıyoruz (force yahu)
# -- 4501 sıralamasına dikkat. 4501 programa argv[2] ile gelen komut satırı parametre indeksi
# --watch uygulama değişiklikleri otomatik algılansın diye
pm2 start index.js --watch --name app1 -f -- 4501
pm2 start index.js --watch --name app2 -f -- 4502
pm2 start index.js --watch --name app3 -f -- 4503
pm2 start index.js --watch --name app4 -f -- 4504
# Process'leri görmek için kullanılır
pm2 status
# id bilgisi ile process'leri silmek için
pm2 delete 0 1 2 3</pre>
<p>pm2 kullanımına ait çalışma zamanı çıktılarını şöyle resmedebiliriz.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/16/Screenshot_1.png" alt="" /></p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/16/Screenshot_2.png" alt="" /></p>
<h2>Nginx Tarafı</h2>
<p>Şimdi proxy server'ımızı Load Balancer olarak ayarlayalım. Bu amaçla kendi Nginx docker imajımızı kullanacağımızdan bahsetmiştik. Oldukça basit bir imaj. Önemli olan bu imaj içerisinde çalışacak Nginx'in Load Balancer özelliklerini tutacak olan konfigurasyon. Bunu nginx.conf dosyasındaki tanımlamalar ile sağlayacağız.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">mkdir judgedredd
cd judgedredd
touch nginx.conf
touch dockerfile
# Senaryoya özel nginx imajının hazırlanması
# Dockerfile içeriğine göre tap taze bir nginx imajı oluşturuyoruz
sudo docker build -t freshnginx .
# Container çalıştırılır ve 8080 portu dışarıya açılır
sudo docker run -d --name judge-dredd -p 8080:80 freshnginx
# Container çalışıyor mu bir bakmak usüldendir
sudo docker ps</pre>
<p>dockerfile ve conf içeriklerini ise aşağıdaki gibi tasarlamamız gerekiyor.</p>
<p>Dockerfile;</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">FROM nginx
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
EXPOSE 80
CMD ["nginx","-g","daemon off;"]</pre>
<p>nginx.conf;</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">upstream backend {
server 172.17.0.1:4501;
server 172.17.0.1:4502;
server 172.17.0.1:4503;
server 172.17.0.1:4504;
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}</pre>
<p>Konfigurasyon tanımlamalarına göre NGinx sunumuz 80 portuna gelen talepleri <a href="http://backend">http://backend</a> adresine yönlendirecek. Bu adres bir upstream bloğu olarak tanımlanmış durumda ve 4 farklı porta sahip alt adresleri barındırmakta. Bu arada kurguyu hazırlarken bulmakta zorlandığım şeylerden birisi de nginx container'ından makinedeki<em>(Heimdall)</em> nodejs servislerine hangi IP ile çıkıldığını öğrenmek oldu<em>(Benim senaryoda 172.17.0.1)</em> Benzer bir ihtiyaç sizin için de geçerli olabilir. Bu adresi bulmak içinse şöyle bir yol izlenebilir.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">sudo docker container attach judge-dredd</pre>
<p>Ben yukarıdaki terminal komutu ile çalışan container'a log açıp http://localhost:8080/ adresine talep gönderdim. Bu sayede nginx.conf içerisinde kullanılan dış IP adresinin ne olduğunu görmeyi başardım. Servislere docker container'ı içinden gidilip gidilmediğinden emin olmak içinse, container içerisindeki terminale girip curl ile talep göndermeyi ihmal etmedim.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">sudo docker exec -it judge-dredd /bin/bash</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/16/Screenshot_3.png" alt="" /></p>
<p>Şu anki kurgumuzda varsayılan olarak kabul edilen <a href="https://www.nginx.com/resources/glossary/round-robin-load-balancing/" target="_blank">Round-Robin</a> isimli Load Balancer algoritması kullanılmakta. Ancak bu algoritma dışında hash, ip_hash, least_conn gibi farklı modeller de mevcut. Benim pek araştırma fırsatım olmadı ama siz kendi çalışma sahanızda bu modeller arasındaki farklılıkları analiz etmeyi deneyebilirsiniz.</p>
<h2>Çalışma Zamanı</h2>
<p>Gelelim yaptıklarımızın ne işe yaradığını görmeye. Nginx Container'ını çalıştırdıktan sonra curl ile farklı sayıda talebi servisimize doğru gönderebiliriz.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">curl http://localhost:8080/
curl http://localhost:8080/quotes</pre>
<p>Buna göre aşağıdaki ekran görüntülerinde yer alan sonuçların benzerlerini elde edebilmeniz gerekiyor.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/16/Screenshot_5.png" alt="" /></p>
<p>Özellikle alttaki görüntüde 80 için gelen taleplerin 450* portlarında dağıldığını görüyoruz.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/16/Screenshot_4.png" alt="" /></p>
<p>Görüldüğü üzere bir şekilde Docker Container ayağa kalktı, reverse proxy olan Nginx görevini yerine getirdi ve gelen talepleri ilgili servisin çalışma zamanı örneklerine iletti. Senaryomuza göre aynı servisin makine üstünde farklı portlardan çalıştırılan dört örneği bulunduğunu fark etmiş olmalısınız. Nginx sunucusundaki upstream ayarlarına göre 8080 üstünden gelen taleplerin bu process'lere dağıtılıyor olması lazım. Soru şu; Gerçekten dağıldıklarını nasıl ispatlarsınız? :) PM2 bizim için 4 farklı process açıp servisin birer örneğini buraya atıyor ama gerçekten taleplerin ayrık işlemci süreçlerine gittiğini nasıl anlarız?</p>
<p>Bunun haricinde senaryonun daha da anlamlı hale getirilmesini sağlayabilirsiniz. Söz gelimi özlü sözlerin yine docker imajı üstünden çalışan bir MongoDb deposundan alınması sağlanabilir. Arka plan servisimizde bir Docker Container olarak kullanılabilir. Hatta senaryoyu biraz daha ilerletip arka plan servisi, veritabanı reposu ve nginx'in docker compose ile hazırlanıp tek seferde ayağa kaldırılıp kullanılması da söz konusu olabilir. Ben bu düşünce tohumlarını ortaya bırakmış oldum. Kalanı sizde :) Böylece geldik bir <a href="https://github.com/buraksenyurt/skynet" target="_blank">SkyNet</a> derlemesinin daha sonuna. Kodların tamamına <a href="https://github.com/buraksenyurt/skynet/tree/master/No%2016%20-%20LB%20With%20Nginx" target="_blank">github reposu</a>ndan ulaşabilirsiniz. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2020-06-17T09:20:00+00:00nginxload balancingproxy servernodejsjavascriptpm2dockerroundRubinbsenyurtDünyanın en popüler ve hızlı proxy serverlarıdan birisi sanıyorum ki Nginx. Daha önce birçok kez onun üzerinde .Net Core tabanlı servisleri konuşlandırmıştım. Ancak bu defa Nginx'in talepleri dengeli bir şekilde dağıtacak şekilde(Load Balancer olarak) nasıl kullanacağımızı öğrenmeye çalışacağız. Senaryo gereği NodeJS ile yazılmış basit bir dummy servisin birkaç kopyasını çalıştıracağız. Aynı makinede farklı port adresleri üzerinden hizmet verecek bu servislere gelen taleplerin dağıtımını, Load Balancer görevini üstlenecek olan Nginx sunucusu üstlenecek. Ben bu kurguyu her zaman yaptığım gibi Heimdall(Ubuntu-20.04) üzerinde icra ediyorum. Ancak teorik olarak farklı platformlarda da benzer şekilde ilerleyebilirsiniz. Sisteminizde docker ve NodeJs'in yüklü olması şu an için yeterli. Nginx sunucusunu makineye kurmaktansa Docker imajı ile çalışmak çok daha mantıklı olacaktır. Hatta bu kurgu özelinde kendi docker imajımızı kullanıp Nginx konfigurasyonlarını bu Container içerisinde yapacağız. Öylese işe Dummy servisimizi yazarak başlayabiliriz.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=b9c964fc-780e-4da4-a3a6-0d2f44f5ea512https://buraksenyurt.com/trackback.axd?id=b9c964fc-780e-4da4-a3a6-0d2f44f5ea51https://buraksenyurt.com/post/nginx-kurgulu-hafif-bir-load-balancer-senaryosu#commenthttps://buraksenyurt.com/syndication.axd?post=b9c964fc-780e-4da4-a3a6-0d2f44f5ea51https://buraksenyurt.com/post/sequelize-kullanilan-bir-nodejs-rest-servisi-gelistirmekSequelize Kullanılan Bir NodeJs Rest Servisi Geliştirmek2020-05-27T20:23:00+00:00bsenyurt<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/12/hearthstone_2.jpg" alt="" align="right" />Bilgisayar ile ilk tanıştığım günden beri oyun oynamayı seven birisiyim. Tabii ilerleyen yıllarda buna vakit ayırmak benim için çok zorlaştı. Bu nedenle hep kendi devrimin efsane sayabileceğim oyunlarında takılı kaldım. Söz gelimi paraya kıyarak aldığım oyun bilgisayarıma<em>(Hani şu acayip ekran kartları olan, bir sürü fan barındıran, renkli ışıklarıyla gece disko topuna dönüşen masaüstü canavarından bahsediyorum)</em> taaa ikibinlerin başında ve öncesinde oynadığım Red Alert II ve Command & Conquer Generals oyunlarını yükleyip vakit geçirdim. Hani en en en yeni oynadığım oyun sanıyorum ki Hearthstone ve onda da herkes beni ezip duruyor diyebilirim :D Bende onu nerede kullanabilirim diye düşünürken bari kart ve kahramanlarını bir NodeJs servisine malzeme yapayım dedim.</p>
<p>Lakin epey zamandır Nodejs ile kod yazmamıştım. İşte bu öğretideki amacım Postgresql veritabanını kullanan bir REST servisini NodeJs ile geliştirmek. Kod tarafındaki Entity nesneleri ile Postgresql arasındaki ORM<em>(Object Relational Mapping)</em> katmanında Sequelize paketini kullanmayı öğrenmeye çalışıyorum.</p>
<p>Postgresql tarafı için sistemi kirletmemek adına Docker imajından yararlanabiliriz. Önce onun ayağa kaldırarak işe başlayalım derim. Aşağıdaki terminal komutlarında hem Postgresql docker container ayağa kalkıyor hem de gamedb isimli veritabanı oluşturuluyor.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">sudo docker run --name London -e POSTGRES_PASSWORD=P@ssw0rd -p 5433:5432 -d postgres
docker exec -it London bash
psql -U postgres
Create Database gamedb;</pre>
<h2>Şablonun Oluşturulması</h2>
<p>Örneği Heimdall<em>(Ubuntu 20.04)</em> üzerinde geliştiriyorum. Senaryomuzda oyun kartı ve kahramanlarına ait bilgileri ekleyip listelememize izin veren bir servis geliştirmeye çalışacağız. İlk olarak aşağıdaki terminal komutlarını kullanıp nodejs ortamını hazırlayalım ve gerekli modülleri yükletelim.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">mkdir hartstone
cd hartstone
npm init
touch index.js
npm install express body-parser sequelize sequelize-cli pg pg-hstore</pre>
<p>Express modülü REST servis alt yapısını yazmak, body-parser HTTP taleplerini kolayca parse etmek, pg postgresql iletişimini kurmak, pg-hstore JSON verilerini hstore formatında serileştirebilmek<em>(hstore Postgresql'e özgü olan key-value türünden bir kolon tipidir)</em> için kullanılıyor. Dahil edilen sequelize aracı ise standart bir proje şablonu oluşturmak için ele alınmakta. Diğer yandan migration işlemlerinde de bu aracı kullanabiliyoruz. İşimizi kolaylaştıracak şablon için aşağıdaki init komutunu kullanmamız yeterli.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">node_modules/.bin/sequelize init</pre>
<p>Bu işlemle üç klasör oluşacaktır. Veritabanı ayarları config klasöründeki config.json dosyasında tanımlanır. Migration işlemlerinin bulunduğu kod dosyaları içinse migrations klasörü kullanılır. Entity türleri ise models klasöründe tutulmaktadır. Biz tabii ki kendi geliştirmelerimizi yapacağız.</p>
<h2>Peki Biz Bu Şablonda Neler Yapacağız?</h2>
<p>İlk olarak config/config.json içeriğini postgresql kullanılacak şekilde ortam bazlı olarak<em>(dev,test,prod için ayrı ayrı)</em> düzenleyelim.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">{
"development": {
"username": "postgres",
"password": "P@ssw0rd",
"database": "gamedb",
"host": "localhost",
"port": 5433,
"dialect": "postgres"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql",
"operatorsAliases": false
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql",
"operatorsAliases": false
}
}</pre>
<p>Oyuna ait kahraman ve kart bilgilerini barındıracağımız tipleri models klasörü içerisinde inşa edebiliriz. Burada sequelize nesnesini nasıl kullandığımıza dikkat edin. Aslında Postgresql tarafındaki veri modeli ve ilişkileri tanımlıyoruz.</p>
<p>card.js</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">module.exports = (sequelize, DataTypes) => {
let Card = sequelize.define('Card', {
name: DataTypes.STRING,
description: DataTypes.STRING,
attack: DataTypes.INTEGER,
health: DataTypes.INTEGER,
spell: DataTypes.INTEGER
});
Card.associate = function (models) {
Card.belongsTo(models.Hero, {
onDelete: "CASCADE",
foreignKey: 'heroId'
});
};
return Card;
}</pre>
<p>hero.js</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">module.exports = (sequelize, DataTypes) => {
let Hero = sequelize.define('Hero', {
name: DataTypes.STRING,
info: DataTypes.STRING
});
Hero.associate = function (models) {
Hero.hasMany(models.Card, {
foreignKey: 'id',
as: 'cards'
});
};
return Hero;
}</pre>
<p>Migrations klasöründe ahero-migration ve card-migration isimli javascript dosyalarını oluşturarak devam edelim. Burası tipik olarak migration işlemleri sırasında Up ve Down operasyonlarında çalışacak kodları içeriyor. Her iki model için ayrı up ve down operasyonları söz konusu olabilir. Bu nedenle ayrı dosyalarda konuşlandırılıyorlar.</p>
<p>ahero-migration.js</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">module.exports = {
up: (queryInterface, Sequelize) =>
queryInterface.createTable('Heros', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
name: {
type: Sequelize.STRING,
allowNull: false,
},
info: {
type: Sequelize.STRING,
allowNull: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
}),
down: (queryInterface) =>
queryInterface.dropTable('Heros'),
};</pre>
<p>card-migration.js</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">module.exports = {
up: (queryInterface, Sequelize) =>
queryInterface.createTable('Cards', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
name: {
type: Sequelize.STRING,
allowNull: false,
},
description: {
type: Sequelize.STRING,
allowNull: false,
},
attack: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 1,
},
health: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 3,
},
spell: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 1,
},
heroId: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'Heros',
key: 'id',
as: 'heroId'
},
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
}),
down: (queryInterface) =>
queryInterface.dropTable('Cards'),
};</pre>
<p>Şimdi Controller isimli bir klasör oluşturup içerisine hero, card ve index dosyalarını ekleyelim. Burada ana klasördeki main.js içerisinde express vasıtasıyla yakalanan yönlendirmelerin karşılığı olan fonksiyonlara yer vermekteyiz. Her modelimiz için ayrı bir controller söz konusu. </p>
<p>hero.js</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">const Hero = require('../models').Hero;
module.exports = {
async getAll(req, res) {
try {
const heros = await Hero.findAll({});
res.status(201).send(heros);
}
catch (e) {
console.log(e);
res.status(500).send(e);
}
},
async create(req, res) {
try {
const hero = await Hero.create({
name: req.body.name,
info: req.body.info
});
res.status(201).send(hero);
}
catch (e) {
console.log(e);
res.status(400).send(e);
}
}
// Update ve delete işlevleri eklenmeli
}</pre>
<p>card.js</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">const Hero = require('../models').Hero;
const Card = require('../models').Card;
module.exports = {
async getAllByHero(req, res) {
try {
const hero = await Hero.findOne({
where: {
id: req.params.heroId
}
});
console.log(hero.name);
if (hero) {
const cards = await Card.findAll({
where: {
heroId: req.params.heroId
}
})
res.status(201).send(cards);
}
else {
res.send(404).send("Hero and it's cards not found")
}
}
catch (e) {
console.log(e);
res.status(500).send(e);
}
},
async create(req, res) {
try {
const card = await Card.create({
name: req.body.name,
description: req.body.description,
attack: req.body.attack,
health: req.body.health,
spell: req.body.spell,
heroId: req.body.heroId
});
res.status(201).send(card);
}
catch (e) {
console.log(e);
res.status(400).send(e);
}
}
// Update ve delete işlevleri eklenmeli
}</pre>
<p>index.js</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">const hero = require('./hero');
const card = require('./card');
module.exports = {
hero,
card
}</pre>
<p>Şimdi minik bir kahve arası verebiliriz. Kahvemizi içip geldikten sonra ise routes isimli klasörü oluşturup içerisindeki index.js dosyasını aşağıdaki gibi kodlayabiliriz.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">const heroController = require('../controller').hero;
const cardController = require('../controller').card;
module.exports = (app) => {
app.get('/game/api', (req, res) => {
res.status(200).send({
data: "Hartstone Oyun API servisi sürüm 1.0"
})
})
app.get('/game/api/hero', heroController.getAll);
app.post('/game/api/hero', heroController.create);
app.get('/game/api/hero/:heroId/cards', cardController.getAllByHero);
app.post('/game/api/card', cardController.create);
}</pre>
<p>Ardından belki bir de çay molası verip dönüşte ana klasöre geçer ve index.js içeriğini aşağıdaki gibi değiştiririz. Main içerisine express paketi devreye giriyor. Express, yönlendirmeler için Routes klasöründeki index.js'i kullanmakta. O da doğru controller tiplerini...Dikkat edileceği üzere main içeriği oldukça sade ve anlaşılır. Kodun tamamını okurken Main'den aşağıya doğru inmeye çalışırsanız çok daha anlaşılır olur ve tüm taşlar yerine oturur.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
require('./routes')(app);
const PORT = 5555;
app.listen(PORT, () => {
console.log(`Hartstone Game API servisi ${PORT} üstünden hizmettedir ;)`);
})</pre>
<p>Bu biraz da uzun sürecek kodlamaların ardından db migration sürecini başlatılabilir ve tabloların oluşup oluşmadığı kontrol edebiliriz. Tek yapmamız gereken aşağıdaki terminal komutunu kullanmak.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">node_modules/.bin/sequelize db:migrate</pre>
<blockquote>
<p>Hero ve Card arasında one-to-many ilişki var. Yani bir kahramana ait birden fazla kart olabilir. Bu nedenle migration sırasında önce Hero planının çalıştırılması lazım ki postgresql tarafında iki tablo arasındaki bire çok ilişki başarılı şekilde kurgulanabilsin. Bu nedenle hero-migration.js dosyasının başında bir a harfi bulunuyor. Çünkü db:migrate komutu klasördeki javascript içeriklerini alfabetik sırada çalıştırıyor. En azından ben denerken böyle bir şey fark ettim. Fark edene kadar da epey bir debelendim :)</p>
</blockquote>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/12/Screenshot_1.png" alt="" /></p>
<h2>Çalışma Zamanı</h2>
<p>Sonuçları görmek için sabırsızlandığınızı tahmin edebiliyorum. Öyleyse ana klasördeki index.js dosyasını çalıştıralım ve sonrasında Postman ile 5555 portundan hizmet veren servise HTTP Get, Post talepleri gönderelim.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">node index.js</pre>
<p>Örnek bir kahramanın oluşturulması için aşağıdaki Response içeriğini kullanabiliriz.</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">HTTP Post
http://localhost:5555/game/api/hero
JSON
{
"name": "Paladin",
"info": "The. Paladin is one of the ten classes in Hearthstone, represented by Uther Lightbringer, Lightforged Uther, Lady Liadrin, Prince Arthas, and Sir Annoy-O."
}</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/12/Screenshot_2.png" alt="" /></p>
<p>Tüm kahramanların listesinin çekilmesi içinde şu komut işe yarar.<em> (http://localhost:5555/game/api/heros daha iyi durabilir)</em></p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">HTTP Get
http://localhost:5555/game/api/hero</pre>
<p> <img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/12/Screenshot_3.png" alt="" /></p>
<p>Yeni bir Card oluşturmak içinse malum bir HTTP Post talebi göndermek icap eder. Body, bir JSON içeriği olmalıdır.</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">HTTP Post
http://localhost:5555/game/api/cad
JSON
{
"name": "Aviana",
"description": "Aviana is a Druid-only minion. This card was introduced with The Grand Tournament and can now only be obtained through crafting. Below the card images, you will find explanations to help you use the card optimally in every game mode of Hearthstone.",
"attack": 5,
"health": 5,
"spell": 10,
"heroId": 2
}</pre>
<p>Belli bir kahramana ait kartları çekmek içinse şöyle bir talep yeterli olur.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">HTTP Get
http://localhost:5555/game/api/hero/2/cards</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/12/Screenshot_4.png" alt="" /></p>
<p>Yazması biraz zahmetli ama sonuçları açısından anlaşılır bir öğreti olduğunu düşünüyorum. Umarım sizler için de faydalı olur. Kodların tamamına <a href="https://github.com/buraksenyurt/skynet/tree/master/No%2012%20-%20REST%20with%20Sequelize" target="_blank">skynet github reposu</a>ndan ulaşabilirsiniz. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2020-05-27T20:23:00+00:00nodejsdockerjavascriptrestsequelizeepxressbsenyurtEpey zamandır Nodejs ile kod yazmamıştım. Bu öğretideki amacım Postgresql veritabanını kullanan bir REST servisini NodeJs ile geliştirmekti. Kod tarafındaki Entity nesneleri ile Postgresql arasındaki ORM(Object Relational Mapping) katmanında Sequelize paketini kullanmayı öğrenmeye çalışıyordum. Postgresql tarafı içinse sistemi kirletmemek adına Docker imajından yararlandım. Önce onun ayağa kaldırarak işe başlayabiliriz.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=d8e82c6f-bb0d-4608-a4ba-c0023393c9fc0https://buraksenyurt.com/trackback.axd?id=d8e82c6f-bb0d-4608-a4ba-c0023393c9fchttps://buraksenyurt.com/post/sequelize-kullanilan-bir-nodejs-rest-servisi-gelistirmek#commenthttps://buraksenyurt.com/syndication.axd?post=d8e82c6f-bb0d-4608-a4ba-c0023393c9fchttps://buraksenyurt.com/post/mqtt-protokolunun-kullanildigi-basit-bir-publisher-subscriber-senaryosuMQTT Protokolünün Kullanıldığı Basit Bir Publisher/Subscriber Senaryosu2020-05-05T13:48:00+00:00bsenyurt<p>Yine bir yerlerde bir şeyleri araştırırken özellikle IoT ve M2M konseptinde yaygın olarak kullanılan MQTT<em>(Message Queuing Telemetry Transport)</em> isimli bir mesajlaşma protokolüne denk geldim. Düşük bant genişliklerinde, yüksek gecikme sürelerinin olduğu senaryolarda hafif bir mesajlaşma protokolü olarak karşımıza çıkıyor. En sık verilen senaryo bir IoT cihazının ısı sensöründen yayınlanan mesajın abone olan cep telefonu veya bilgisayarlar tarafından görülebilmesi. Elimde bir Raspberry PI vardı ama ısı sensörü yoktu. Dahası sensör alıp kurcalamaya üşendim diyelim. Hızlı bir antrenman için hayali bir senaryo düşündüm aşağıdaki karalamayı yaptım.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/17/Screenshot_1.jpg" alt="" /></p>
<p>Bir basketbol sahasının seyirci giriş çıkıp kapılarını düşünelim. Bilet okutulur, kapıdaki cihaz bunla alakalı bir konuda<em>(topic)</em> mesaj yayınlamak ister. Cihaz akıllıdır ve salonun WiFi ağına bağlıdır. Kapı giriş/çıkış taleplerini toplayan bir REST servisine HTTP Post ile bilgi gönderir. Servis bunu MQTT protokolü üzerinden bir Broker'a aktarır<em>(ki bizim senaryoda O açık kaynak Eclipse Moqsquitto'nun docker container örneğidir)</em> Broker MQTT mesajlarını dinleyip abonelere dağıtan bir aracı görevini üstlenmektedir. Abone olan cep telefonu, bilgisayar veya farklı IoT cihazları bu mesajları yakayabilir. Senaryom çok anlamlı değil belki ama ben ille de MQTT'yi kullanacağım ya :D O yüzden antrenman için ideal. Bu arada örneğimizi gerçekleştirmek için iki önemli malzeme gerekiyor; Mosquitto Docker Image ve NodeJs :) Dilerseniz vakit kaybetmeden Broker servisi ayağa kaldırıp sunucu tarafını yazarak işe başlayalım.</p>
<h2>Broker ve Sunucu Tarafı</h2>
<p>Broker için gerekli docker imajını yükleyip sonrasında proje klasör yapısını oluşturmamız lazım. İki NodeJs uygulamamız var. Birisi 4444 nolu porttan yayın yapan<em>(publisher)</em> ve gelen mesajları Mosquitto Broker'ına gönderen, diğeri 4445 nolu porttan ayağa kalkan ve abone olduğu konuları broker'dan dinleyen<em>(subscriber)</em>.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false"># Gerekli docker imajını yükleyip başlatıyoruz. MQTT Broker hazır ve nazır
sudo docker run -d --name jerry-maguire -p 1883:1883 eclipse-mosquitto
# Verileri toplayan REST Servisin oluşturulması
mkdir collector
cd collector
touch server.js
npm init --y
# REST Api için express paketini kullanabiliriz. JSON içerikleri içinde body-parser biçilmiş kaftan
# MQTT Broker ile iletişim kurabilmek içinse mqtt paketini yüklüyoruz
npm install --save express body-parser mqtt
cd ..</pre>
<p>Verileri toplayan server.js içeriğini aşağıdaki gibi kodlayabiliriz.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">var express = require('express');
var bodyParser = require('body-parser');
const mqtt = require('mqtt');
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Önce bir bağlanalım. Docker Container'ın olduğu adrese doğru.
var qt = mqtt.connect("http://localhost:1883");
// MQTT Client paketi olay bazlı çalışır. Kullanımı kolaydır.
// Broker ile bağlantı sağlandığında
qt.on('connect', () => {
console.log('Eclipse Mosquitto ile bağlantı sağlandı');
});
// Bir hata oluştuğunda
qt.on('error', (err) => {
console.log(`Bir hata oluştu sanırım. ${err}`);
qt.end();
});
// İstemci HTTP Post üstünden Broker'a iletilmek üzere bir istek aldığında
app.post("/input", function (req, res) {
// HTTP Body ile gelen JSON içeriği alıyoruz
var payload = req.body;
// Bu içerikteki gate niteliğinin değerini topic olarak
// identity niteliğini değerini de mesaj olarak kullanıyoruz
// ve Broker'a gönderiyoruz
qt.publish(payload.gate, payload.identity);
console.log(`Broker'a ${payload.gate} konusunda ${payload.identity} mesajı gönderildi`);
res.status(200).send('Mesaj Brokera gönderildi'); //İstemciye de HTTP 200 Ok gönderelim
});
app.listen(4444, function () {
console.log("Uygulama 4444 nolu porttan hizmette");
});</pre>
<h2>İstemci Tarafı</h2>
<p>İstemci tarafını da basit bir NodeJs uygulaması olarak tasarlayacağız.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">mkdir subscriber
cd subscriber
touch index.js
npm init --y
# istemci tarafında da mqtt paketini kullanmamız gerekiyor tabii
npm install --save mqtt express</pre>
<p>ve index.js içeriğimiz.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">const mqtt = require('mqtt');
var express = require('express');
var app = express();
// Hemen bağlantımızı sağlayalım
var qt = mqtt.connect("http://localhost:1883");
// westSide isimli topic için abonelik başlatıyoruz
qt.subscribe('west side', { qos: 0 });
// Broker ile bağlantı sağlandığında
qt.on('connect', () => {
console.log('Eclipse Mosquitto ile bağlantı sağlandı');
});
// Broker ile olan bağlantı kapatıldığında
qt.on('close', () => {
console.log('Mosquitto ile bağlantı kesildi');
});
// Broker'a belli bir konuda bir mesaj geldiğinde
qt.on('message', function (topic, message) {
console.log(`${topic} konusunda ${message} şeklinde bir mesaj geldi.`);
});
// Bir hata oluştuğunda
qt.on('error', (err) => {
console.log(`Bir hata oluştu sanırım. ${err}`);
qt.end();
});
app.listen(4445, function () {
console.log("Uygulama 4445 nolu porttan ayakta ve dinlemede");
});</pre>
<h2>Çalışma Zamanı</h2>
<p>Artık sonuçları görmek için kolları sıvayabiliriz. Her iki node uygulamasını npm run start terminal komutu ile kendi klasörlerinde çalıştırdıktan sonra http://localhost:4444/input adresine</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">{
"gate": "west side",
"identity": "1132"
}</pre>
<p>benzeri farklı türde ve sayıda talepler göndererek abone olan diğer istemcide mesajların çıkıp çıkmadığını görebiliriz. Aşağıdaki ekran görüntüsünde olduğu gibi ;)</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/17/Screenshot_3.png" alt="" /></p>
<p>Hepsi bu kadar sevgili dostlar :) Elbette bu örnek çalışma üstünden yapabileceğiniz birçok şey olduğunu da ifade etmek isterim. Örneğin subscriber uygulamasından birden fazla örnek çalıştırıp her bir dinleyiciye aynı mesajlar ulaşıyor mu kontrol edebilirsiniz. Diğer yandan subscriber olarak NodeJs'ten farklı dillerde program geliştirip broker ile çalışmayı deneyebilirsiniz. Ben üşendim ama siz üşenmezseniz eğer bir Raspberry PI'ye ısı sensörü bağlayıp sensörden okuduğunuz bilgiyi REST servisine veya doğrudan Mosquitto broker'ına göndermeyi düşünebilirsiniz. Böylece geldik bir SkyNet derlemesinin daha sonuna. Örneğe ait tüm kodlara <a href="https://github.com/buraksenyurt/skynet/tree/master/No%2017%20-%20MQTT%20on%20Simple%20Scenario" target="_blank">github reposu</a> üzerinden erişebilirsiniz. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2020-05-05T13:48:00+00:00mqttnodejsIoTm2mdockerbrokerpublisher subscriber modelbsenyurtYine bir yerlerde bir şeyleri araştırırken özellikle IoT ve M2M konseptinde yaygın olarak kullanılan MQTT(Message Queuing Telemetry Transport) isimli bir mesajlaşma protokolüne denk geldim. Düşük bant genişliklerinde, yüksek gecikme sürelerinin olduğu senaryolarda hafif bir mesajlaşma protokolü olarak karşımıza çıkıyor. En sık verilen senaryo bir IoT cihazının ısı sensöründen yayınlanan mesajın abone olan cep telefonu veya bilgisayarlar tarafından görülebilmesi. Elimde bir Raspberry PI vardı ama ısı sensörü yoktu. Dahası sensör alıp kurcalamaya üşendim diyelim. Hızlı bir antrenman için hayali bir senaryo düşündüm aşağıdaki karalamayı yaptım.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=609fe9ae-bc62-4083-91ce-0ad68b7496191https://buraksenyurt.com/trackback.axd?id=609fe9ae-bc62-4083-91ce-0ad68b749619https://buraksenyurt.com/post/mqtt-protokolunun-kullanildigi-basit-bir-publisher-subscriber-senaryosu#commenthttps://buraksenyurt.com/syndication.axd?post=609fe9ae-bc62-4083-91ce-0ad68b749619https://buraksenyurt.com/post/sikca-duydugum-deno-ya-hello-dedimSıkça Duyduğum Deno'ya Hello Dedim2020-01-04T21:00:00+00:00bsenyurt<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/07/denverdino.jpg" alt="" align="right" /><em>Denver, the last dinosaur</em><br /><em>He's my friend and a whole lot more</em><br /><em>Denver, the last dinosaur</em><br /><em>Shows me a world I never saw before...</em></p>
<p>Ha ha haaa! Deno'nun logosunu gördüğüm zaman her nedense aklıma, orta lise çağlarımda izlediğim çizgi dizi Denver'ın bu sözleri gelmişti. Ve hatta melodisi! O sözler eşliğinde 2020'nin herhangi bir noktasında sonradan eskiyeceğinden emin olduğum bir çalışmaya girişeyim istedim. Deno ile basit bir REST servisi ile merhaba demek. Notlar Github'daki skynet reposunda birikti. Buraya da derlenmiş bir özetini yazmak düştü.</p>
<p>Bir süre önce adını sıklıkla duyduğum ve NodeJs'in yerini alır mı almaz mı tartışmalarını<em>(ki öyle bir şey yok)</em> okuduğum Deno'yu basit bir örnekle incelemek istedim. Deno, Javascript haricinde dahili olarak Typescript desteği de sunan<em>(ki örnekte de onu kullandım)</em>, V8 üzerinde koşan ve Rust ile yazılmış bir çalışma zamanı olarak nitelendiriliyor. İşin içerisinde Rust olduğu için performans anlamında oldukça önemli beklentiler de beraberinde geliyor. Ben nasıl bir geliştirme tecrübesi yaşatacağını tatmak istemiştim. Klasik kurgu olarak REST tipinden bir servisin birkaç operasyonunu icra etsem yeterli olacaktı. Örnek verileri almak için <a href="http://www.icndb.com/" target="_blank"><strong>International Chuck Norris</strong></a> veritabanını kullandım :D Keza olaya biraz da olsa eğlence katmak her zaman iyidir. Hatta verileri SQLite veritabanında tutmak da fena olmaz. Onu kullanmak için bir kurulum yapmaya da pek gerek yok doğrusu. Sadece Deno Land'den çekeceğimizi söylesek yeterli. Öyleyse harakete geçme zamanı.</p>
<h2>Kurulum (Aslında Pek de Değil)</h2>
<p>İlk olarak deno çalışma zamanını sistemime<em>(Heimdall - Ubuntu 20.04)</em> yüklemek gerekiyordu. <a href="https://deno.land/#installation" target="_blank">Resmi adresinde</a> basit bir kurulum kılavuzu mevcut. Ben şirket bilgisayarına<em>(powershell üzerinden)</em> ve evdeki Linux sistemime ilgili kurulumu aşağıdaki komutlarla yaptım. Windows tarafında tek bir exe geldi ki zaten Deno'nun özelliği de buymuş. NodeJs gibi bir kurulum gerektirmiyor ve tek binary yeterli oluyor. İhtiyaç duyulan modüller <a href="https://deno.land/std" target="_blank">https://deno.land/std</a> ve benzeri paket adreslerinden import ile uygulama ortamına indiriliyor. Doğruyu söylemek gerekirse zahmetsiz bir kurulum oldu diyebilirim :)</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">$env:DENO_INSTALL = "C:\Program Files\deno"
iwr https://deno.land/x/install/install.ps1 -useb | iex
curl -fsSL https://deno.land/x/install/install.sh | sh</pre>
<p>Çalışmak istediğim uygulama iskeletini ise aşağıdaki şekilde kurguladım. Servis basit bir şekilde yeni Chuck Norris şakaları eklenmesine ve var olan şakaların listelenmesine imkan tanıyacak.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">mkdir chuck_jokes
cd chuck_jokes
touch main.ts
mkdir controller route
touch controller/jokescontroller.ts route/jokesrouter.ts</pre>
<h2>Kod İçerikleri</h2>
<p>Şakaları kod tarafında ifade etmek için model klasörü içerisinde yer alan joke sınıfını ve hatta örnek şakaları tutmak için jokesdb dosyasını kullanmıştım. Sonrasında ise SQLite'ı kullanmaya karar verdim. Bu nedenle controller sınıfını SQLite kullanacak hale dönüştürdüm<em>(Her nedense birkaç alan için ayrı bir model tipi kullanmayı terk etmişim. Sanırım çabuk bir Hello World olsun istemişim)</em></p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">import { open, save } from "https://deno.land/x/sqlite/mod.ts"; //SQLite'ı işin içerisine katalım
//SQLite veritabanını bir hazırlayalım
const db = await open("ICNDB.db"); // International Chuck Norris Database :)
// Şayet veritabanı dosyasında Jokes tablosu yoksa oluşturalım
await db.query("CREATE TABLE IF NOT EXISTS Jokes (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT,popularity TEXT)")
await save(db);
// Yeni bir şaka eklemek için kullanılır
export const insert = async ({ request, response }: { request: any; response: any }) => {
// HTTP Post Body'sinde bir şey yoksa 400 ile geri dönelim
if (!request.hasBody) {
response.status = 400;
response.body = { msg: "Şaka mı yapıyorsun?" };
return;
}
// body içeriğini content ve popularity'ye alalım. (bunların da olup olmadığını kontrol etmek lazım)
const {
value: { content, popularity }
} = await request.body();
//console.log(content + ' ' + popularity);
// Insert sorgusunu çalıştıralım
await db.query("INSERT INTO Jokes (content,popularity) VALUES (?,?)", [content, popularity]);
// Değişikliği kaydedip kapatalım
await save(db);
// HTTP 200 OK dönelim
response.status = 200;
response.body = { message: 'Chuck Norris buna sevindi :D' }
}
// Select All fonksiyonu
export const getAll = async ({ request, response }: { request: any; response: any }) => {
// Select sorgusunun sonucunu bir array'e alıyoruz
const allJokes = [];
for (const [id, content, popularity] of await db.query(
"SELECT id,content,popularity FROM jokes ORDER BY id DESC")) {
allJokes.push({ RuleNo: id, chuksMessage: content, Pop: popularity });
}
// HTTP 200 ile elde ettiğimiz içeriği döndürüyoruz
response.status = 200;
response.type = "application/json";
response.body = JSON.stringify(allJokes);
}
//TODO: getById, Delete, Update gibi operasyonlar eklenebilir</pre>
<p>Olmazsa olmaz bu CRUD<em>(CreateReadUpdateDelete)</em> operasyonlarının HTTP talepleri ile buluştuğu noktada devreye girecek bir sunucu da gerekiyor. Sunucu görevini ise jokesrouter sınıfı üstleniyor. Kodu oldukça basit. Kritik nokta gelen HTTP taleplerinin jokescontroller içerisindeki fonksiyonlarla eşleştirilmesi.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">import { Router } from "https://deno.land/x/oak/mod.ts"; //HTTP Server görevini üstlenecek typescript modülü
// deno.land/x adresinde 3rd Party modüller yer alır
import { getAll, insert } from '../controller/jokescontroller.ts';
const router = new Router();
// Root web adresine gelen ki(http://localhost:5555 oluyor) talepler için yönlendirme
router
.get("/", getAll)
.post("/", insert);
//TODO: getById, Delete, Update gibi operasyonlar eklenebilir
export default router;</pre>
<p>Elbette uygulamanın ana çalıştırıcısının da kodlanması gerekir. İşte çalıştırıcı görevini üstlenen main.ts içeriği.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">import { Application } from "https://deno.land/x/oak/mod.ts"; //Modül sistemi NodeJS ile farklı. URL kullanılıyor.
import router from "./route/jokesrouter.ts"; // Deno Varsayılan olarak Typescript kullanıyor ve destekliyor
import errorHandler from "./errorHandler.js";
import { open, save } from "https://deno.land/x/sqlite/mod.ts";
const PORT = 5555;
const app = new Application();
app.use(errorHandler);
// Middleware'e router eklendi.
app.use(router.routes());
// HTTP Get, Post, Put, Delete, Head, Options, Patch metodlarının kullanımına izin veriyoruz
app.use(router.allowedMethods());
console.log(`Chuck Norris ${PORT} nolu portta hazır :[] `);
// Portu açıp dinlemeye başlıyoruz
app.listen({ port: PORT });</pre>
<p>Küçük bir dipnot; hata yönetimi için aşağıdaki errorHandler sınıfı kullanılmakta.</p>
<pre class="brush:js;auto-links:false;toolbar:false" contenteditable="false">export default async ({ response }, nextFn) => {
try {
await nextFn();
} catch (err) {
response.status = 500;
response.body = { msg: err.message };
}
};</pre>
<p>Kod tarafındaki bu hazırlıkları tamamladıktan sonra birkaç deneme yaptım.</p>
<h2>Çalışma Zamanı</h2>
<p>Uygulamayı çalıştırırken internetten indirilecek bazı modüller olması sebebiyle --allow-net ile erişime izin verilmesi gerekiyor. Ayrıca SQLite veritabanı için diske yazma ve diskten okuma iznini de bu uygulamaya vermek lazım. Sanırım NodeJs'in yaratıcısının dili bir takım zafiyetlerden dolayı epeyce yanmış. O nedenle bu insiyatif geliştiricinin sorumluluğunda.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">deno run --allow-net --allow-write --allow-read main.ts</pre>
<p>İşte Heimdall<em>(Ubuntu 20.04)</em> çalışma zamanına ait birkaç görüntü<em>(Eğer aynı örneği kendi ortamınızda inşa edip benzer görüntüleri alamıyorsanız sürüm farklılıklarına takılmış veya kod tarafında bir hata yapmış olabilirsiniz)</em></p>
<p>Terminalden gerekli izinleri verip uygulamayı çalıştırdıktan sonra curl ile çektiğimiz örnek veriler...</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/07/screenshot_4.png" alt="" /></p>
<p>Çalışma sırasında sık sık verilerin db'ye yazılıp yazılmadığını da Visual Studio Code üstündeki SQLite eklentisinden kontrol ettim. Başta söylemeyi unuttum ama Ubuntu ortamımda her zaman olduğu gibi geliştirme aracı olarak Visual Studio Code kullanmaktayım.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/07/screenshot_1.png" alt="" /></p>
<p>Tabii veri eklemek için en kolay yollardan birisi Postman. Aşağıda örnek bir POST çağrısı ile bir Chuck Norris şakası ekleyişimiz resmedilmekte :)</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/07/screenshot_2.png" alt="" /></p>
<p>Http Get ile tüm listeyi de aşağıdaki gibi çekebiliyoruz.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2020/skynet/07/screenshot_3.png" alt="" /></p>
<p>Chuck Norris artık mutlu diyebilirim. Ancak yapılması gereken birçok şey var. Örneğin verinin devasallaşacağını düşünerek servisin sayfalamalı bir şekilde cevap döndürmesini sağlayabiliriz. Bunlara ek olarak güncelleme ve silme gibi operasyonları da işin içerisine katmak iyi bir pratik olabilir. Bunları Deno'nun güncel sürümleri üzerinden denemenizi öneririm. Kodun son haline <a href="https://github.com/buraksenyurt/skynet/tree/master/No%2007%20-%20What%20is%20Deno" target="_blank">github</a> SkyNet reposu üzerinden ulaşabilirsiniz. Tekrarda görüşünceye dek hepinize mutlu günler dilerim.</p>2020-01-04T21:00:00+00:00denonodejsjavascriptlinuxsqliterouterpostmanubuntubsenyurtŞu sıralar adını sıklıkla duyduğum ve NodeJs'in yerini alır mı almaz mı tartışmalarını(ki öyle bir şey yok) okuduğum Deno'yu basit bir örnekle inceledim. Javascript haricinde dahili olarak Typescript desteği de sunan, V8 üzerinde koşan ve Rust ile yazılmış bir çalışma zamanı olarak nitelendiriliyor Deno. Ben nasıl bir geliştirme tecrübesi yaşatacağını tatmak istemiştim. Klasik kurgu olarak REST tipinden bir servisin birkaç operasyonunu icra etsem yeterliydi. Örnek verileri almak için International Chuck Norris veritabanını kullandım :D Keza biraz eğlence katmak lazımdı olaya. Hatta verileri SQLite veritabanında tutmak da fena olmayacak.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=6a540c4f-d13d-40d9-a0f7-a06067a23ae30https://buraksenyurt.com/trackback.axd?id=6a540c4f-d13d-40d9-a0f7-a06067a23ae3https://buraksenyurt.com/post/sikca-duydugum-deno-ya-hello-dedim#commenthttps://buraksenyurt.com/syndication.axd?post=6a540c4f-d13d-40d9-a0f7-a06067a23ae3