RabbitMQ ile Kargo Takip Uygulaması

Bu makale ilk olarak 11/04/2021 tarihinde https://ardaorkin.com/blog adresinde yayınlanmıştır.

Genel Bakış

Yazılım geliştirme süreçlerinde geliştirilecek uygulamada kullanılacak olan teknolojileri belirlemenin yanı sıra uygulamanın mimarisini de belirlemek çok önemlidir. Bugünlerde en yaygın kullanılan mimari yapı micro servis yapısıdır. Micro servis yapılarının temel özelliklerinden birisi her bir servisin ayrı ayrı deploy edilebilir olmasıdır. Bu özellikleri yoluyla her servis uygulama içerisinde ayrı bir bağlantı protokolü kullanabilir. Yani bir uygulamada hem HTTP, hem AMQP hem de WS kullanılabilir.

Bugün burada hem HTTP hem AMQP hem WS kullanan küçük bir kargo takip uygulaması yapacağız. Bu uygulamda şu teknolojileri kullanacağız:

Express.js kütüphanesini HTTP isteklerini karşılamak için kullanacağız. HTTP istekleri geldiğinde RabbitMQ message broker’a bir mesaj yollayan publisher’lar kullanacağız. Bu publisher’ların RabbitMQ message broker’a yolladığı mesajları dinleyip MongoDB veri tabanınına kaydeden consumer’lar yazacağız. Son olarak veritabanındaki her bir değişkliğin, sayfa yenilenmeksizin ReactJS ile kodlayacağımız önyüzde görüntülenmesini sağlamak adına da Socket.IO kullanacağız.

İsteyen projenin bitmiş halini Github reposundan klonlayabilir.

Gereklikler

Geliştirme Ortamı Kurulumu

Sunucu

Öncelikle kargo-takip-sistemi diye bir klasör oluşturalım. Bu klasörün içinde de bir adet daha klasör oluşturalım. Bu klasöre de sunucu adını verelim. Sonra bir terminal açıp sunucu adlı klasörün olduğu dizine gidelim ve npm init -y komutunu çalıştıralım. Daha sonra ana klasörümüzü bir metin editöründe açalım ve şöyle göründüğünden emin olalım:

Preview

Tekrar terminalimize dönelim ve proje bağımlılıklarını yüklemek için sunucu klasörünün altında şu komutu çalıştıralım:

npm i express dotenv tortoise mongoose socket.io nodemon

Komutun sorunsuz çalıştığından ve paketlerin yüklendiğinden emin olmak için hem terminalde dönen mesajlara bakalım hem package.json adlı dosyanın dependecies bölümüne bakalım:

Ayrıca geliştirme ortamı bağımlılıklarımızı da yükleyelim:

npm i --save-dev @babel/core @babel/preset-env babel-loader

Bu komutu çalıştırdıktan sonra package.json dosyamızın içi resimdeki gibi görünmeli:

Daha sonra .babelrc diye bir dosya oluşturalım ve içerisine şu kodu yazalım:

{                                        
"presets": ["@babel/env"]
}

Şimdi de server.js adlı bir dosya oluşturalım ve dosyanın içerisine şu kodu yazalım:

//server.jsimport express from "express"              const app = express()              app.use("/", (req, res) => {                res.send("Kargo takip sistemi")              })              app.listen(8000, () => console.log(`Server listening on 8000`))

server.js adlı dosyayı nodemon ./server --exec babel -e jsterminalde çalıştıralım. Tarayıcımızda http://localhost:8000 adresini açalım ve karşımıza çıkan sayfada "Kargo takip sistemi" yazıyorsa Express.js kurulumumuz tamamlanmış demektir.

Publisher ve Consumer Oluşturma

Şimdiki adımımızda ilk publisher’ımızı ve ilk consumer’ımızı oluşturacağız. Publisher bir mesaj yayınlayıp, bunu exchange’e gönderecek. RabbitMQ message broker’da bulunan exchange’imiz bu mesajı kuyruğa yollayacak ve exchange’te kendisini ilgilendiren mesajları dinleyen consumer’ımız da bu mesaj alacak ve istediğimiz işlemi yapacak.

Şimdilik consumer üzerinden edindiğimiz mesajımızı konsola basacağız. Bunun için öncelikle sunucu dizini altında bir publishers bir de consumers adında iki adet dizin oluşturalım. publishers dizinimizin içerisinde shippingPublihser.js diye bir dosya oluşturalım. consumers dizinimizin içerisine de shippingConsumer.js diye bir dosya oluşturalım. shippingPublihser.js adlı dosyamızın içerisine şu kodu yazalım:

//shippingPublisher.js

import Tortoise from "tortoise"
import dotenv from "dotenv";
dotenv.config()
const tortoise = new Tortoise(process.env.AMQP_URL)
tortoise
.exchange("parcel-tracking", "topic", { durable: false })
.publish("parcel.shipping", { name: "test", status: "shipping" });

Şimdi de publisher’dan message broker’a giden mesajı alacak olan consumer’ı yazalım. Bunun için oluşturduğumuz shippingConsumer.js dosyasını açalım ve içine şu kodu yazalım:

//shippingConsumer.js

import Tortoise from "tortoise"
import dotenv from "dotenv";
dotenv.config()
const tortoise = new Tortoise(process.env.AMQP_URL) tortoise
.queue("", { durable: false })
.exchange("parcel-tracking", "topic", "*.shipping", { durable: false })
.prefetch(1)
.json()
.subscribe((msg, ack, nack) => {
console.log(msg)
ack();
});

Bu iki dosyayı oluşturduktan sonra publihser’ımızı ve consumer’ımızı hazırlamış olduk. Şimdi sırada message broker işlevi görecek RabbitMQ sunucumuzu ayaklandırmak var. Bunun için RabbitMQ’yu yerel makinamıza kurmayı da tercih edebiliriz ama bu yol hem uzun süreceğinden hem de işletim sistemine göre kurulum adımları değişeceğinden.http://cloudamqp.comüzerinden bir sunucu açarak devam edeceğiz. http://cloudamqp.com adresinde bir hesap olutşuruyoruz. Daha sonra Create New Instance yazan düğmeye basıp yeni bir message broker oluşturuyoruz. Mesaj broker’ı istediğiniz şekilde isimlendirebilirsiniz. Ücretsiz plan için Little Lemur’u seçmeniz gerekecektir. Sonra Select Region > Review > Create Instance düğmelerine basıp örnek message broker’ımızı oluşturuyoruz. Message broker’larımızın listelendiği sayfaya gidip, yeni oluşturduğumuz message broker’ın adına tıklıyoruz. Karşımıza çıkan Details bölümündeki AMQP URL değerini kopyalıyoruz. Projemizin olduğu dizine dönüyoruz ve sunucu adlı dizinimizde .env diye bir dosya oluşturuyoruz. Bu .env dosyasının içerisine şu satırları yazıyoruz:

AMQP_URL="kopyaladığımız_amqp_urlsi"

Yani AMQP_URL değişkeninin değeri az evvel kopyaladığımız AMQP URL’si olmalı.

Bu işlemden sonra basit bir amqp servisi kurmuş olduk. Şimdi bu servisi çalıştırmak için iki ayrı terminalde, sunucu klasörümüzün bulundupu konuma gidip sırasıyla şu komutları çalıştırıyoruz:

nodemon ./consumers/shippingConsumer --exec babel-node -e js          
nodemon ./publishers/shippingPublisher --exec babel-node -e js

Bu adımları uyguladıktan sonra shippingConsumer.js dosyasını çalıştırdığımız terminalde şu JSON biçimindeki çıktıyı göreceğiz:

{ name: 'test', status: 'shipping' }

Şu an elimizde çalışan bir servis var. Bu servis haberleşme protokolü olarak AMQP kullanıyor. Serviste iki uç nokta var, biri publihser, diğeri consumer. Publisher mesajı mesaj broker’a yolluyor, bu işlemden sonra publisher’ın işi bitiyor ve herhangi bir geri dönüş beklemiyor. Consumer da mesaj borker’daki mesajı alıp konsola basıyor. Consumer için tek önemli olan mesaj başlığının ne içerdiği. Buradaki örnek senaryoda consumer “shipping” ifadesi ile biten başlıklara sahip mesajlarla ilgileniyor. Mesaj başlığı bilgisi dışında consumer’ın ihtiyaç duyduğu hiçbir şey bulunmuyor.

Sıradaki adımda HTTP üzerinden gelen isteklerle mesaj yayınlayacağız.

HTTP İsteklerini Değerlendirme

Bu adım için öncelikle ikişer adet daha publisher ve consumer oluşturalım.

Yine sunucu adlı dizinimize gidelim. Burada publishers ve consumers adlı dizinlerimizin altında şu dosyaları oluşturalım:

Bu işlem sonucuda dosya-dizin yapımız şöyle görünmeli:

Bütün publihser dosyalarımızı Promise döndürecek şekilde yazıyoruz:

//shippingPublisher.js

import Tortoise from "tortoise";
import dotenv from "dotenv";
dotenv.config();

const tortoise = new Tortoise(process.env.AMQP_SERVER);

const shippingPublisher = (name) =>
new Promise((resolve, reject) => {
tortoise
.exchange("parcel-tracking", "topic", { durable: false })
.publish("parcel.shipping", { name, status: "shipping" });
resolve({ name, status: "shipping" });
});

export default shippingPublisher;
//onroadPublisher.js

import Tortoise from "tortoise";
import dotenv from "dotenv";
dotenv.config();

const tortoise = new Tortoise(process.env.AMQP_SERVER);

const onroadPublisher = (name) =>
new Promise((resolve, reject) => {
tortoise
.exchange("parcel-tracking", "topic", { durable: false })
.publish("parcel.onroad", { name, status: "onroad" });
resolve({ name, status: "onroad" });
});

export default onroadPublisher;
//deliveredPublisher.js

import Tortoise from "tortoise";
import dotenv from "dotenv";
dotenv.config();

const tortoise = new Tortoise(process.env.AMQP_SERVER);

const deliveredPublisher = (name) =>
new Promise((resolve, reject) => {
tortoise
.exchange("parcel-tracking", "topic", { durable: false })
.publish("parcel.delivered", { name, status: "delivered" });
resolve({ name, status: "delivered" });
});

export default deliveredPublisher;

Daha sonra consumer dosyalarımıza geçiyoruz. Bütün consumer dosyalarımızı aşağıdaki gibi yazıyoruz:

//shippingConsumer.js

import Tortoise from "tortoise";
import dotenv from "dotenv";
dotenv.config();

const tortoise = new Tortoise(process.env.AMQP_SERVER);
tortoise
.queue("", { durable: false })
.exchange("parcel-tracking", "topic", "*.shipping", { durable: false })
.prefetch(1)
.json()
.subscribe((msg, ack, nack) => {
console.log(msg)
ack();
});
//onroadConsumer.js

import Tortoise from "tortoise";
import dotenv from "dotenv";
dotenv.config();

const tortoise = new Tortoise(process.env.AMQP_SERVER);
tortoise
.queue("", { durable: false })
.exchange("parcel-tracking", "topic", "*.onroad", { durable: false })
.prefetch(1)
.json()
.subscribe((msg, ack, nack) => {
console.log(msg)
ack();
});
//deliveredConsumer.js

import Tortoise from "tortoise";
import dotenv from "dotenv";
dotenv.config();

const tortoise = new Tortoise(process.env.AMQP_SERVER);
tortoise
.queue("", { durable: false })
.exchange("parcel-tracking", "topic", "*.delivered", { durable: false })
.prefetch(1)
.json()
.subscribe((msg, ack, nack) => {
console.log(msg)
ack();
});

Bu ayarlamalardan sonra HTTP istekleri geldikçe çalıştıracağımız publisher’larımızı ve bu publisher’lar message broker’a mesaj yolladıkça bu mesajları işleyecek olan consumer’ları oluşturmuş olduk. Şimdi de HTTP route’larımızı oluşturacağız. Daha okunaklı ve yönetilebilir bir uygulama yapısı oluşturmak adına sunucu dizini altında routes diye bir dizin oluşturalım ve bu dizinin içerisinde de index.js diye bir dosya oluşturalım. index.js dosyamızın içerisine şu kodu yazalım:

//index.js

import { Router } from "express";
import shippingPublishers from "../publishers/shippingPublisher";
import onroadPublisher from "../publishers/onroadPublisher";
import deliveredPublisher from "../publishers/deliveredPublisher";

const router = Router();

router.get("/", (req, res) => {
res.send("Kargo takip sistemine hoş geldiniz");
});

router.get("/shipping/:name", async (req, res, next) => {
const name = req.params.name;
await shippingPublishers(name).then((message) => res.json(message));
});

router.get("/onroad/:name", async (req, res, next) => {
const name = req.params.name;
await onroadPublisher(name).then((message) => res.json(message));
});

router.get("/delivered/:name", async (req, res, next) => {
const name = req.params.name;
await deliveredPublisher(name).then((message) => res.json(message));
});

export default router;

HTTP route’larımızı oluşturduktan sonra server.js dosyamıza gidelim ve dosyamızı şöyle değiştirelim:

//server.jsimport express from "express";
import router from "./routes";
const app = express();
const port = process.env.PORT || 8000;
app.use(router);app.listen(port, () => console.log(`Server listening on port ${port}`));

HTTP route’larımızı uygulamanın geneline bağladıktan sonra ayrı ayrı terminallerde, sunucu dizinimizin altına giderek şu komutları çalıştıralım:

nodemon ./server --exec babel-node -e js            
nodemon ./consumers/shippingConsumer --exec babel-node -e js
nodemon ./consumers/onroadConsumer --exec babel-node -e js
nodemon ./consumers/deliveredConsumer --exec babel-node -e js

Bütün consumer’larımız ve uygulamamız çalıştıktan sonra sırasıyla şu adresleri tarayıcımızda açalım ve her adresi açtığımızda consumer’larımızın çalıştığı terminallerdeki çıktılara bakalım; terminal çıktılarında, message broker’a giden mesajları JSON biçiminde göreceğiz:

Hem consumer’ların çalıştığı konsoldaki çıktıyı hem de tarayıcıdaki JSON formatındaki mesajı gördükten sonra HTTP üzerinden gelen isteklerle publisher’larımızın çalıştığını ve consumer’larımızın da bu publisher’ların message broker’a gönderdiği mesajları işlediğini görmüş oluruz. Şimdi de sırada consumer’ların MongoDB veritabanına, message broker’dan aldıkları mesajı kaydedeceğiz.

MongoDB Yapılandırması

MongoDB için öncelikle https://www.mongodb.com/cloud adresinde bir hesap oluşturalım. Sonrasında bir organizasyon, bu organizasyon içerisinde bir proje, projenin içerisinde bir cluster oluşturalım. Cluster oluşturduktan sonra, cluster’ın olduğu sayfada, sol panelde yer alan Database Access bağlantısına tıklayalım. Burada ADD NEW DATABASE USER düğmesine tıklayalım ve veritabanı kullanıcımızı oluşturalım. Daha sonra yine sol panelde yer alan Clusters bağlantısını tıklayalım. Açılan sayfada Connect düğmesine tıklayalım, Setup connection security adımında Allow anywhere seçeneğini seçelim ve sağ alttaki Choose a connection method düğmesine tıklayalım. Burada Connect your application seçeneğine tıklayalım ve Add your connection string into your application code başlığı altındaki bağlantı bilgisini kopayalayalım. Uygulamamızda, sunucu dizini altında yer alan .env dosyamızın içerisinde MONGODB_URL adlı bir değişken oluşturalım ve kopyaladığımız bağlantı bilgisini bu değişkene değer olarak verelim, <password> kısmını az evvel Database Access bölümünde oluşturduğumuz parola ile değiştirelim. myFirstDatabase kısmını da kargotakip olarak değiştirelim:

MONGODB_URL="mongodb+srv://username:12345@cluster0.mjh9d.mongodb.net/kargotakip?retryWrites=true&w=majority"

Bu işlemleri yaptıktan sonra server.js dosyamıza gelelim, ve routes dizinini import ettiğimiz satırdan hemen sonra şu satırları yazalım:

//server.jsimport mongoose from "mongoose"
import dotenv from "dotenv"
dotenv.config()
mongoose.connect(process.env.MONGODB_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
});

const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", () => console.log("Connected to database"));

Bu satırlar yoluyla bir MongoDB bağlantısı açmış olduk. Her şeyin doğru olduğundan emin olmak için server.js dosyamızı şu komutla çalıştıralım:

nodemon ./server --exec babel-node -e js

Eğer terminal konsolunda Connected to database mesajını görüyorsanız bütün adımları başarılı bir şekilde geçmişsiniz demektir. Bundan sonraki adımda da ilk MongoDB şemamızı ve modelimizi oluşturacağız. Bu adım için de öncelikle sunucu dizini altında model diye bir dizin oluşturalım. Bu dizin içerisinde de Tracking.js diye bir dosya oluşturalım ve dosyanın içerisine şu kodu yazalım:

//Tracking.js

import mongoose from "mongoose";

const trackingSchema = new mongoose.Schema({
name: String,
status: String,
});

const Track = mongoose.model("Track", trackingSchema);

export default Track;

Modelimizi olutşurduktan sonra consumer’larımızda bu modeli kullanacağız. Fakat shippingConsumer’da mongoose kütüphanesindeki save() metodunu kullanırken, diğer consumerlarda da updateOne() metodunu kullanacağız. Öncelikle shippinConsumer.js dosyamızı şöyle değiştirelim:

//shippingConsumer.js

import Tortoise from "tortoise";
import mongoose from "mongoose"
import Track from "../model/Tracking";
import dotenv from "dotenv"
dotenv.config()
mongoose.connect(process.env.MONGODB_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
});

const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", () => console.log("Connected to database"));

const tortoise = new Tortoise(process.env.AMQP_SERVER);
tortoise
.queue("", { durable: false })
.exchange("parcel-tracking", "topic", "*.shipping", { durable: false })
.prefetch(1)
.json()
.subscribe((msg, ack, nack) => {
const newParcel = new Track(msg);
newParcel.save((err, parcel) => {
if (err) throw err;
console.log("shipped parcel:", parcel);
return parcel;
});
ack();
});

Bu sayede shippingConsumer, MongoDB içerisinde yeni bir kayıt oluşturacak. Şimdi de bu kaydı güncellemek için onroadConsumer ve deliveredConsumer dosyalarımızı değiştirelim

//onroadConsumer.js

import Tortoise from "tortoise";
import mongoose from "mongoose"
import Track from "../model/Tracking";
import dotenv from "dotenv"
dotenv.config()
mongoose.connect(process.env.MONGODB_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
});

const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", () => console.log("Connected to database"));

const tortoise = new Tortoise(process.env.AMQP_SERVER);
tortoise
.queue("", { durable: false })
.exchange("parcel-tracking", "topic", "*.onroad", { durable: false })
.prefetch(1)
.json()
.subscribe(async (msg, ack, nack) => {
const onroadParcel = await Track.updateOne(
{ name: msg.name },
{ status: msg.status },
(err, parcel) => {
if (err) throw err;
else return parcel;
}
);
console.log("parcel is on road:", onroadParcel);
ack();
});
//deliveredConsumer.js

import Tortoise from "tortoise";
import mongoose from "mongoose"
import Track from "../model/Tracking";
import dotenv from "dotenv"
dotenv.config()
mongoose.connect(process.env.MONGODB_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
});

const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", () => console.log("Connected to database"));

const tortoise = new Tortoise(process.env.AMQP_SERVER);
tortoise
.queue("", { durable: false })
.exchange("parcel-tracking", "topic", "*.delivered", { durable: false })
.prefetch(1)
.json()
.subscribe(async (msg, ack, nack) => {
const deliveredParcel = await Track.updateOne(
{ name: msg.name },
{ status: msg.status },
(err, parcel) => {
if (err) throw err;
else return parcel;
}
);
console.log("parcel was delivered:", deliveredParcel);
ack();
});

Dosyalarımızın içerisini değiştirdikten sonra yine şu komutları sunucu dizinimizin altında çalıştırıyoruz:

nodemon ./server --exec babel-node -e js            
nodemon ./consumers/shippingConsumer --exec babel-node -e js
nodemon ./consumers/onroadConsumer --exec babel-node -e js
nodemon ./consumers/deliveredConsumer --exec babel-node -e js

Bütün consumer’larımızın ve server.js dosyamızın çalıştığı terminallerdeki çıktılarda Connected to database mesajını görüyorsanız bütün adımları başarılı bir şekilde uygulamışsınız demektir. Bundan sonra veritabanı sorgularının doğruluğunu test etmek için sırasıyla aşağıdaki bağlantıları tarayıcıda açalım ve her bağlantıyı açtığımızda MongoDB veritabanımızı kontrol edelim.

MongoDB Cloud’da Clusters sayfasındak Collections sekmesine tıklayalım. Karşımıza çıkan sayfada kargotakip veritabanı altındaki tracks adlı collection’a tıklayalım.

Eğer sorgularımız doğruysa, shippingConsumer mesajı işlediğinde MongoDB’ye bir kayıt eklenecek, diğer consumer’lar da mesajı işledeğinde MongoDB’deki kaydın status değeri değişecektir. Kaydın değiştiğini görüntülemek için Collections sayfasındaki Refresh butonuna tıklamamız gerekecektir.

Her şey düzgün çalışıyorsa consumer’larımız kendilerine gelen mesajı veritabanına başarılı bir şekilde işliyor demektir. Bundan sonraki adımda bu verileri önyüzde göstermek için Web Socket kullanacağız.

Web Socket İle Gerçek Zamanlı Verileri Gösterme

Web socket ile gerçek zamanlı verileri göstermek için Socket.IO kütüphanesini kullanacağız. Consumer’lar veritabanında her işlem yaptığında bu işlemle, sayfa yenileme gerek kalmaksızın önyüzde kulanıcıya görünecek. Bunun için öncelikleserver dizini altında socket diye bir dizin oluşturalım ve içerisine trackerSocket.js diye bir dosya ekleyelim. trackerSocket.js dosyasının içerisine şu satırları yazalım:

//trackerSocket.js

import socketIo from "socket.io";
import express from "express";
import http from "http";
import mongoose from "mongoose";
import Track from "../model/Tracking";
import dotenv from "dotenv";

dotenv.config();

const port = process.env.WS_PORT || 8001;

mongoose.connect(process.env.MONGODB_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
});

const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", () => console.log("Connected to database"));

const app = express();

const server = http.createServer(app);

const io = socketIo(server, {
cors: {
origin: "*",
methods: ["GET", "POST"],
},
});

let interval;

const findParcel = async (socket) => {
const parcel = await Track.find({}, (err, parcel) => {
if (err) throw err;
console.log(parcel);
return parcel;
});
socket.emit("parcel", parcel);
};

io.on("connection", (socket) => {
console.log("New client connected");
if (interval) {
clearInterval(interval);
}
interval = setInterval(() => findParcel(socket), 1000);
socket.on("disconnect", () => {
console.log("Client disconnected");
clearInterval(interval);
});
});

server.listen(port, () => console.log(`Listening on port ${port}`));

Dosyamızı yazdıktan sonra şu komutu çalıştırıyoruz:

nodemon ./socket/trackerSocket --exec babel-node -e js

Komutun çıktısında Listening on port 8001 ve Connected to database mesajlarını görüyorsak socketimiz çalıştı demektir.

Şimdide önyüz kurulumu için, bir terminal açalım, en başta oluşturduğumuz kargo-takip-sistemi adlı dizinimize gidelim ve şu komutu çalıştıralım

npx create-react-app client

Bu komut sonrasında hazır bir React uygulama yapısı, client adlı bir dizin altında kurulmuş olacak. Daha sonra terminalde client dizininin altında şu komutu çalıştırarak socket.io-client kütüphanesini yükleyelim:

yarn add socket.io-client

Kütüphane yükleme işlemi bittikten sonra client dizini altında bulunan src dizinine gidelim. Burada socket.js adlı bir dosya oluşturalım ve içerisine şu satırları yazalım:

//socket.js

import socketIOClient from "socket.io-client";
const ENDPOINT = "http://127.0.0.1:8001";

const socket = socketIOClient(ENDPOINT);

export default socket;

socket.js içerisini oluşturduktan sonra src dizini altındaki App.js dosyamızı açalım ve içerisini şöyle düzenleyelim:

//App.js

import React from "react";
import socket from "./socket";

function App() {
const [parcels, setParcel] = React.useState([{}]);
React.useEffect(() => {
socket.on("parcel", (data) => setParcel(data));
});
return (
<div>
{parcels.map((parcel) => (
<>
<div>ID: {parcel._id}</div>
<div>Name: {parcel.name}</div>
<div>Status: {parcel.status}</div>
<br></br>
</>
))}
</div>
);
}

export default App;

React uygulamayı çalıştırmak için terminalde client dizini altına gidin ve şu komutu çalıştırın:

yarn start

Karşınıza çıkan sayfada MongoDB’deki kaydın bilgisi bulunacaktır. Yeni kayıt eklemek ve bu kaydı güncelleyp, her güncellemenin gerçek zamanlı olarak React uygulamaya yansıdığından emin olmak için sırasıyla şu bağlantıları tarayıcınızda açın:

Eğer her bağlantıya tıkladığınızda React uygulamada veritabanındaki değişikliği görüntüleyebiliyorsanız, tebrikler, küçük bir kargo takip servisiniz oldu.

Sonuç

Microservis yapıları her geçen gün yeni uygulamalarda daha çok tercih edilen yapılar olmakta. Yazılım ekipleri var olan monotlith yapılarını microservis mimarilere göç ettirmek içi çalışmalar yapmakta. Bunun yanısıra AMQP de çok fazla tercih edilen bir haberleşme protokolü olarak ön çıkmakta ve HTTP ile yaşanan bir takım sorunlara çözüm sağlamakta. Son kullanıcı tarafında ise gerçek zamanlı uygulamalara olan ilgi ve talep de gittikçe artmakta ve burada web socket’ler yazılım geliştiriciye büyük kolaylıklar sağlamakta. Yeni taleplere ve yeni teknolojilere cevap vermek için kullanılan bu araçlarda ustalaşmak hem çok zevkli hem de geliştiriciye çok fazla katkı sağlıyor. Bu makale boyunca da ufak bir servis yazıp, sözkonusu yapıları ve teknolojileri özet halinde kullanıp genel bir kavrayış sağlamaya çalıştım. Umarım okuyana, takip edene, uygulayana bir katkısı olmuştur.

I am a self-taught JavaScript Developer. I like to tell, listen and read stories about JavaScript world

I am a self-taught JavaScript Developer. I like to tell, listen and read stories about JavaScript world