SignalR Chatleşme sırasında Yazışmaları RabbitMQ Kullanılarak Sql Server’a Kaydetme Bölüm1
Selamlar,
Bugün yoğun bir işlem topluluğu sırasında, mesela 10bin kişinin aynı anda chatleşmesi durumunda, bu işlere bağımlı olarak, anlık yapılmasına ihtiyaç duyulmayan yani daha sonradan da yapılabilecek işlemlerin, performans amaçlı bir quee’ya alınıp (RabbitMQ), sıra ile nasıl işleneceğini hep beraber incleyeceğiz. RabbitMQ ile alakalı makalemi buradan inceleyebilirsiniz. http://www.borakasmer.com/rabbitmq-nedir/ Yine clientların birbirleri ile real time yazışabilmeleri için SignalR kullanılmıştır. SignalR ile alakalı makaleme de buradan erişebilirsiniz. http://www.borakasmer.com/signalri-derinlemesine-inceleme/
Gelin isterseniz öncelikle Quee yapısı, neden bir projede kullanılmalıdır onu tartışalım: Esas amaç, performans ve istenen bazı işlerin asenkron olarak arka tarafta yapılmasıdır. Örneğin hata durumunda logların bir DB’ye yazılması, bilet alan kişilere bildirim SMS’i veya mail’in gönderilmesi ya da bu makaledeki örnekde olduğu gibi chatleşen kişilerin konuşmalarının history amaçlı yine bir DB’ye yazılmasıdır. Eğer bu işlemler anlık yapmaya çalışılır ise, sunucuların cpu ve ramlerinin tavan yapması işten bile değildir. Örneğin anlık maç bileti satışında, 50bin kişiye aynı anda Mail ve Sms atmayı, eğer özel bir alt yapınız yok ise sakın denemeyin :)
Bu problemin çözüm yollarından biri olarak Quee yani bir sıraya koyup, teker teker işleme almak faydalı olabilir. İşte biz de bu çözüm yolundan faydalanarak, Chat yapan kişilerin herbir yazışmasını RabbitMQ’ya atıp, microservis mantığı ile teker teker işleyeceğiz ve SqlDB’ye Dapper kullanarak da kaydedeceğiz.
Bölüm 1 Chat Uygulaması Yazma :
Öncelikle gelin Chat uygulaması için boş bir Mvc WebApplication yaratıp, aşağıdaki paketleri Nuget’den kuralım. Real time chat uygulaması için SignalR socket teknolojisi bu uygulamada kullanılacaktır. Ön yüz tarafında javascript kütüphanelerinden Angular kullanılacaktır. AngularJS ile olan makaleme buradan erişebilirsiniz. http://www.borakasmer.com/angularjs-nedir/ Son olarak Quee mekanizması olarak RabbitMQ kullanılacaktır. Quee atılacak ve çekilecek sınıf json formatında olacaktır. Bu neden ile serialize ve deserialize işlemleri için Newtonsoft kullanılacaktır. Görsel olarak bootstrap CSS kütüphanesi kullanılmıştır.
Gelin isterseniz önce basit bir login sayfası yazalım. Böylece chat’e girecek kişilerin ismini önce bir session’da saklayalım.
Bir aşağıda “Index” sayfasına girilmeden önce ilgili kişinin login olup olmadığını kontrol eden custom [LoginFilter] oluşturulmuştur. Ayrıca login olunmamış ise, gidilecek Login sayfasına ait “Login Action” aşağıdaki gibi tanımlanmıştır. Sayfaya ilk gelinildiğinde Login sayfası açılır ve ilgili Nick yazılıdığında yönlendirilecek, yani Post edildiğinde gidilecek “Login ActionPost” methodu aşağıdaki gibi yazılmıştır. Dikkat edilirse static Dictionary tipinde bir “ChatList” oluşturulmuştur. Amacı yeni user sayfaya login olduğunda, ait olduğu Nick’in bir dizide tutulup, aynı nickten başka birinde var ise hata dönülmesidir. İşte tam da bu nedenden dolayı Login Post Actionın’da ilgili Nick’e linq ile bakılıp yok ise Session’a ve ilgli ChatList dictionary’e eklenmiş eğer var ise hata geri dönülmüştür.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// GET: Home [LoginFilter] public ActionResult Index() { return View(); } static private Dictionary<string, string> ChatList = new Dictionary<string, string>(); public ActionResult LogIn() { return View(); } [HttpPost] public ActionResult LogIn(string name) { //Bu Nick daha önceden alındı mı? if (!ChatList.Any(cl => cl.Value.ToUpper() == name.ToUpper()) && name.Trim() != "") { ChatList.Add(DateTime.Now.ToString(), name); Session.Add("UserName", name); return View(); } else { return Content("Error"); } } |
LoginFilter: Aşağıdaki filter’da amaç, projede herhangi bir sayfaya gidilmek istendiğinde ilgili kişinin login olup olmadığını her seferinde yazmaktan ise (Dry) prensibinden yola çıkılarak filter ile tekbir yerden bakılmasının sağlanmasıdır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace SignalR_RabbitMQ.Models { [AttributeUsageAttribute(AttributeTargets.All, AllowMultiple = true)] public class LoginFilter : AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext filterContext) { if (HttpContext.Current.Session["UserName"] == null) { filterContext.Result = new RedirectToRouteResult("LogIn", new RouteValueDictionary("LogIn")); } } } } |
Login.cshtml : Aşağıda görüldüğü gibi bir login sayfası yapılmıştır. Kullanıcı adı girilecek “userName” adında bir input alan ve Ajax Post işleminin yapılması için “btnSubmit” adında bir Html Button konulmuştur. Aşağıda yazılan Jquery’de ilgili button tıklandığında, “Login” Post action’ına “name” parametresi ile bir gönderme işlemi yapmaktadır. Gönderilen isim static dictionary “ChatList” içerisinde yok ise session’a eklenmiş ve eğer geri dönüş result’ında string “Error” kelimesi yok ise “Index” ana sayfasına yönlendirme işlemi yapılmıştır. Eğer hata var ise “Bu Nicke’de Bir Kullanıcı Vardır.” hatası ekrana basılmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>ChatMe LogIn</title> <script src="~/scripts/jquery-3.1.1.min.js"></script> <script src="~/scripts/bootstrap.min.js"></script> <link href="~/Content/bootstrap.min.css" rel="stylesheet" /> <script> $(document).ready(function () { $("#btnSubmit").click(function () { $.post("/Home/Login", { name: $("#userName").val(), }, function (result) { if (result != "Error") { location.href = "/Home/Index"; } else { alert("Bu Nicke'de Bir Kullanıcı Vardır."); } }); }); $("#userName").focus(); }); function pushEnter(event) { if (event.which == 13) { $('#btnSubmit').click(); } } </script> </head> <body> <div class="container"> <div class="row"> <div class="col-md-offset-5 col-md-3"> <div class="form-login"> <h3>RabbitMq İle Chat</h3> <input type="text" id="userName" class="form-control input-sm chat-input" placeholder="Kullanıcı Adı Giriniz" onkeydown="pushEnter(event)" /> <br /> <div class="wrapper"> <span class="group-btn"> <a href="#" class="btn btn-primary btn-md" id="btnSubmit">Giriş Yapınız <i class="fa fa-sign-in"></i></a> </span> </div> </div> </div> </div> </div> </body> </html> |
Şimdi login olduğumuza göre gelin hep beraber chat sayfasını yazalım. Yazacağımız chat sayfası One to Many yani bir gurup yazışması olacaktır. Kısaca herkez ortaya yazabilecek ve yine bu yazılanlar herkez tarafından okunabilecektir. Sizden gelen isteklere göre one to one private chat ile alakalı farklı bir çalışma yapabiliriz. Ama bu makalenin esas amacı büyük yükler altında sonradan yapılabilecek işlerin, çalışan sisteme yük bindirmeden asenkron olarak arkada microservisler ile çalıştırılmasıdır. Ama bu demek olmuyor ki chat uygulamaları için genel geçer konseptlerden de bahsetmiyelim:)
Öncelikle aşağıdaki scriptler, Index.cshtml sayfasına Inculde edilir. Bu makalede modern javascript kütüphanelerinden angular1 kullanılmıştır.
1 2 3 4 5 6 7 8 |
<script src="~/scripts/angular.min.js"></script> <script src="~/Scripts/angular-sanitize.min.js"></script> <script src="~/scripts/jquery-3.1.1.min.js"></script> <script src="~/scripts/bootstrap.min.js"></script> <link href="~/Content/bootstrap.min.css" rel="stylesheet" /> <script src="~/Scripts/crpt.js"></script> <script src="~/scripts/jquery.signalR-2.2.2.min.js"></script> <script src="~/signalr/hubs"></script> |
“angular-sanitize” script kütüphanesinin amacı: Chat konuşmaları içinde geçen html tagların, yorumlanmasını sağlanmasıdır. İleride bu konuyu detaylıca inceleyeceğiz.
Aşağıda “app” module’ü ve diğer include moduller tanımlanmıştır.
- $sce: Html tagların yorumlanmasını sağlar.
- $timeout: disconnect durumunda 2sn bekleyip(setTimeout) tekrardan bağlantıyı sağlamak için kullanılır.
- “hubProxy = $.connection.chat;” : signalR Hub chat sınıfına connect olunur.
- “hdnUserName” hidden alanına sessiondaki UserName türkçe karakterler convert edilerek atanır=> “ConvertTurkish(‘@Session[“UserName”]’)”.
- hubProxy.client.connected: server side tarafından çağrılacak client side tarafdaki function’dır. Client Connect olunca kişiye özel connectionID’sini hdnConnectionID’ye atar. İlerde bire bir görüşme yazılmak istenir ise, kişiye ait connectionID gerekmektedir:)
- Son olarak “disconnected” durumunda: 2sn beklenip tekrardan bağlanma işlemi yapılmaktadır.
Index.cshtml/Script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<script> var hubProxy; var app = angular.module("app", ['ngSanitize']); app.controller('Controller', ['$scope', '$sce', '$timeout', function ($scope, $sce, $http, $timeout) { hubProxy = $.connection.chat; $.connection.hub.logging = true; $("#hdnUserName").val(ConvertTurkish('@Session["UserName"]')); $scope.UserName = $("#hdnUserName").val(); $.connection.hub.start().done(function () { console.log("hub.start.done"); }).fail(function (error) { console.log(error); }); hubProxy.client.connected = function (_connectionID) { $scope.ConnectionID = _connectionID; $("#hdnConnectionID").val(_connectionID); console.log(_connectionID); } $.connection.hub.disconnected(function () { setTimeout(function () { $.connection.hub.start(); }, 2000); // 2sn sonra tekrar connection kurulur. }); </script> |
ConvertTurkish: Yazılan Nick’in client taraftan sunucu tarafa alınırken çıkablecek Türkçe karakterler sorunu bu şekilde çözülmektedir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
function ConvertTurkish(input) { input = input .replace(/Ü/g, "Ü").replace(/ü/g, "ü") .replace(/Ö/g, "Ö").replace(/ö/g, "ö") .replace(/Ç/g, "Ç").replace(/ç/g, "ç"); input = input .replace(/ı/g, "ı").replace(/İ/g, "İ") .replace(/Ü/g, "Ü").replace(/ü/g, "ü") .replace(/Ö/g, "Ö").replace(/ö/g, "ö") .replace(/Ğ/g, "Ğ").replace(/ğ/g, "ğ") .replace(/Ş/g, "Ş").replace(/ş/g, "ş") .replace(/Ç/g, "Ç").replace(/ç/g, "ç"); input = input .replace(/\u0131/g, "ı").replace(/\u0130/g, "İ") .replace(/\u00FC/g, "ü").replace(/\u00DC/g, "Ü") .replace(/\u00F6/g, "ö").replace(/\u00D6/g, "Ö") .replace(/\u011F/g, "ğ").replace(/\u011E/g, "Ğ") .replace(/\u015F/g, "ş").replace(/\u015E/g, "Ş") .replace(/\u00E7/g, "ç").replace(/\u00C7/g, "Ç"); return input; } |
Aşağıdaki “SendMessageAll()” function’ı, gene server side tarafından, yani Hub class’dan tetiklenen client side taraflı bir functiondır. Amacı server side taraftan gönderilen text mesajı global olarak ortada duran “MessageHistory” div’ine append etmektir.
- $sce.trustAsHtml: Amacı gelen string message içinde html elementler var ise bunu yorumlamaktır. Örneğin: “<font color=’red’>” html tag’ı yorumlanıp “UserName” kırmızı olarak ekrana basılmaktadır.
- Crypto: Özel olarak yazılmış bir functiondır. Amacı gelen cryptolu textlerin okunur hale getirilmesidir.
- İlgili div’in html’ine bind edilen “$scope.MessageHistory” değişkeni “$scope.$apply()” methodu ile model’de olan son değişikliği, ön yüze yansıtmaktadır.
1 2 3 4 5 |
hubProxy.client.SendMessageAll = function (Message, UserName) { if ($scope.MessageHistory == null || $scope.MessageHistory == undefined || $scope.MessageHistory == "") { $scope.MessageHistory = " "; } $scope.MessageHistory = $sce.trustAsHtml("<font color='red'><b>" + UserName + ":</b></font>") + Crypto.decode(Message) + "<br>" + $scope.MessageHistory; $scope.$apply(); } |
crpt.js: Crypto değimiz Base64’e çeviren ve tekrar text’e dönüştüren script kodlarıdır. Amaç, en azından yazılan mesajların çıplak gözle anlaşılmamasının sağlanmasıdır. Yoksa gerçekte bir şifreleme sözkonusu değildir. Bu da detaylı bir konu olduğu için ve bu makalenin konusu olmadığı için böyle basit bir yola gidilmiştir.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
var Crypto = { _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", encode: function (e) { var t = ""; var n, r, i, s, o, u, a; var f = 0; e = Crypto._utf8_encode(e); while (f < e.length) { n = e.charCodeAt(f++); r = e.charCodeAt(f++); i = e.charCodeAt(f++); s = n >> 2; o = (n & 3) << 4 | r >> 4; u = (r & 15) << 2 | i >> 6; a = i & 63; if (isNaN(r)) { u = a = 64 } else if (isNaN(i)) { a = 64 } t = t + this._keyStr.charAt(s) + this._keyStr.charAt(o) + this._keyStr.charAt(u) + this._keyStr.charAt(a) } return t }, decode: function (e) { var t = ""; var n, r, i; var s, o, u, a; var f = 0; e = e.replace(/[^A-Za-z0-9+/=]/g, ""); while (f < e.length) { s = this._keyStr.indexOf(e.charAt(f++)); o = this._keyStr.indexOf(e.charAt(f++)); u = this._keyStr.indexOf(e.charAt(f++)); a = this._keyStr.indexOf(e.charAt(f++)); n = s << 2 | o >> 4; r = (o & 15) << 4 | u >> 2; i = (u & 3) << 6 | a; t = t + String.fromCharCode(n); if (u != 64) { t = t + String.fromCharCode(r) } if (a != 64) { t = t + String.fromCharCode(i) } } t = Crypto._utf8_decode(t); return t }, _utf8_encode: function (e) { e = e.replace(/rn/g, "n"); var t = ""; for (var n = 0; n < e.length; n++) { var r = e.charCodeAt(n); if (r < 128) { t += String.fromCharCode(r) } else if (r > 127 && r < 2048) { t += String.fromCharCode(r >> 6 | 192); t += String.fromCharCode(r & 63 | 128) } else { t += String.fromCharCode(r >> 12 | 224); t += String.fromCharCode(r >> 6 & 63 | 128); t += String.fromCharCode(r & 63 | 128) } } return t }, _utf8_decode: function (e) { var t = ""; var n = 0; var r = c1 = c2 = 0; while (n < e.length) { r = e.charCodeAt(n); if (r < 128) { t += String.fromCharCode(r); n++ } else if (r > 191 && r < 224) { c2 = e.charCodeAt(n + 1); t += String.fromCharCode((r & 31) << 6 | c2 & 63); n += 2 } else { c2 = e.charCodeAt(n + 1); c3 = e.charCodeAt(n + 2); t += String.fromCharCode((r & 15) << 12 | (c2 & 63) << 6 | c3 & 63); n += 3 } } return t } }; |
Son olarak mesaj yazılıp “Gönder” button’una basıldığında aşağıdaki “SendMessage()” function’ı call edilir. İlgili message “Crypt.encode()” ile gizlendikten sonra servser side tarafındaki “sendMessageAll()” methoduna “UserName” parametresi ile birlikte gönderilir.
1 2 3 4 5 6 7 8 |
$scope.SendMessage = function () { var message = Crypto.encode($scope.Message); $scope.Message = ""; hubProxy.server.sendMessageToAll(message, $scope.UserName) }; $scope.pushEnter = function (event) { if (event.which == 13) { $scope.SendMessage(); } } |
Index.cshtml sayfasnın Html codeları aşağıdaki gibidir:
Yukarıda görüldüğü gibi bir karşılama div’i, hemen altına mesajın yazılacağı bir “textarea”, onun da altına mesajın gönderileceği bir button ve son olarak ortak olarak herkesin mesajının gösterildiği bir div sayfa üzerinde bulunmaktadır. Ayrıca “connectionID” ve “UserName”‘in tutulduğu hidden alanlar sayfanın en altına konulmuştur.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<body> <div class="container" ng-controller="Controller" id="container"> <div style="background-color:lightblue !important" class="jumbotron"> <h1>RabbitMq İle Chat<br />@String.Format("".PadRight(8, '_')) Wellcome <font color="red" face="Comic sans MS"> @Session["UserName"]</font></h1> </div> <textarea class="form-control" cols="40" rows="2" id="comment" ng-model="Message" ng-keydown="pushEnter($event)"></textarea> <input type="button" value="Gönder" class="btn-warning" ng-click="SendMessage()" id="btnGeneral" /> <br /><br /> <b>Konuşma Geçmişi:</b> <div class="editable2"> <div class="editable" contenteditable="true" ng-bind-html="MessageHistory"></div> </div> </div> <input type="hidden" id="hdnUserName" /> <input type="hidden" id="hdnConnectionID" /> </body> |
Full Index.cshtml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
@{ Layout = null; } <!DOCTYPE html> <html ng-app="app"> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <script src="~/scripts/angular.min.js"></script> <script src="~/Scripts/angular-sanitize.min.js"></script> <script src="~/scripts/jquery-3.1.1.min.js"></script> <script src="~/scripts/bootstrap.min.js"></script> <link href="~/Content/bootstrap.min.css" rel="stylesheet" /> <script src="~/Scripts/crpt.js"></script> <script src="~/scripts/jquery.signalR-2.2.2.min.js"></script> <script src="~/signalr/hubs"></script> <script> var hubProxy; var app = angular.module("app", ['ngSanitize']); app.controller('Controller', ['$scope', '$sce', '$timeout', function ($scope, $sce, $timeout) { hubProxy = $.connection.chat; $.connection.hub.logging = true; $("#hdnUserName").val(ConvertTurkish('@Session["UserName"]')); $scope.UserName = $("#hdnUserName").val(); $.connection.hub.start().done(function () { console.log("hub.start.done"); }).fail(function (error) { console.log(error); }); hubProxy.client.connected = function (_connectionID) { $scope.ConnectionID = _connectionID; $("#hdnConnectionID").val(_connectionID); console.log(_connectionID); } $.connection.hub.disconnected(function () { setTimeout(function () { $.connection.hub.start(); }, 2000); // 2sn sonra tekrar connection kurulur. }); hubProxy.client.SendMessageAll = function (Message, UserName) { if ($scope.MessageHistory == null || $scope.MessageHistory == undefined || $scope.MessageHistory == "") { $scope.MessageHistory = " "; } $scope.MessageHistory = $sce.trustAsHtml("<font color='red'><b>" + UserName + ":</b></font>") + Crypto.decode(Message) + "<br>" + $scope.MessageHistory; $scope.$apply(); } $scope.SendMessage = function () { var message = Crypto.encode($scope.Message); $scope.Message = ""; hubProxy.server.sendMessageToAll(message, $scope.UserName) }; $scope.pushEnter = function (event) { if (event.which == 13) { $scope.SendMessage(); } } }]); function ConvertTurkish(input) { input = input .replace(/Ü/g, "Ü").replace(/ü/g, "ü") .replace(/Ö/g, "Ö").replace(/ö/g, "ö") .replace(/Ç/g, "Ç").replace(/ç/g, "ç"); input = input .replace(/ı/g, "ı").replace(/İ/g, "İ") .replace(/Ü/g, "Ü").replace(/ü/g, "ü") .replace(/Ö/g, "Ö").replace(/ö/g, "ö") .replace(/Ğ/g, "Ğ").replace(/ğ/g, "ğ") .replace(/Ş/g, "Ş").replace(/ş/g, "ş") .replace(/Ç/g, "Ç").replace(/ç/g, "ç"); input = input .replace(/\u0131/g, "ı").replace(/\u0130/g, "İ") .replace(/\u00FC/g, "ü").replace(/\u00DC/g, "Ü") .replace(/\u00F6/g, "ö").replace(/\u00D6/g, "Ö") .replace(/\u011F/g, "ğ").replace(/\u011E/g, "Ğ") .replace(/\u015F/g, "ş").replace(/\u015E/g, "Ş") .replace(/\u00E7/g, "ç").replace(/\u00C7/g, "Ç"); return input; } </script> </head> <body> <div class="container" ng-controller="Controller" id="container"> <div style="background-color:lightblue !important" class="jumbotron"> <h1>RabbitMq İle Chat<br />@String.Format("".PadRight(8, '_')) Wellcome <font color="red" face="Comic sans MS"> @Session["UserName"]</font></h1> </div> <textarea class="form-control" cols="40" rows="2" id="comment" ng-model="Message" ng-keydown="pushEnter($event)"></textarea> <input type="button" value="Gönder" class="btn-warning" ng-click="SendMessage()" id="btnGeneral" /> <br /><br /> <b>Konuşma Geçmişi:</b> <div class="editable2"> <div class="editable" contenteditable="true" ng-bind-html="MessageHistory"></div> </div> </div> <input type="hidden" id="hdnUserName" /> <input type="hidden" id="hdnConnectionID" /> </body> </html> |
Chat:Hub (Class): Server Side tarafındaki, client tarafından bağlanılan Hub sınıfı.
- Aşağıda görüldüğü gibi Client Connect olduğu zaman ConnecrionID’si yine sadece kendisine parametre olarak “connected()” function’ına gönderilmektedir.
- Ayrıca “SendMessageAll()” methodu ile herhangi bir client’ın yazdığı mesajlar tüm online clientlara gönderilir.
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Chat : Hub { public async override Task OnConnected() { await Clients.Caller.connected(Context.ConnectionId); } public async Task SendMessageToAll(string Message, string UserName) { await Clients.All.SendMessageAll(Message, UserName); } } |
Böylece standart bir chat uygulamasını login işlemi ile birlikte yazmış olduk. Real Time Chat için SignalR Web Socket kullanılmıştır. Ön yüz tarafında da angularjs’in gücünden faydalanılmıştır. Ayrıca chat uygulamalarında bolca karşımıza çıkabilecek Türkçe karakter sorununa ve yazılanların en azından gözle okunmasını engelleyen yapılar üzerinde durulmuştur. Angualr1 üzerinde Html taglar nasıl yorumlanır ve Mvc’de Custom Filter nasıl yapılır ve de hangi amaca hizmet eder bir diğer konuşulan konuların arasındadır. Bir sonraki makalemizde, işte bu yazışmaları nasıl asenkron olarak var olan sisteme yük bindirmeden ve başka hangi alanlarda kullanabileceğimizi hep beraber tartışacağız.
Bir sonraki makalede görüşmek üzere hoşçakalın.
Source:
- https://www.geeksforgeeks.org/javascript-encode-decode-a-string-to-base64/
- https://stackoverflow.com/questions/784586/convert-special-characters-to-html-in-javascript
- https://docs.microsoft.com/tr-tr/aspnet/signalr/overview/getting-started/tutorial-high-frequency-realtime-with-signalr
- https://docs.microsoft.com/tr-tr/aspnet/core/signalr/hubs?view=aspnetcore-2.2
Eline sağlık. İyi bir çalışma..
Teşekkürler Kerim.
Hocam elinize sağlık, rabbitmq ile ilgili kısımları sabırsızlıkla bekliyoruz. Burada sizden ricam, microservice-message queue’i biraz daha derinlemesine anlatmanız :) teşekkürler.
Teşekkürler Mesut,
Queelar’ın mantığı birbirine benziyor. Birini anlamanız hepsi hakkında fikir sahibi olmanızı sağlar.
2. Bölüm de çok yakında :)
Güzel makale. Elinize sağlık.
Teşekkür ederim Hasan…
Part 2 yi 4 dört gözle bekliyoruz hocam :)