ããã«ã¡ã¯ãHabrïŒ 9æ14æ¥ã«ãASP.Net Coreã®SignalRã®æåã®ããŒãžã§ã³ãçºè¡šãããŸãããããã«é¢é£ããŠããã®ã€ãã³ãã«é¢ããã¡ã¢ã翻蚳ããããå°ãè¿œå ããããšã«ããŸããã å ã®ã¡ã¢ã¯ã MSDNããã°ã§å ¥æã§ããŸãã
æ°æ©èœ
SignalR for ASP.Net Coreã¯ãå ã®SignalRããŒãããæžãçŽããããŒãžã§ã³ã§ãã æ°ããSignalRã¯ãããã·ã³ãã«ã§ãä¿¡é Œæ§ãé«ãã䜿ããããã§ãã ãããã®å éšçãªå€æŽã«ãããããããã©ã€ãã©ãªAPIã以åã®ããŒãžã§ã³ã«æãè¿ã¥ããããšããŸããã
JavaScript / TypeScriptã¯ã©ã€ã¢ã³ã
SignalR for ASP.Net Coreã«ã¯ããŸã£ããæ°ããJavaScriptã¯ã©ã€ã¢ã³ãããããŸãã TypeScriptã䜿çšããŠèšè¿°ãããŠãããjQueryã«äŸåããªããªããŸããã ã¯ã©ã€ã¢ã³ãã¯ãããã€ãã®è¿œå ã®äŸåé¢ä¿ãæã€Node.jsããã䜿çšã§ããŸãã
ã¯ã©ã€ã¢ã³ãã¯ãã¯ã©ã€ã¢ã³ãã®Node.jsããŒãžã§ã³ïŒrequireãä»ããŠæ¥ç¶ïŒãããã³<script>
ã䜿çšããŠåã蟌ãããšãã§ãããã©ãŠã¶ãŒã®ããŒãžã§ã³ãå«ãnpmã¢ãžã¥ãŒã«ãšããŠé
åžãããŸãã ã¢ãžã¥ãŒã«ã«çµã¿èŸŒãŸããTypeScriptããããŒãã¡ã€ã«ã«ãããTypeScriptã¢ããªã±ãŒã·ã§ã³ããã¯ã©ã€ã¢ã³ããç°¡åã«äœ¿çšã§ããŸãã
ã¯ã©ã€ã¢ã³ãã¯ãããŒãžã§ã³9以éã®ChromeãFireFoxãEdgeãSafariãOperaãããã³Internet Explorerã®ææ°ããŒãžã§ã³ããµããŒãããŠããŸãïŒãã ãããã¹ãŠã®ãã©ã³ã¹ããŒããããã³ã«ãåãã©ãŠã¶ãŒãšäºææ§ãããããã§ã¯ãããŸããïŒã
ãã€ããªãããã³ã«ã®ãµããŒã
SignalR for ASP.Net Coreã¯ãJSONããŒã¹ã®ããã¹ããšMessagePackããŒã¹ã®ãã€ããªãšãã2ã€ã®çµã¿èŸŒã¿ãããã³ã«ãæäŸããŸãã éåžžãMessagePackãããã³ã«ã䜿çšããã¡ãã»ãŒãžã¯ãJSONã®åãã¡ãã»ãŒãžãããå°ãããªããŸãã ããšãã°ã1ã®æŽæ°å€ãå«ãããã¡ãã»ãŒãžã¯ãJSONã䜿çšãããš43ãã€ããæ¶è²»ããMessagePackã§ã¯16ãã€ãã®ã¿ãæ¶è²»ããŸãã ïŒãµã€ãºã®éãã¯ãã¡ãã»ãŒãžã®çš®é¡ããã®ã³ã³ãã³ããããã³äœ¿çšããããã©ã³ã¹ããŒããããã³ã«ã«ãã£ãŠç°ãªãå ŽåããããŸããSSEã¯ããã¹ããã©ã³ã¹ããŒãã§ãããããServer-Sent EventsïŒSSEïŒãä»ããŠéä¿¡ããããã€ããªã¡ãã»ãŒãžã¯base64ã§ãšã³ã³ãŒããããŸãïŒã
ãŠãŒã¶ãŒãããã³ã«ã®ãµããŒã
SignalRãããããã³ã«ã¯GitHubã«èšèŒãããŠãããã«ã¹ã¿ã å®è£ ãæ¥ç¶ã§ããæ¡åŒµãã€ã³ããå«ãŸããŠããŸãã
ã¹ããªãŒãã³ã°
ãµãŒããŒããã¯ã©ã€ã¢ã³ãã«ããŒã¿ã¹ããªãŒã ã転éã§ããããã«ãªããŸããã éåžžã®ããã¡ãœããåŒã³åºããšã¯ç°ãªããã¹ããªãŒãã³ã°ãšã¯ãåŒã³åºããçµäºããåã«ãµãŒããŒãã¯ã©ã€ã¢ã³ãã«çµæãéä¿¡ã§ããããšãæå³ããŸãã
ã¯ãªãŒã³ãªWebãœã±ããã§SignalRã䜿çšãã
SignalRãžã®æ¥ç¶ããã»ã¹ã¯ãWebãœã±ããã䜿çšããå Žåãã¯ã©ã€ã¢ã³ãã䜿çšããã«åäžã®èŠæ±ã§ãµãŒããŒã«æ¥ç¶ã§ããããã«ãªããŸã§ç°¡ç¥åãããŸããã
ç°¡ç¥åãããæ°Žå¹³ã¹ã±ãŒãªã³ã°
æ®å¿µãªãããã¢ããªã±ãŒã·ã§ã³ã®æ°Žå¹³ã¹ã±ãŒãªã³ã°ã«ã¯ãç¹å¹è¬ãã¯ãããŸãããåã¢ããªã±ãŒã·ã§ã³ã«ã¯ãã¹ã±ãŒãªã³ã°æã«èæ ®ããªããã°ãªããªãç¬èªã®èŠä»¶ããããŸãã ãã®ã¢ã«ãã¡çã§ã¯ãæ°Žå¹³ã¹ã±ãŒãªã³ã°ãç°¡çŽ åããæ°Žå¹³ã¹ã±ãŒãªã³ã°çšã®RedisããŒã¹ã®ã³ã³ããŒãã³ããæäŸããããã«ããã€ãã®äœæ¥ãè¡ã£ãŠããŸãã ãµãŒãã¹ãã¹ãªã©ã®ä»ã®ãããã€ããŒã®ãµããŒãã¯ãæçµãªãªãŒã¹ã§ãã¹ããããŠããŸãã
äœãå€ãã£ãïŒ
ASP.Net Coreã®SignalRã«å€ãã®æ°æ©èœãè¿œå ããŸããããåæã«æ¢åã®æ©èœã®ãµããŒããåé€ãããããã®åäœãå€æŽããããšã決å®ããŸããããã®çµæãSignalRã®æ°ããããŒãžã§ã³ã¯ä»¥åã®æ©èœãšäºææ§ããããŸããã ã€ãŸããå€ãã¯ã©ã€ã¢ã³ããæ°ããã¯ã©ã€ã¢ã³ãã§äœ¿çšããããå€ãã¯ã©ã€ã¢ã³ããæ°ãããµãŒããŒã§äœ¿çšãããããããšã¯ã§ããŸããã 以äžã¯ãæ°ããããŒãžã§ã³ã§é€å€ãããæ©èœãŸãã¯åäœãå€æŽãããæ©èœã§ãã
ç°¡ç¥åãããæ¥ç¶ã¢ãã«
SignalRã®æ¢åã®ããŒãžã§ã³ã§ã¯ãæ¥ç¶ã«å€±æããå Žåãã¯ã©ã€ã¢ã³ãã¯å¥ã®ãã©ã³ã¹ããŒãã䜿çšããŠæ¥ç¶ãè©Šã¿ãŸãã ã¯ã©ã€ã¢ã³ãã¯ãå©çšå¯èœãªãã©ã³ã¹ããŒãã®ããããã䜿çšããŠãµãŒããŒã«æ¥ç¶ã§ããªãå Žåãæ¥ç¶ã確ç«ããŸããã SignalRã®æ°ããããŒãžã§ã³ã§ã¯ããã®æ©èœã¯ãµããŒããããªããªããŸããã
ãŸããèªååæ¥ç¶ã¯äœ¿çšã§ããªããªããŸããã 以åã®ããŒãžã§ã³ã§ã¯ãSignalRã¯ãµãŒããŒãšã®æ¥ç¶ã倱ã£ããšãã«åæ¥ç¶ããããšããŸããã ã¯ã©ã€ã¢ã³ããåæãããå ŽåããŠãŒã¶ãŒã¯æ瀺çã«æ°ããæ¥ç¶ã確ç«ããå¿ èŠããããŸãã ããã¯ä»¥åã«å¿ èŠã§ãã£ãããšã«æ³šæããŠãã ãã-ç¹å®ã®ã¿ã€ã ã¢ãŠãå ã«åŸ©å ã§ããªãå Žåãã¯ã©ã€ã¢ã³ãã¯èªåçã«åæ¥ç¶ããããšããªããªããŸããã ãã®æ©èœãåé€ãããã1ã€ã®çç±ã¯ãã¯ã©ã€ã¢ã³ãã«éä¿¡ãããã¡ãã»ãŒãžãä¿åããã³ã¹ããé«ãããšã§ãã ããã©ã«ãã§ã¯ããµãŒããŒã¯æåŸã®1000件ã®ã¡ãã»ãŒãžãä¿åãããããæ¥ç¶ã倱ãããã¯ã©ã€ã¢ã³ãã«ã¡ãã»ãŒãžãåéä¿¡ã§ããŸããã åæ¥ç¶ã«ã¯ç¬èªã®ãããã¡ãããããããããã®ã¡ãã»ãŒãžãä¿åãããšã¡ã¢ãªã倧éã«æ¶è²»ãããŸãã
ã¹ãã£ãããŒã»ãã·ã§ã³ã®ãã€ã³ã
SignalRã®ä»¥åã®ããŒãžã§ã³ã®æ°Žå¹³ã¹ã±ãŒãªã³ã°ã¢ãããŒãã䜿çšããŠãã¯ã©ã€ã¢ã³ãã¯ãã¡ãŒã å ã®ä»»æã®ãµãŒããŒã«åæ¥ç¶ãŸãã¯ã¡ãã»ãŒãžãéä¿¡ã§ããŸããã ãã®ã¢ãããŒãã®å€æŽãšèªååæ¥ç¶ã®æåŠã«ãããã¯ã©ã€ã¢ã³ãã¯æ¥ç¶ã確ç«ããããµãŒããŒãšã®ã¿ã¡ãã»ãŒãžã亀æããå¿ èŠããããŸãã
æ¥ç¶çšã®1ã€ã®ãã
SignalRã®æ°ããããŒãžã§ã³ã¯ãåäžã®æ¥ç¶å ã§ã®è€æ°ã®ããã®äœ¿çšããµããŒãããŠããŸããã ããã«ãããã¯ã©ã€ã¢ã³ãAPIãç°¡çŽ åãããè¿œå ã®èªèšŒããªã·ãŒããã®ä»ã®ããã«ãŠã§ã¢ã®ããæ¥ç¶ãžã®é©çšãç°¡çŽ åãããŸããã ãŸããæ¥ç¶ãéå§ããåã«ãããã®ã¡ãœãããžã®ãµãã¹ã¯ãªãã·ã§ã³ã¯äžèŠã«ãªããŸããã
ãã®ä»ã®å€æŽ
ã¯ã©ã€ã¢ã³ããšããéã§ä»»æã®ã¹ããŒã¿ã¹ã転éããæ©èœïŒHubStateïŒãããã³ã¹ããŒã¿ã¹ã¡ãã»ãŒãžïŒé²æã¡ãã»ãŒãžïŒã®ãµããŒããåé€ãããŸããã ãŸãããããããã·ã®ä»£æ¿ã¯ãã®ããŒãžã§ã³ã§ã¯è¿œå ãããŸããã§ããã
ã¯ããã«
SignalRã®ã€ã³ã¹ããŒã«ã¯æ¯èŒçç°¡åã§ãã æ°ããASP.Net Coreã¢ããªã±ãŒã·ã§ã³ãäœæããããäŸã«ç€ºãããã«ãMicrosoft.AspNetCore.SignalR nugetããã±ãŒãžãžã®äŸåé¢ä¿ãè¿œå ããå¿ èŠããããŸã
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" /> <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.0.0-alpha1-final" /> </ItemGroup> <ItemGroup> <Folder Include="wwwroot\scripts\" /> </ItemGroup> </Project>
ãããŠããªãã®ããã¯ã©ã¹
using System; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; namespace Sample { public class Chat : Hub { public Task Send(string message) { return Clients.All.InvokeAsync("Send", message); } } }
ãã®ããã«ã¯ãåŒã³åºããããšãã«ãåã¯ã©ã€ã¢ã³ãã§Sendã¡ãœãããåŒã³åºãã¡ãœãããå«ãŸããŠããŸãã
ããã¯ã©ã¹ãè¿œå ããããéä¿¡ãããèŠæ±ã次ã®ããã«åŠçããããã«ãµãŒããŒãæ§æããå¿ èŠããããŸãã
using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; namespace Sample { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddSignalR(); } public void Configure(IApplicationBuilder app) { app.UseSignalR(routes => { routes.MapHub<Chat>("chat"); }); } } }
å¿
èŠãªèšå®ãèšå®ããåŸãã¯ã©ã€ã¢ã³ãããããã¡ãœãããåŒã³åºããããã¯ã©ã€ã¢ã³ãããããã¡ãœãããåŒã³åºãããã§ããŸãã ãã©ãŠã¶ã§JavaScriptã¯ã©ã€ã¢ã³ãã䜿çšããã«ã¯ãSignalRã¯ã©ã€ã¢ã³ããå«ãnpmããã±ãŒãžã次ã®ã³ãã³ãã§ã€ã³ã¹ããŒã«ããå¿
èŠããããŸã
npm install @aspnet/signalr-client
ããã§ãsignalr-client.jsããããžã§ã¯ãã®ã¹ã¯ãªãããã©ã«ããŒã«ã³ããŒããããŒãžã§æ¥ç¶ã§ããŸãã
<script src="scripts/signalr-client.min.js"></script>
ã¹ã¯ãªãããæ¥ç¶ãããšããµãŒããŒãžã®æ¥ç¶ãéå§ãããµãŒããŒãšã³ãã³ãã亀æã§ããŸã
let connection = new signalR.HubConnection('/chat'); connection.on('send', data => { console.log(data); }); connection.start() .then(() => connection.invoke('send', 'Hello'));
管ç察象ã¯ã©ã€ã¢ã³ãã䜿çšããã«ã¯ãããã±ãŒãžMicrosoft.AspNetCore.SignalR.Clientãžã®ãªã³ã¯ãè¿œå ããå¿ èŠããããŸã
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="1.0.0-alpha1-final" /> </ItemGroup> </Project>
ãã®åŸãããã®ã¡ãœããã«ã¢ã¯ã»ã¹ããã¯ã©ã€ã¢ã³ãã¡ãœãããžã®åŒã³åºããåŠçã§ããããã«ãªããŸãã
ã¹ããªãŒãã³ã°ãå©çšããå Žåã¯ã ReadableChannel<T>
ãŸãã¯IObservable<T>
ãè¿ãããã¡ãœãããäœæããå¿
èŠããããŸãã 以äžã¯ãæ ªäŸ¡ãã¹ããªãŒã ã§è»¢éããStockTimerã®äŸã®ããã¡ãœããã§ãã
public IObservable<Stock> StreamStocks() { return _stockTicker.StreamStocks(); }
ãã®ã¡ãœããã«ãã£ãŠåŒã³åºãããJavaScriptã³ãŒãã¯æ¬¡ã®ããã«ãªããŸã
function startStreaming() { connection.stream("StreamStocks").subscribe({ next: displayStocks, error: function (err) { logger.log(err); } }); }
ãµãŒããŒãã¹ããªãŒã ã¢ã€ãã ãéä¿¡ãããã³ã«ãã¯ã©ã€ã¢ã³ãã®displayStocksã¡ãœãããåŒã³åºãããŸã
CïŒã³ãŒãããããã¡ãœãããåŒã³åºãæ¹æ³ã以äžã«ç€ºããŸãã
private async Task StartStreaming() { var channel = connection.Stream<Stock>("StreamStocks", CancellationToken.None); while (await channel.WaitToReadAsync()) { while (channel.TryRead(out var stock)) { Console.WriteLine($"{stock.Symbol} {stock.Price}"); } } }
SignalRã®æ¢åã®ããŒãžã§ã³ããã®ã¢ããã°ã¬ãŒã
ä»åŸæ°é±éã§ãSignalRã®ä»¥åã®ããŒãžã§ã³ããã®ç§»è¡ããã¥ã¡ã³ãããªãªãŒã¹ãããŸãã
æ¢ç¥ã®åé¡
- ãµãŒããŒãIISãä»ããŠèµ·åãããå Žåãéã¢ã¯ãã£ããªç¶æ ã2åéç¶ããšãSSEãã©ã³ã¹ããŒããä»ããæ¥ç¶ã倱ãããå ŽåããããŸã
- IISãWindows 7ãŸãã¯Windows Server 2008 R2ã§äœ¿çšãããŠããå ŽåãWebãœã±ããã¯æ©èœããŸãã
- CïŒã®ã¯ã©ã€ã¢ã³ãã§SSEã䜿çšãããšããµãŒããŒããããŒã¿ãåä¿¡ããŠââããéã«ã¯ã©ã€ã¢ã³ããéããããå Žåãã¯ã©ã€ã¢ã³ããããªãŒãºããããšããããŸã
- ã¯ã©ã€ã¢ã³ãã¯ã¹ããªãŒã åŒã³åºãããã£ã³ã»ã«ã§ããŸãã
- UglifyJSã¯ES6æšæºããµããŒãããŠããªããããangular-cliã䜿çšããŠè£œåããŒãžã§ã³ãæ§ç¯ããããšã¯ã§ããŸããã ãã®åé¡ã®åé¿çã¯ããã®ã³ã¡ã³ãã«èšèŒãããŠããŸãã
翻蚳ã®èè ãã
ç§ããã¯ãAngularã®SPAã¢ããªã±ãŒã·ã§ã³ã§TypeScriptããŒãžã§ã³ã®ã¯ã©ã€ã¢ã³ãã䜿çšããå°ããªäŸãè¿œå ããŸãã äžèšã®ãã¯ããã«ãã»ã¯ã·ã§ã³ã®ãã¹ãŠã®æé ãå®äºããããAngularã³ã³ããŒãã³ãã§HubConnection
ã¯ã©ã¹ãã€ã³ããŒããããã®ã¡ãœããã䜿çšããã ãã§ååã§ãã fetchdata.component.ts
ã䜿çšããŠäœæãããã¢ããªã±ãŒã·ã§ã³ã®åºæ¬çãªäŸã§ã¯ã次ã®ããã«ã counter
ã³ã³ããŒãã³ãããfetchdata.component.ts
ã³ã³ããŒãã³ããžã®ã¯ãªãã¯æ°ã衚瀺ããæ©èœãè¿œå ããŸãã
import { Component, Inject } from '@angular/core'; import { Http } from '@angular/http'; import { HubConnection } from "@aspnet/signalr-client"; @Component({ selector: 'fetchdata', templateUrl: './fetchdata.component.html' }) export class FetchDataComponent { public forecasts: WeatherForecast[]; public count: number; private notificationHub: HubConnection; constructor(http: Http, @Inject('BASE_URL') baseUrl: string) { http.get(baseUrl + 'api/SampleData/WeatherForecasts').subscribe(result => { this.forecasts = result.json() as WeatherForecast[]; }, error => console.error(error)); this.notificationHub = new HubConnection("/notifications"); this.notificationHub.on("send", (data) => { this.count = data; }); this.notificationHub.start(); } }
ã³ã³ã¹ãã©ã¯ã¿ãŒåŒã³åºãã§æå®ãããããã¢ãã¬ã¹ã¯ãASP.Net Coreã¢ããªã±ãŒã·ã§ã³ã®Startupã¯ã©ã¹ã§æå®ããããã®ãšäžèŽããå¿
èŠããããŸãïŒå€æã®äŸã§ã¯ããã®ã¢ãã¬ã¹ã¯ "chat"ã§ãïŒã ããã®Sendã¡ãœããã¯ã次ã®ããã«counter
ã³ã³ããŒãã³ãããåŒã³åºãããŸã
this.notificationHub.invoke("send", this.currentCount);
PSããã¯ç§ã®æåã®æçš¿ã§ãããåæã«ç¿»èš³ã§ããããŸãããã®ãããç§ã¯PMã«éãããã«ãã®å質ã«é¢ããã³ã¡ã³ããæ±ããŸãã