Поделиться через


Разработка расширений рабочих ролей Python для функций Azure

Замечание

Начиная с Python 3.13, расширения для рабочих процессов Python больше не будут поддерживаться.

Функции Azure позволяют интегрировать пользовательские поведения в рамках выполнения функции Python. Эта функция позволяет создавать бизнес-логику, которую клиенты могут легко использовать в своих приложениях-функциях. Расширения рабочих ролей поддерживаются как в моделях программирования Python версии 1, так и в версии 2.

В этом руководстве описано, как:

  • Создайте расширение рабочей роли Python на уровне приложения для функций Azure.
  • Используйте расширение в приложении так, как это делают ваши клиенты.
  • Упакуйте и опубликуйте расширение для использования.

Предпосылки

Перед началом работы необходимо выполнить следующие требования:

Создайте расширение обработчика Python

Создаваемое расширение сообщает время вызова триггера HTTP в журналах консоли и в тексте ответа HTTP.

Структура папок

Папка для проекта расширения должна быть похожа на следующую структуру:

<python_worker_extension_root>/
 | - .venv/
 | - python_worker_extension_timer/
 | | - __init__.py
 | - setup.py
 | - readme.md
Папка или файл Description
.venv/ (Необязательно) Содержит виртуальную среду Python, используемую для локальной разработки.
python_worker_extension/ Содержит исходный код модуля воркера Python. Эта папка содержит основной модуль Python для публикации в PyPI.
setup.py Содержит метаданные пакета расширения рабочего процесса Python.
readme.md Содержит инструкции по применению вашего расширения. Это содержимое отображается как описание на домашней странице проекта PyPI.

Настройка метаданных проекта

Сначала создайте setup.py, предоставив важные сведения о вашем пакете. Чтобы убедиться, что ваше расширение правильно доступно и интегрировано в функции приложений клиента, убедитесь, что 'azure-functions >= 1.7.0, < 2.0.0' находится в разделе install_requires.

В следующем шаблоне необходимо изменить author, author_email, install_requires, licensepackagesи url поля по мере необходимости.

from setuptools import find_packages, setup
setup(
    name='python-worker-extension-timer',
    version='1.0.0',
    author='Your Name Here',
    author_email='your@email.here',
    classifiers=[
        'Intended Audience :: End Users/Desktop',
        'Development Status :: 5 - Production/Stable',
        'Intended Audience :: End Users/Desktop',
        'License :: OSI Approved :: Apache Software License',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3.7',
        'Programming Language :: Python :: 3.8',
        'Programming Language :: Python :: 3.9',
        'Programming Language :: Python :: 3.10',
    ],
    description='Python Worker Extension Demo',
    include_package_data=True,
    long_description=open('readme.md').read(),
    install_requires=[
        'azure-functions >= 1.7.0, < 2.0.0',
        # Any additional packages that will be used in your extension
    ],
    extras_require={},
    license='MIT',
    packages=find_packages(where='.'),
    url='https://your-github-or-pypi-link',
    zip_safe=False,
)

Затем вы реализуете код расширения в области приложения.

Реализация расширения таймера

Добавьте следующий код в python_worker_extension_timer/__init__.py, чтобы внедрить расширение на уровне приложения:

import typing
from logging import Logger
from time import time
from azure.functions import AppExtensionBase, Context, HttpResponse
class TimerExtension(AppExtensionBase):
    """A Python worker extension to record elapsed time in a function invocation
    """

    @classmethod
    def init(cls):
        # This records the starttime of each function
        cls.start_timestamps: typing.Dict[str, float] = {}

    @classmethod
    def configure(cls, *args, append_to_http_response:bool=False, **kwargs):
        # Customer can use TimerExtension.configure(append_to_http_response=)
        # to decide whether the elapsed time should be shown in HTTP response
        cls.append_to_http_response = append_to_http_response

    @classmethod
    def pre_invocation_app_level(
        cls, logger: Logger, context: Context,
        func_args: typing.Dict[str, object],
        *args, **kwargs
    ) -> None:
        logger.info(f'Recording start time of {context.function_name}')
        cls.start_timestamps[context.invocation_id] = time()

    @classmethod
    def post_invocation_app_level(
        cls, logger: Logger, context: Context,
        func_args: typing.Dict[str, object],
        func_ret: typing.Optional[object],
        *args, **kwargs
    ) -> None:
        if context.invocation_id in cls.start_timestamps:
            # Get the start_time of the invocation
            start_time: float = cls.start_timestamps.pop(context.invocation_id)
            end_time: float = time()
            # Calculate the elapsed time
            elapsed_time = end_time - start_time
            logger.info(f'Time taken to execute {context.function_name} is {elapsed_time} sec')
            # Append the elapsed time to the end of HTTP response
            # if the append_to_http_response is set to True
            if cls.append_to_http_response and isinstance(func_ret, HttpResponse):
                func_ret._HttpResponse__body += f' (TimeElapsed: {elapsed_time} sec)'.encode()

Этот код наследует от AppExtensionBase , чтобы расширение применялось ко каждой функции в приложении. Можно также реализовать расширение на уровне функции, наследуя от FuncExtensionBase.

Метод init — это метод класса, который вызывается рабочим процессом при импорте класса расширения. Здесь можно выполнить действия инициализации для расширения. В этом случае хэш-карта инициализируется для записи времени начала вызова для каждой функции.

Этот configure метод ориентирован на клиента. В файле readme вы можете сообщить клиентам, когда им нужно позвонить Extension.configure(). Файл README также должен документировать функциональные возможности, возможную конфигурацию и использование расширения. В этом примере заказчики могут выбрать, сообщается ли истекшее время в HttpResponse.

Метод pre_invocation_app_level вызывается рабочим процессом Python перед выполнением функции. Он предоставляет сведения из функции, например контекст функции и аргументы. В этом примере расширение регистрирует сообщение и записывает время начала вызова, основываясь на его идентификаторе вызова (invocation_id).

Аналогичным образом post_invocation_app_level вызывается после выполнения функции. В этом примере вычисляется истекшее время на основе времени начала и текущего времени. Он также перезаписывает возвращаемое значение ОТВЕТА HTTP.

Создайте readme.md

Создайте файл readme.md в корне проекта расширения. Этот файл содержит инструкции и использование расширения. Содержимое readme.md отображается в качестве описания на домашней странице проекта PyPI.

# Python Worker Extension Timer

In this file, tell your customers when they need to call `Extension.configure()`.

The readme should also document the extension capabilities, possible configuration,
and usage of your extension.

Используйте расширение локально

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

Создание функции для триггеров HTTP

  1. Создайте папку для проекта приложения и перейдите к ней.

  2. В соответствующей оболочке, например Bash, выполните следующую команду, чтобы инициализировать проект:

    func init --python
    
  3. Используйте следующую команду, чтобы создать новую функцию триггера HTTP, которая разрешает анонимный доступ:

    func new -t HttpTrigger -n HttpTrigger -a anonymous
    

Активация виртуальной среды

  1. Создайте виртуальную среду Python на основе ОС следующим образом:

    python3 -m venv .venv
    
  2. Активируйте виртуальную среду Python на основе ОС следующим образом:

    source .venv/bin/activate
    

Настройка расширения

  1. Установите удаленные пакеты для проекта приложения-функции с помощью следующей команды:

    pip install -r requirements.txt
    
  2. Установите расширение из локального пути к файлу в редактируемом режиме следующим образом:

    pip install -e <PYTHON_WORKER_EXTENSION_ROOT>
    

    В этом примере замените <PYTHON_WORKER_EXTENSION_ROOT> корневым файлом проекта расширения.

    Когда пользователь использует ваше расширение, он вместо этого добавит расположение пакета расширения в файл requirements.txt, например:

    # requirements.txt
    python_worker_extension_timer==1.0.0
    
  3. Откройте файл проекта local.settings.json и добавьте в следующее поле Values:

    "PYTHON_ENABLE_WORKER_EXTENSIONS": "1" 
    

    При запуске в Azure вместо этого добавьте PYTHON_ENABLE_WORKER_EXTENSIONS=1 в настройки приложения для функции.

  4. Добавьте следующие две строки перед main функцией в файле __init.py__ для модели программирования версии 1 или в файле function_app.py для модели программирования версии 2:

    from python_worker_extension_timer import TimerExtension
    TimerExtension.configure(append_to_http_response=True)
    

    Этот код импортирует TimerExtension модуль и задает append_to_http_response значение конфигурации.

Проверка расширения

  1. Из корневого каталога вашего приложения запустите хост функции, используя func host start --verbose. В выходных данных должна отображаться локальная точка входа вашей функции как https://localhost:7071/api/HttpTrigger.

  2. В браузере отправьте запрос типа GET на https://localhost:7071/api/HttpTrigger. Вы увидите ответ, как показано ниже, с данными TimeElapsed для добавленного запроса.

    This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response. (TimeElapsed: 0.0009996891021728516 sec)
    

Опубликуйте ваше расширение

После создания и проверки расширения необходимо выполнить следующие оставшиеся задачи публикации:

  • Выберите лицензию.
  • Создайте readme.md и другую документацию.
  • Опубликуйте библиотеку расширений в реестре пакетов Python или системе управления версиями (VCS).

Чтобы опубликовать расширение в PyPI, выполните приведенные действия.

  1. Выполните следующую команду, чтобы установить twine и wheel в среде Python по умолчанию или в виртуальной среде:

    pip install twine wheel
    
  2. Удалите старую dist/ папку из репозитория расширений.

  3. Выполните следующую команду, чтобы создать новый пакет внутри dist/:

    python setup.py sdist bdist_wheel
    
  4. Выполните следующую команду, чтобы отправить пакет в PyPI:

    twine upload dist/*
    

    Во время отправки может потребоваться указать учетные данные учетной записи PyPI. Вы также можете протестировать отправку пакета с помощью twine upload -r testpypi dist/*. Дополнительные сведения см. в документации Twine.

После выполнения этих действий клиенты могут использовать расширение, включив имя пакета в requirements.txt.

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

Примеры

  • Вы можете просмотреть завершенный пример проекта расширения из этой статьи в репозитории python_worker_extension_timer.

  • Интеграция OpenCensus — это проект с открытым исходным кодом, использующий интерфейс расширения для интеграции трассировки телеметрии в приложениях Python для функций Azure. Ознакомьтесь с репозиторием opencensus-python-extensions-azure , чтобы просмотреть реализацию этого рабочего расширения Python.

Дальнейшие шаги

Дополнительные сведения о разработках под Python в рамках службы "Функции Azure" см. в статьях ниже.