Azure Üzerinde SignalR Service Oluşturma ve Angular Bir Sayfada Kullanma
Selamlar,
Bu makalede, Azure üzerinde signalR bir service oluşturup, Angular 7 bir web uygulamasında kullanacağız.
Azure Üzerinde SignalR Bir Servisin Kurulması:
Öncelikle yukarıda görüldüğü gibi, Create a resource + buttonuna tıklanarak, arama kutucuğuna “signalR” yazılır. Ve böylece “SignalR Service” oluşturulur.
Daha sonra karşımıza yukarıdaki gibi bir ekran gelir:
- Resource Name: signalR Hub servisinin adı yazılır.
- Location: Türkiye’ye en yakın, “North Europe” seçilir.
- Pricing tier: Aşağıdaki ödeme planlarından biri seçilir. Bu örnekte FREE olan seçilmiştir.
Tüm özellikler doldurulduktan sonra, aşağıda görüldüğü gibi Create butonuna basılarak, ilgili BannerHub signalR servisi Azure üzerinde oluşturulmuş olunur.
SignalR service Azure üzerinde ayağa kalktıktan sonra, aşağıdaki gibi bir ekran ile karşılaşılır. Bağlanan Client Hub sayısı ve Backend’de açılan “Server Hub” sayısı, buradan kolaylıkla monitor edilebilir. Settings’den Keys alanına gelinir ise, ilgili servisin Connection String’ine erişilir.
Keys alanına gelindiğinde aşağıdaki gibi bir ekran ile karşılaşılır. İlgili SignalR servisin yolu : “bannerhub.service.signalr.net“‘dir. Ayrıca Connection String gene burada tanımlanmıştır. Bir sonraki adımda yazılacak .Net Core SignalR projesi Azure servis’e, burada görünen Connection String ile erişecektir. İstenir ise, Connection String içinde geçen, “Primary Key” tekrardan yaratılarak (Regenarate Primary key) connection string’in güvenlik amaçlı değiştirilmesi sağlanır.
.Net Core SignalR Service:
Şimdi sıra geldi, öncelikle .Net Core signalR servisinin oluşturulmasına. Bu servis Azure üzerindeki SignalR servisini kullanacaktır. Aşağıdaki komut ile ilgili proje yaratılır. WebApi yaratılmasındaki sebep, ilgili servis call edilirken, ayakta olup olmadığına bakmaktır.
1 |
dotnet new webapi -o AzureSignalR |
Azure.SignalR kütüphanesi, aşağıdaki komut ile eklenir.
1 |
dotnet add package Microsoft.Azure.SignalR --version 1.0.6 |
Test amaçlı, sadece alttaki Method yazılmıştır.
1 2 3 4 5 6 7 8 9 10 11 |
[Route("api/[controller]")] [ApiController] public class BannerController : ControllerBase { // GET api/values [HttpGet] public ActionResult<IEnumerable<string>> Get() { return new string[] { "value1", "value2" }; } } |
Oluşturulan signalR BannerHub sınıfı, aşağıdaki gibidir:
RobotController.cs:
- OnConnectedAsync(): İlgili method ile, signalR BannerHub sınıfına bağlanan herbir client’ın “GetConnectionId()” function’ı çalıştırılıp, kendisine özel oluşturulan connectionId‘si gönderilmiştir.
- MoveRobot(): Burada amaç, kendisine gelen X ve Y koordinatlarını, tüm clientların moveRobot() action’ınına göndererek, realtime olarak ilgili robotun tüm clientlarda hareket etmesinin sağlanmasıdır.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class BannerHub : Hub { public override async Task OnConnectedAsync() { await this.Clients.Caller.SendAsync("GetConnectionId", this.Context.ConnectionId); } public async Task MoveRobot(int y, int x) { Console.WriteLine($"Y:{y} - X:{x}"); await this.Clients.All.SendAsync("moveRobot", y, x); } } |
Startup.cs:
Aşağıda görüldüğü gibi ConfigureServices’inde signalR, Azure üzerindeki servise bağlanılmıştır.
1 2 3 4 5 |
public void ConfigureServices(IServiceCollection services) { services.AddSignalR().AddAzureSignalR("Endpoint=https://bannerhub.service.signalr.net;AccessKey=*******************=;Version=1.0;"); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); } |
- Client Side tarafta, signalR Hub’a bağlanırken, CORS’a yakalanmamak için alttaki gibi bir konfigürasyonun yapılması gerekmektedir.
- app.UseAzureSignalR() : “BannerHub” signalR hub sınıfına erişilecek yol, yani routing bu method ile tanımlanır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { . . app.UseCors((builder) => { builder.WithOrigins("http://localhost:4200") .AllowAnyHeader() .WithMethods("GET", "POST") .AllowCredentials(); }); app.UseAzureSignalR(router => router.MapHub<BannerHub>("/bannerhub")); app.UseMvc(); } |
Program.cs: Uygulamanın, aynı IIS’de olduğa gibi kestrel’de yayımlanabilmesi için “0.0.0.0” portundan yayınlanması gerekmektedir. Ayrıca default 5001 portu değiştirilerek, “1923” yapılmıştır.
1 2 3 4 5 |
public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseUrls("http://0.0.0.0:1923") .UseStartup<Startup>(); } |
Angular Web Application:
Sıra geldi Angular 7 kullanacağımız client side projeye. Aşağıdaki komut ile ilgili proje oluşturulur.
1 |
ng new azuresignalRClient |
app.component.html: Ekrana bir robot resmi konmuştur. İlgili robot, sayfanın herhangi bir yerine tıklanarak gitmesi sağlanacaktır.
1 2 3 |
<div id="content" style="position:absolute;"> <img src="assets/images/robot.png" style="width:200px" /> </div> |
Aşağıdaki komut ile, Angular 7 projesine son signalR kütüphanesi eklenmiş olunur.
1 |
npm install @aspnet/signalr |
app.component.ts: SignalR Hub sınıfı, Mediator Design Pattern mantığındaki çalışmaktadır. Tüm clientlar, bir merkez sınıfa bağlanmaktadır. Sınıfların arasındaki iletişim, bu merkez aracılığı ile sağlamaktadır. Herbir client, signalR Hub sınıfına bağlanıp, kendisine özel yeni bir connectionId almaktadır.
- _connectionId: SignalR Hub sınıfına bağlanıldığı zaman, alınacak connectionId değeridir. Yani herbir client’ın cep numarasıdır.
- _hubConnection: signalR Hub sınıfa bağlantıyı sağlayan, HubConnection nesnesidir.
- “@HostListener(‘document:click’, [‘$event’]) documentClick(event) {“: Form üzerinde, “click” event’i global olarak dinlenir.
- “var x = event.pageX – 100, y = event.pageY;” : Tıklanan noktanın X ve Y koordinatları alınır.
- “$(‘div’).animate({“: Sayfa üzerindeki “div” elementi animatif olarak tıklanan noktaya getirilir. Not: Bu komut ile, sayfa üzerindeki tüm “div”lere erişilir. Ama bu örnekte herhangi bir sorun yoktur. Çünkü sayfada Robot resmi olan, tek bir div vardır:)
- “this._hubConnection.invoke(“MoveRobot”, y, x);”: SignalR BannerHub sınıfının, “MoveRobot()” methodu, ilgili X ve Y koordinatları ile tetiklenir.
- ” this._hubConnection = new HubConnectionBuilder()” : SignalR sınıfına bağlantıyı sağlayan, HubConnection nesnesi üreten methoddur.
- .withUrl(“http://0.0.0.0:1923/bannerhub”) : Server Side signalR Hub sınıfına bağlanılır.
- “this._hubConnection.start().then(” : Client, signalR hub sınıfına bağlandığı zaman tetiklenen methoddur.
- “this._hubConnection.on(‘GetConnectionId’, (connectionId: string) => {” : Server Side BannerHub sınıfının “OnConnectedAsync()” methodu, client’ın => BannerHub signalR sınıfına Connect olması durumunda çağrılır. İşte bu noktada, server side taraftan Client üzerindeki “GetConnectionId()” function’ı alınan yeni connectionId ile tetiklenir. Ve Client’ın ekranına, ilgili connectionId basılır.
- “this._hubConnection.on(“moveRobot”, (y: number, x: number) => {” : signalR BannerHub sınıfının “MoveRobot(int y, int x)” methodu, kendisine bağlı tüm clientların “moveRobot()” functionını, kendisine gelen koordinatlar ile tetikler. Amaç, herhangi bir client’ın ekranı tıklanıp, Robotun bu tıklanan X ve Y konumuna gelmesi durumunda, güncel son koordinatların diğer clientlara da bildirilip, onlardaki robotların da aynı noktaya gelmesinin sağlanmasıdır.
Not: Animasyonlar için Jquery kullanılmıştır. Angular’ın animation komutlarının haddinden fazla karışık olmasından dolayı, basitlik amaçlı Jquery tercih edilmiştir. Angular projesine Jquery, aşağıdaki komut ile eklenmektedir.
1 |
npm install jquery |
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 |
import { Component, OnInit, HostListener } from '@angular/core'; import { HubConnection, HubConnectionBuilder, LogLevel } from '@aspnet/signalr'; import * as signalR from '@aspnet/signalr'; import * as $ from 'jquery'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit { title = 'azureSignalRClient'; _connectionId: string; _hubConnection: HubConnection; @HostListener('document:click', ['$event']) documentClick(event) { var x = event.pageX - 100, y = event.pageY; $('div').animate({ top: y, left: x }, 1000); this._hubConnection.invoke("MoveRobot", y, x); } public ngOnInit() { this._hubConnection = new HubConnectionBuilder() .withUrl("http://0.0.0.0:1923/bannerhub") .build(); this._hubConnection.start().then( () => console.log("Hub Connection Start")) .catch(err => console.log(err)); this._hubConnection.on('GetConnectionId', (connectionId: string) => { this._connectionId = connectionId; //alert(`ConnectionID:${this._connectionId}`); console.log("ConnectionID :" + connectionId); }); this._hubConnection.on("moveRobot", (y: number, x: number) => { $('div').animate({ top: y, left: x }, 1000); }); } } |
SignalR Hub sınıfına bağlanma, herbir client için socket açılması ve connectionId’nin verilmesi işlemlerinin tamamı, Azure üzerindeki signalR servisi ile yapılmaktadır. Böylece performans, istendiğinde scale işlemleri ve continuous integration’ın tamamı Azure üzerine yıkılmış olunur.
Geldik bir makalenin daha sonuna. Yeni bir makalede görüşmek üzere hepinize hoşçakalın.
Son Yorumlar