Како започети рад са СигналР на Азуреу са ЈаваСцрипт-ом

Пре неки дан, неки фини програмери у мојој компанији спремали су се да покрену страницу за ажурирање статуса. Испробали смо га опсежно, али сада смо се спремали да га избацимо у великој мери.

Био сам забринут због његове зависности од АПИ сервера који је недавно функционисао. Нисмо утврдили основни узрок проблема на страни АПИ-ја, а ова апликација користи анкетирање - то јест, непрестано тражи од АПИ-ја нове податке. Ако се тај АПИ сруши, узима нашу апликацију са собом и повећано оптерећење наше апликације може погоршати проблеме које видимо.

Туристи у Ирској, можда чекају поруку

Један од начина да се одмакнете од анкетирања је интегрисање СигналР, трајног алата за повезивање који користи вебсокете и сродне технологије како би омогућио серверима да гурну исправке клијентима.

Технологија је написана у .НЕТ-у, а већина документације коју ћете наћи на вебу користи Ц #. Овај водич обухватиће основну имплементацију ЈаваСцрипт-а.

Шта ради?

СигналР отвореног кода ствара трајну везу између клијента и сервера. Прво користи веб-утичнице, затим лонгполлинг и друге технологије када веб-утичнице нису доступне.

Једном када су клијент и сервер створили везу, СигналР се може користити за „емитовање“ порука клијенту. Када клијент прими те поруке, може обављати функције попут ажурирања продавнице.

Најчешћи пример наведен за веб-сокете је апликација за ћаскање - нови подаци морају бити приказани кориснику, а да она не мора да освежава страницу. Али ако ваш сервер добије било каква ажурирања о промени података које требате приказати клијенту, ово је можда услуга за вас.

СигналР на Азуре платформи

Можда зато што га је развио Мицрософт, СигналР има врло чисту интеграцију на Азуре платформи у облаку. Као и друге функције функције, створићете окидач „ин“ и „оут“ везу за емитовање порука.

Трошкови

Будући да сам први размотрио ову технологију у својој компанији, морао сам мало да истражим трошкове ове услуге. Азуре наплаћује око 50 долара месечно за једну „јединицу“ услуге СигналР - 1000 истовремених веза и милион порука дневно. Такође постоји бесплатна услуга за оне који се играју или за мала предузећа.

Било је заиста добро што сам ископао те бројеве, као што ћете видети мало испод.

Направите чвориште СигналР

Хајде да почнемо. Требаће нам чвориште СигналР, две функције и клијентски код које ћемо додати у нашу веб апликацију.

Идите на СигналР -> Додајте и попуните своје детаље. Потребна је секунда да радник изгради вашу услугу. Обавезно дајте услузи пристојно име ресурса, јер ћете је користити са осталим апликацијама. Узмите и тастере -> низ везе за употребу у нашем повезу.

Постављање СигналР на Азуреу

Креирајте своју функцијску апликацију за слање СигналР порука

Будући да радимо са Азуреом, креираћемо функционалне апликације за интерфејс са СигналР. Мало пре сам написао почетни пост на блогу о апликацијама Азуре функција.

Овај туторијал претпоставља да већ знате како да радите са функцијским апликацијама. Наравно, са овим библиотекама можете радити без обавезујуће магије, али мораћете сами да направите превод .НЕТ кода!

Апликација за повезивање

Прво што нам треба је начин да клијенти затраже дозволу за повезивање са нашом услугом СигналР. Код ове функције не може бити основнији:

module.exports = function (context, _req, connectionInfo) { context.res = { body: connectionInfo } context.done() } 

Магија се све догађа у везама, где увлачимо нашу СигналР услугу. Окидач је ХТТП захтев који наш клијент може да позове.

{ "bindings": [ { "authLevel": "function", "type": "httpTrigger", "direction": "in", "name": "req", "methods": ["get"] }, { "type": "signalRConnectionInfo", "name": "connectionInfo", "hubName": "your-signalr-service-name", "connectionStringSetting": "connection-string", "direction": "in" } ] } 

Шифра клијента

Да би приступио овој методи, наш клијент ће позвати:

import * as signalR from '@microsoft/signalr' const { url: connectionUrl, accessToken } = await axios .get(url-to-your-connection-app) .then(({ data }) => data) .catch(console.error) 

Наша функција функција ће вратити а urlи accessToken, које затим можемо користити за повезивање са нашом услугом СигналР. Имајте на уму да смо везивање креирали помоћу hubNameнаше услуге СигналР - то значи да у једном клијенту можете имати више веза са различитим чвориштима.

Радиодифузна служба

Сада смо спремни за почетак слања порука. Поново ћемо почети са апликацијом функције. Узима окидач и издаје СигналР поруку.

Окидач може бити други помоћу објављивања поруке, догађаја из чворишта догађаја или било ког другог окидача који Азуре подржава. Морам да покренем промене базе података.

{ "bindings": [ { "type": "cosmosDBTrigger", "name": "documents", "direction": "in", [...] }, { "type": "signalR", "name": "signalRMessages", "hubName": "your-signalr-service-name", "connectionStringSetting": "connection-string", "direction": "out" } ] } 

И код. Опет, мртво једноставно.

module.exports = async function (context, documents) { const messages = documents.map(update => { return { target: 'statusUpdates', arguments: [update] } }) context.bindings.signalRMessages = messages } 

СигналР поруке узимају а targetи argumentsприговарају. Једном када се ваши окидачи покрену, то је све што вам је потребно за почетак рада са СигналР-ом на серверу! Мицрософт нам је све ово веома олакшао.

Шифра клијента

На страни клијента, ствари су мало сложеније, али не и њима се не може управљати. Ево остатка клијентског кода:

const connection = new signalR.HubConnectionBuilder() .withUrl(connectionUrl, { accessTokenFactory: () => accessToken }) // .configureLogging(signalR.LogLevel.Trace) .withAutomaticReconnect() .build() connection.on('statusUpdates', data => { // do something with the data you get from SignalR }) connection.onclose(function() { console.log('signalr disconnected') }) connection.onreconnecting(err => console.log('err reconnecting ', err) ) connection .start() .then(res => // Potential to do something on initial load) .catch(console.error) 

Трошимо connectionUrlи accessTokenдобили смо од функције повезивања раније, а затим градимо везу помоћу тих вредности.

Затим слушамо поруке помоћу дељеног кључа (за мене је то statusUpdates) и пружамо руковаоце функцијама за затварање и поновно повезивање.

Коначно започињемо везу. Овде можемо пружити функцију почетног оптерећења. Требао ми је један за преузимање почетних података за приказ тренутног статуса. Ако градите апликацију за ћаскање, можда ћете овде морати да преузмете почетне поруке.

Ово је (готово, можда) све што вам је потребно за почетак рада са ЈаваСцрипт-ом са СигналР-ом на Азуреу!

Обим према кориснику

Али можда и ви, попут мене, морате да пошаљете пуно порука великом броју корисника.

Када сам ово први пут пустио у производњу, на подскупу корисника, уништавао сам сваку везу са сваком појединачном исправком. Будући да клијентски код може да обухвати поруке које слуша, користио сам отприлике statusUpdates-${userId}тако да клијент види само своја ажурирања.

That could work just fine if you have very low volume, and the more general one is great if everybody in your system needs the same message. But the status I work with is particular to an individual.

800.000 СигналР порука послатих са Азуре платформе

Remember how Azure charges per "unit" and each unit has one million messages? I hit that during a few hours of testing this during a not-busy time.

Azure counts each message SignalR has to send as one message. That is, if five connections are hooked up to your hub and you send ten messages, that counts as 50, not 10. This was a surprise to me, and also required a couple more hours of research.

We can scope our SignalR function code to send only to certain users. First, we update the connection app to accept userId as a query param:

 { "type": "signalRConnectionInfo", "name": "connectionInfo", "userId": "{userId}", "hubName": "your-signalr-service-name", "connectionStringSetting": "connection-string", "direction": "in" } 

Then we update the broadcasting function to send only to that user:

const messages = documents.map(update => { return { target: 'statusUpdates', userId: update.user.id, arguments: [update] } }) 

The broadcasting service won't know who has connected, so you'll need to trigger it with something that has access to a unique ID that the client will also have access to.

The client code simply passes in the userId as a query param:

const { url: connectionUrl, accessToken } = await axios .get(`${url-to-your-connection-app}&userId=${userId}`) .then(({ data }) => data) .catch(console.error) 

I swear to you, the only place on the entire internet I found to let me know how to request a connection using the userId was an answer on this Stack Overflow question.

The internet is amazing, and JavaScript Azure docs are hard to come by.

Resources

  • SignalR Javascript client docs from Microsoft
  • Configuring Users and Groups when sending SignalR messages -

    examples in C# but you can maybe figure out how the JavaScript client is going to behave and make some educated guesses.

  • SignalR Service bindings for Azure Functions
  • Client API
  • Working with Groups in SignalR
  • Водич: Провера идентитета услуге Азуре СигналР са Азуре функцијама

Овај пост се првобитно појавио на вилкие.тецх.