Azure Functions development and configuration with Azure SignalR Service (Разработка и настройка функций Azure с помощью Службы Azure SignalR)

Приложения Azure Functions могут использовать привязки службы Azure SignalR для добавления возможностей работы в режиме реального времени. Клиентские приложения используют клиентские пакеты SDK, доступные на нескольких языках, для подключения к Службе Azure SignalR и получения сообщений в режиме реального времени.

В этой статье описываются основные понятия разработки и настройки приложения-функции Azure с интеграцией со Службой SignalR.

Внимание

Необработанные строки подключения представлены в этой статье исключительно для демонстрации.

Строка подключения включает информацию о авторизации, необходимую приложению для доступа к службе Azure SignalR. Ключ доступа в строке подключения аналогичен паролю привилегированного пользователя для службы. В рабочих средах всегда защищать ключи доступа. Используйте Azure Key Vault для безопасного управления ключами и защиты строки подключения с помощью Microsoft Entra ID и авторизации доступа с помощью Microsoft Entra ID.

Старайтесь не распространять ключи доступа среди других пользователей, жестко программировать их или где-то сохранять в виде обычного текста в открытом доступе для других пользователей. Меняйте свои ключи, если считаете, что они могли быть скомпрометированы.

Конфигурация Службы SignalR

Служба Azure SignalR можно настроить в разных режимах. При использовании с платформой Функции Azure служба должна быть настроена в Бессерверном режиме.

На портале Azure откройте страницу Параметры для ресурса Службы SignalR. Установите Режим службы — Бессерверный.

Режим Службы SignalR

Разработка функций Azure

Для приложения в режиме реального времени, созданного с использованием функций Azure и службы Azure SignalR, требуется по крайней мере две функции Azure:

  • Функцияnegotiate, вызываемая клиентом для получения действительного маркера доступа к службе SignalR и URL-адреса конечной точки.
  • Одна или несколько функций, обрабатывающих сообщения, отправляемые из службы SignalR клиентам.

Функция согласования

Клиентскому приложению требуется допустимый токен доступа для подключения к Службе Azure SignalR. Маркер доступа может быть анонимным или прошедшим проверку подлинности в идентификаторе пользователя. Бессерверным приложениям службы SignalR требуется конечная точка HTTP с именем negotiate, чтобы получить токен и другую информацию о подключении, например URL-адрес конечной точки службы SignalR.

Используйте функцию Azure с триггером HTTP и SignalRConnectionInfo входную привязку для создания объекта сведений о подключении. Функция должна иметь маршрут HTTP, который заканчивается на /negotiate.

При использовании модели на основе классов в C#не требуется SignalRConnectionInfo входная привязка и можно гораздо проще добавлять пользовательские утверждения. Дополнительные сведения см. в разделе «Опыт согласования в модели, основанной на классах».

Для получения дополнительной информации о функции negotiate, обратитесь к разделу разработка Azure Functions.

Чтобы узнать, как создать аутентифицированный токен, см. статью "Использование аутентификации в Службе приложений".

Обработка сообщений от Службы SignalR

Используйте привязку SignalRTrigger для обработки сообщений, отправленных из службы SignalR. Вы можете получать уведомления, когда клиенты отправляют сообщения или когда клиенты подключаются и отключаются.

Для получения дополнительной информации см. справочник по привязке триггеров службы SignalR.

Кроме того, необходимо настроить конечную точку функции как вышестоящую конечную точку, чтобы служба активировала функцию при наличии сообщения от клиента. Дополнительные сведения о настройке конечных точек вышестоящего потока см. в разделе "Конечные точки вышестоящего потока".

Примечание.

Служба SignalR не поддерживает StreamInvocation сообщение от клиента в бессерверном режиме.

Отправка сообщений и управление членством в группах

Используйте выходную привязку SignalR для отправки сообщений клиентам, подключенным к Служба Azure SignalR. Вы можете передавать сообщения всем клиентам или отправлять их в подмножество клиентов. Например, только отправлять сообщения клиентам, прошедшим проверку подлинности с определенным идентификатором пользователя или только в определенную группу.

Пользователи могут быть добавлены в одну или несколько групп. Вы также можете использовать выходную привязку SignalR для добавления или удаления пользователей в группы или из нее.

Для получения дополнительной информации см. справочник по выходному привязочному интерфейсу.

Хабы SignalR

SignalR имеет концепцию хабов. Каждое клиентское подключение и каждое сообщение, отправленное из Функций Azure, ограничены конкретным концентратором. Центры обработки можно использовать в качестве способа разделения подключений и сообщений на логические пространства имен.

Модель на основе классов

Модель на основе классов выделена для C#.

Модель на основе классов обеспечивает более эффективное программирование, которое может заменить входные и выходные привязки SignalR следующими функциями:

  • Более гибкие переговоры, отправка сообщений и управление возможностями групп.
  • Поддерживаются дополнительные функции управления, включая закрытие подключений, проверку наличия подключения, пользователя или группы.
  • Строго типизированный концентратор
  • Единое имя концентратора и строка подключения параметр в одном месте.

В следующем коде показано, как записывать привязки SignalR в модель на основе классов:

Сначала определите хаб, происходящий от класса ServerlessHub:

[SignalRConnection("AzureSignalRConnectionString")]
public class Functions : ServerlessHub
{
    private const string HubName = nameof(Functions); // Used by SignalR trigger only

    public Functions(IServiceProvider serviceProvider) : base(serviceProvider)
    {
    }

    [Function("negotiate")]
    public async Task<HttpResponseData> Negotiate([HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequestData req)
    {
        var negotiateResponse = await NegotiateAsync(new() { UserId = req.Headers.GetValues("userId").FirstOrDefault() });
        var response = req.CreateResponse();
        response.WriteBytes(negotiateResponse.ToArray());
        return response;
    }

    [Function("Broadcast")]
    public Task Broadcast(
    [SignalRTrigger(HubName, "messages", "broadcast", "message")] SignalRInvocationContext invocationContext, string message)
    {
        return Clients.All.SendAsync("newMessage", new NewMessage(invocationContext, message));
    }

    [Function("JoinGroup")]
    public Task JoinGroup([SignalRTrigger(HubName, "messages", "JoinGroup", "connectionId", "groupName")] SignalRInvocationContext invocationContext, string connectionId, string groupName)
    {
        return Groups.AddToGroupAsync(connectionId, groupName);
    }
}

В файле Program.cs зарегистрируйте бессерверный концентратор:

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(b => b.Services
        .AddServerlessHub<Functions>())
    .Build();

Опыт ведения переговоров в модели, основанной на классах

Вместо использования входной привязки SignalR в модели на основе классов согласование может быть более гибким. Базовый класс ServerlessHub имеет метод NegotiateAsync, который позволяет пользователям настраивать параметры согласования, такие как userId, claimsи т. д.

Task<BinaryData> NegotiateAsync(NegotiationOptions? options = null)

Отправка сообщений и управление ими в модели на основе классов

Вы можете отправлять сообщения, управлять группами или управлять клиентами, доступом к членам, предоставляемым базовым классом ServerlessHub.

  • ServerlessHub.Clients для отправки сообщений клиентам.
  • ServerlessHub.Groups для управления подключениями с группами, например добавление подключений к группам, удаление подключений из групп.
  • ServerlessHub.UserGroups для управления пользователями с группами, например добавление пользователей в группы, удаление пользователей из групп.
  • ServerlessHub.ClientManager для проверки существования подключений, закрытия подключений и т. д.

Строго типизированный концентратор

Строго типизированный концентратор позволяет использовать строго типизированные методы при отправке сообщений клиентам. Чтобы использовать строго типизированный концентратор в модели на основе классов, извлеките клиентские методы в интерфейс T, и сделайте класс концентратора производным от ServerlessHub<T>.

Следующий код — это пример интерфейса для клиентских методов.

public interface IChatClient
{
    Task newMessage(NewMessage message);
}

Затем можно использовать строго типизированные методы следующим образом.

Необработанные строки подключения приведены в этой статье только для демонстрационных целей. В рабочих средах всегда защищать ключи доступа. Используйте Azure Key Vault для безопасного управления ключами и защиты строки подключения с помощью идентификатора Microsoft Entra и авторизации доступа с помощью идентификатора Microsoft Entra.

[SignalRConnection("AzureSignalRConnectionString")]
public class Functions : ServerlessHub<IChatClient>
{
    private const string HubName = nameof(Functions);  // Used by SignalR trigger only

    public Functions(IServiceProvider serviceProvider) : base(serviceProvider)
    {
    }

    [Function("Broadcast")]
    public Task Broadcast(
    [SignalRTrigger(HubName, "messages", "broadcast", "message")] SignalRInvocationContext invocationContext, string message)
    {
        return Clients.All.newMessage(new NewMessage(invocationContext, message));
    }
}

Примечание.

Полный пример проекта можно получить на сайте GitHub.

Единое имя хаба и параметр настройки строки подключения в одном месте

  • Имя класса бессерверного концентратора автоматически используется в качестве HubName.
  • Возможно, вы заметили, что атрибут SignalRConnection используется в классах концентраторов без серверов следующим образом.
    [SignalRConnection("AzureSignalRConnectionString")]
    public class Functions : ServerlessHub<IChatClient>
    
    Он позволяет настроить расположение строка подключения для бессерверного концентратора. Если он отсутствует, используется значение AzureSignalRConnectionString по умолчанию.

Внимание

Триггеры SignalR и бессерверные концентраторы независимы. Таким образом, имя класса бессерверного концентратора и SignalRConnection атрибута не изменяет параметры триггеров SignalR, даже если вы используете триггеры SignalR внутри бессерверного концентратора.

Разработка клиентских приложений

Клиентские приложения SignalR могут использовать клиентский пакет SDK SignalR на одном из нескольких языков, чтобы легко подключаться к Служба Azure SignalR и получать сообщения.

Конфигурация подключения клиента

Чтобы подключиться к службе SignalR, клиент должен завершить успешное согласование подключения, которое состоит из следующих действий.

  1. Выполните запрос к конечной точке negotiate HTTP, описанной выше, чтобы получить допустимые сведения о подключении
  2. Подключение к службе SignalR с помощью URL-адреса конечной точки службы и маркера доступа, полученного из negotiate конечной точки.

Клиентские пакеты SDK SignalR уже включают логику, необходимую для выполнения рукопожатия с согласованием. Передайте URL-адрес конечной точки переговоров без сегмента negotiate в компонент SDK HubConnectionBuilder. Ниже приведен пример в JavaScript:

const connection = new signalR.HubConnectionBuilder()
  .withUrl("https://my-signalr-function-app.azurewebsites.net/api")
  .build();

Обычно пакет SDK автоматически присоединяет /negotiate к URL-адресу и использует его для начала согласования.

Примечание.

Если вы используете пакет SDK для JavaScript/TypeScript в браузере, необходимо включить общий доступ к ресурсам независимо от источника (CORS) в приложении-функции Azure.

Дополнительные сведения об использовании клиентского пакета SDK SignalR см. в документации по языку:

Отправка сообщений от клиента к службе

Если вы настроили вышестоящий поток для ресурса SignalR, вы можете отправлять сообщения от клиента в Функции Azure с помощью любого клиента SignalR. Ниже приведен пример в JavaScript:

connection.send("method1", "arg1", "arg2");

Конфигурация Azure Functions

Приложения-функции Azure, интегрированные со Службой Azure SignalR, можно развернуть как любое типичное приложение-функцию Azure с помощью таких методик, как: непрерывное развертывание, развертывание из ZIP-файла и запуск из пакета.

Однако существует несколько особых соображений для приложений, использующих привязки Службы SignalR. Если клиент выполняется в браузере, необходимо включить CORS. Если приложению требуется проверка подлинности, можно интегрировать точку окончания переговоров со службой проверки подлинности App Service.

Включение CORS

Клиент JavaScript/TypeScript отправляет HTTP-запрос в функцию согласования, чтобы инициировать согласование соединения. Если клиентское приложение размещено в другом домене, отличном от приложения-функции Azure, необходимо включить совместное использование ресурсов между источниками (CORS) в приложении-функции или браузер заблокирует запросы.

Localhost

При запуске приложения-функции на локальном компьютере можно добавить раздел Host в local.settings.json, чтобы включить CORS. В разделе Host добавьте два свойства:

  • CORS — введите базовый URL-адрес, который является источником клиентского приложения.
  • CORSCredentials — установите значение true, чтобы разрешить запросы "withCredentials".

Пример:

{
  "IsEncrypted": false,
  "Values": {
    // values
  },
  "Host": {
    "CORS": "http://localhost:8080",
    "CORSCredentials": true
  }
}

Облако - CORS для функций Azure

Чтобы включить CORS в приложении функции Azure, перейдите на страницу конфигурации CORS на вкладке Возможности платформы на портале Azure.

Примечание.

Конфигурацию CORS пока нельзя настроить в плане с оплатой за фактическое использование Linux в Azure Functions. Используйте Управление API Azure, чтобы включить CORS.

CORS с помощью access-Control-Allow-Credentials необходимо включить, чтобы клиент SignalR вызывал функцию согласования. Чтобы включить его, установите флажок.

В разделе Разрешенные источники добавьте запись с базовым URL-адресом источника вашего приложения.

Конфигурация CORS

Управление API Azure из облака

Управление API Azure предоставляет шлюз API, который добавляет возможности к существующим серверным службам. С ее помощью можно добавить CORS к вашему приложению-функции. Служба поставляется на уровне потребления с оплатой за действия и бесплатным ежемесячным лимитом.

Сведения о том, как импортировать приложение-функцию Azure, см. в документации по Управлению API. После импорта можно добавить политику входящего трафика, чтобы включить CORS с поддержкой Access-Control-Allow-Credentials.

<cors allow-credentials="true">
  <allowed-origins>
    <origin>https://azure-samples.github.io</origin>
  </allowed-origins>
  <allowed-methods>
    <method>GET</method>
    <method>POST</method>
  </allowed-methods>
  <allowed-headers>
    <header>*</header>
  </allowed-headers>
  <expose-headers>
    <header>*</header>
  </expose-headers>
</cors>

Настройте клиенты SignalR для использования URL-адреса службы Управления API.

Использование проверки подлинности в службе приложений

Azure Functions имеет встроенную аутентификацию, поддерживая популярных поставщиков, таких как Facebook, X, Аккаунт Microsoft, Google и Microsoft Entra ID. Эту функцию можно интегрировать с SignalRConnectionInfo связыванием для создания подключений к службе Azure SignalR, прошедших проверку подлинности по идентификатору пользователя. Приложение может отправлять сообщения, используя выходную привязку SignalR, которая нацелена на этот идентификатор пользователя.

На портале Azure на вкладке Возможности приложения-функции откройте окно параметров Проверка подлинности и авторизация. Чтобы настроить аутентификацию с помощью выбранного поставщика удостоверений, следуйте документации по аутентификации Службы приложений.

После настройки прошедшие проверку подлинности HTTP-запросы включают x-ms-client-principal-name и x-ms-client-principal-id заголовки, содержащие имя пользователя и идентификатор пользователя, прошедшие проверку подлинности, соответственно.

Эти заголовки можно использовать в SignalRConnectionInfo конфигурации привязки для создания прошедших проверку подлинности подключений. Ниже приведен пример функции согласования C#, которая использует x-ms-client-principal-id заголовок.

[FunctionName("negotiate")]
public static SignalRConnectionInfo Negotiate(
    [HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest req,
    [SignalRConnectionInfo
        (HubName = "chat", UserId = "{headers.x-ms-client-principal-id}")]
        SignalRConnectionInfo connectionInfo)
{
    // connectionInfo contains an access key token with a name identifier claim set to the authenticated user
    return connectionInfo;
}

Затем можно отправлять сообщения этому пользователю, задав свойство UserId сообщения SignalR.

[FunctionName("SendMessage")]
public static Task SendMessage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post")]object message,
    [SignalR(HubName = "chat")]IAsyncCollector<SignalRMessage> signalRMessages)
{
    return signalRMessages.AddAsync(
        new SignalRMessage
        {
            // the message will only be sent to these user IDs
            UserId = "userId1",
            Target = "newMessage",
            Arguments = new [] { message }
        });
}

Для получения информации о других языках обратитесь к справочнику по привязкам службы Azure SignalR для Azure Functions.

Следующие шаги

В этой статье вы узнаете, как разрабатывать и настраивать бессерверные приложения Служба SignalR с помощью Функции Azure. Попробуйте создать приложение самостоятельно с помощью одного из быстрых запусков или руководств на странице обзора Службы SignalR.