Files
spring-cloud-function/docs/modules/ROOT/pages/adapters/azure-intro.adoc

583 lines
21 KiB
Plaintext

[[microsoft-azure-functions]]
= Microsoft Azure Functions
https://azure.microsoft.com[Azure] function adapter for deploying `Spring Cloud Function` applications as native Azure Java Functions.
The `Azure Functions` https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java[programming model] relays, extensively, on Java https://learn.microsoft.com/en-us/java/api/com.microsoft.azure.functions.annotation?view=azure-java-stable[annotations] for defining the function's handler methods and their input and output types.
At compile time the annotated classes are processed by the provided Azure Maven/Gradle plugins to generate the necessary Azure Function binding files, configurations and package artifacts.
The Azure annotations are just a type-safe way to configure your java function to be recognized as Azure function.
The https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[spring-cloud-function-adapter-azure] extends the basic programming model to provide Spring and Spring Cloud Function support.
With the adapter you can build your Spring Cloud Function application using dependency injections and then auto-wire the necessary services into your Azure handler methods.
image::{github-raw}/docs/src/main/asciidoc/images/scf-azure-adapter.svg[width=800,scaledwidth="75%",align="center"]
TIP: For Web-based function applications, you can replace the generic `adapter-azure` with the specialized https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web[spring-cloud-function-adapter-azure-web].
With the Azure Web Adapter you can deploy any Spring Web application as an Azure, HttpTrigger, function.
This adapter hides the Azure annotations complexity and uses the familiar https://docs.spring.io/spring-boot/docs/current/reference/html/web.html[Spring Web] programming model instead.
For further information follow the xref:adapters/azure-intro.adoc#azure.web.adapter[Azure Web Adapter] section below.
[[azure-adapter]]
== Azure Adapter
Provides `Spring` & `Spring Cloud Function` integration for Azure Functions.
[[dependencies]]
=== Dependencies
In order to enable the Azure Function integration add the azure adapter dependency to your `pom.xml` or `build.gradle`
files:
[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-azure</artifactId>
</dependency>
</dependencies>
----
Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
dependencies {
implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure'
}
----
======
NOTE: version `4.0.0+` is required. Having the adapter on the classpath activates the Azure Java Worker integration.
[[azure.development.guidelines]]
=== Development Guidelines
Use the `@Component` (or `@Service`) annotation to turn any exiting Azure Function class (e.g. with `@FunctionName` handlers) into a Spring component.
Then you can auto-wire the required dependencies (or the xref:spring-cloud-function/programming-model.adoc#function.catalog[Function Catalog] for Spring Cloud Function composition) and use those inside the Azure function handlers.
[source,java]
----
@Component // <1>
public class MyAzureFunction {
// Plain Spring bean - not a Spring Cloud Functions!
@Autowired private Function<String, String> uppercase; // <2>
// The FunctionCatalog leverages the Spring Cloud Function framework.
@Autowired private FunctionCatalog functionCatalog; // <2>
@FunctionName("spring") // <3>
public String plainBean( // <4>
@HttpTrigger(name = "req",
methods = { HttpMethod.POST },
authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
ExecutionContext context) {
return this.uppercase.apply(request.getBody().get());
}
@FunctionName("scf") // <3>
public String springCloudFunction( // <5>
@HttpTrigger(name = "req",
methods = { HttpMethod.POST },
authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
ExecutionContext context) {
// Use SCF composition. Composed functions are not just spring beans but SCF such.
Function composed = this.functionCatalog.lookup("reverse|uppercase"); // <6>
return (String) composed.apply(request.getBody().get());
}
}
----
<1> Indicates that the `MyAzureFunction` class is a "component" to be considered by the Spring Framework as a candidate for auto-detection and classpath scanning.
<2> Auto-wire the `uppercase` and `functionCatalog` beans defined in the `HttpTriggerDemoApplication` (below).
<3> The https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#java-function-basics[@FunctionName] annotation identifies the designated Azure function handlers.
When invoked by a trigger (such as `@HttpTrigger`), functions process that trigger, and any other inputs, to produce one or more outputs.
<4> The `plainBean` method handler is mapped to an Azure function that uses of the auto-wired `uppercase` spring bean to compute the result.
It demonstrates how to use "plain" Spring components in your Azure handlers.
<5> The `springCloudFunction` method handler is mapped to another Azure function, that uses the auto-wired `FunctionCatalog` instance to compute the result.
<6> Shows how to leverage the Spring Cloud Function xref:spring-cloud-function/programming-model.adoc#function.catalog[Function Catalog] composition API.
TIP: Use the Java annotations included in the https://learn.microsoft.com/en-us/java/api/com.microsoft.azure.functions.annotation?view=azure-java-stable[com.microsoft.azure.functions.annotation.*] package to bind input and outputs to your methods.
The implementation of the business logic used inside the Azure handlers looks like a common Spring application:
[[HttpTriggerDemoApplication]]
[source,java]
----
@SpringBootApplication // <1>
public class HttpTriggerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(HttpTriggerDemoApplication.class, args);
}
@Bean
public Function<String, String> uppercase() { // <2>
return payload -> payload.toUpperCase();
}
@Bean
public Function<String, String> reverse() { // <2>
return payload -> new StringBuilder(payload).reverse().toString();
}
}
----
<1> The `@SpringBootApplication` annotated class is used as a `Main-Class` as explained in xref:adapters/azure-intro.adoc#star-class-configuration[main class configuration].
<2> Functions auto-wired and used in the Azure function handlers.
[[function-catalog]]
=== Function Catalog
The Spring Cloud Function supports a range of type signatures for user-defined functions, while providing a consistent execution model.
For this it uses the xref:spring-cloud-function/programming-model.adoc#function.catalog[Function Catalog] to transform all user defined functions into a canonical representation.
The Azure adapter can auto-wire any Spring component, such as the `uppercase` above.
But those are treated as plain Java class instances, not as a canonical Spring Cloud Functions!
To leverage Spring Cloud Function and have access to the canonical function representations, you need to auto-wire the `FunctionCatalog` and use it in your handler, like the `functionCatalog` instance the `springCloudFunction()` handler above.
[[accessing-azure-executioncontext]]
=== Accessing Azure ExecutionContext
Some time there is a need to access the target execution context provided by the Azure runtime in the form of `com.microsoft.azure.functions.ExecutionContext`.
For example one of such needs is logging, so it can appear in the Azure console.
For that purpose the `AzureFunctionUtil.enhanceInputIfNecessary` allow you to add an instance of the `ExecutionContext` as a Message header so you can retrieve it via `executionContext` key.
[source,java]
----
@FunctionName("myfunction")
public String execute(
@HttpTrigger(name = "req",
methods = { HttpMethod.POST },
authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
ExecutionContext context) {
Message message =
(Message) AzureFunctionUtil.enhanceInputIfNecessary(request.getBody().get(), context); // <1>
return this.uppercase.apply(message);
}
----
<1> Leverages the `AzureFunctionUtil` utility to inline the `context` as message header using the `AzureFunctionUtil.EXECUTION_CONTEXT` header key.
Now you can retrieve the ExecutionContext from message headers:
[source,java]
----
@Bean
public Function<Message<String>, String> uppercase(JsonMapper mapper) {
return message -> {
String value = message.getPayload();
ExecutionContext context =
(ExecutionContext) message.getHeaders().get(AzureFunctionUtil.EXECUTION_CONTEXT); // <1>
. . .
}
}
----
<1> Retrieve the ExecutionContext instance from the header.
[[azure.configuration]]
== Configuration
To run your function applications on Microsoft Azure, you have to provide the necessary configurations, such as `function.json` and `host.json`, and adhere to the compulsory https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#folder-structure[packaging format].
Usually the Azure Maven (or Gradle) plugins are used to generate the necessary configurations from the annotated classes and to produce the required package format.
IMPORTANT: The Azure https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#folder-structure[packaging format] is not compatible with the default Spring Boot packaging (e.g. `uber jar`).
The xref:adapters/azure-intro.adoc#disable.spring.boot.plugin[Disable Spring Boot Plugin] section below explains how to handle this.
[[azure-maven/gradle-plugins]]
=== Azure Maven/Gradle Plugins
Azure provides https://github.com/microsoft/azure-maven-plugins/tree/develop/azure-functions-maven-plugin[Maven] and https://github.com/microsoft/azure-gradle-plugins/tree/master/azure-functions-gradle-plugin[Gradle] plugins to process the annotated classes, generate the necessary configurations and produce the expected package layout.
Plugins are used to set the platform, runtime and app-settings properties like this:
[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
<plugin>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-functions-maven-plugin</artifactId>
<version>1.22.0 or higher</version>
<configuration>
<appName>YOUR-AZURE-FUNCTION-APP-NAME</appName>
<resourceGroup>YOUR-AZURE-FUNCTION-RESOURCE-GROUP</resourceGroup>
<region>YOUR-AZURE-FUNCTION-APP-REGION</region>
<appServicePlanName>YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME</appServicePlanName>
<pricingTier>YOUR-AZURE-FUNCTION-PRICING-TIER</pricingTier>
<hostJson>${project.basedir}/src/main/resources/host.json</hostJson>
<runtime>
<os>linux</os>
<javaVersion>11</javaVersion>
</runtime>
<appSettings>
<property>
<name>FUNCTIONS_EXTENSION_VERSION</name>
<value>~4</value>
</property>
</appSettings>
</configuration>
<executions>
<execution>
<id>package-functions</id>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
----
Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
plugins {
id "com.microsoft.azure.azurefunctions" version "1.11.0"
// ...
}
apply plugin: "com.microsoft.azure.azurefunctions"
azurefunctions {
appName = 'YOUR-AZURE-FUNCTION-APP-NAME'
resourceGroup = 'YOUR-AZURE-FUNCTION-RESOURCE-GROUP'
region = 'YOUR-AZURE-FUNCTION-APP-REGION'
appServicePlanName = 'YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME'
pricingTier = 'YOUR-AZURE-FUNCTION-APP-SERVICE-PLANE-NAME'
runtime {
os = 'linux'
javaVersion = '11'
}
auth {
type = 'azure_cli'
}
appSettings {
FUNCTIONS_EXTENSION_VERSION = '~4'
}
// Uncomment to enable local debug
// localDebug = "transport=dt_socket,server=y,suspend=n,address=5005"
}
----
======
More information about the runtime configurations: https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#java-versions[Java Versions], https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption#specify-the-deployment-os[Deployment OS].
[[disable.spring.boot.plugin]]
=== Disable Spring Boot Plugin
Expectedly, the Azure Functions run inside the Azure execution runtime, not inside the SpringBoot runtime!
Furthermore, Azure expects a specific packaging format, generated by the Azure Maven/Gradle plugins, that is not compatible with the default Spring Boot packaging.
You have to either disable the SpringBoot Maven/Gradle plugin or use the https://github.com/dsyer/spring-boot-thin-launcher[Spring Boot Thin Launcher] as shown in this Maven snippet:
[source,xml]
----
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot.experimental</groupId>
<artifactId>spring-boot-thin-layout</artifactId>
</dependency>
</dependencies>
</plugin>
----
[[star-class-configuration]]
=== Main-Class Configuration
Specify the `Main-Class`/`Start-Class` to point to your Spring application entry point, such as the xref:adapters/azure-intro.adoc#HttpTriggerDemoApplication[HttpTriggerDemoApplication] class in the example above.
You can use the Maven `start-class` property or set the `Main-Class` attribute of your `MANIFEST/META-INFO`:
[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
<properties>
<start-class>YOUR APP MAIN CLASS</start-class>
...
</properties>
----
Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
jar {
manifest {
attributes(
"Main-Class": "YOUR-APP-MAIN-CLASS"
)
}
}
----
======
TIP: Alternatively you can use the `MAIN_CLASS` environment variable to set the class name explicitly.
For local runs, add the `MAIN_CLASS` variable to your `local.settings.json` file and for Azure portal deployment set the variable in the https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings?tabs=portal#get-started-in-the-azure-portal[App Settings].
IMPORTANT: If the `MAIN_CLASS` variable is not set, the Azure adapter lookups the `MANIFEST/META-INFO` attributes from the jars found on the classpath and selects the first `Main-Class:` annotated with either a `@SpringBootApplication` or `@SpringBootConfiguration` annotation.
[[metadata-configuration]]
=== Metadata Configuration
You can use a shared https://learn.microsoft.com/en-us/azure/azure-functions/functions-host-json[host.json] file to configure the function app.
[source,json]
----
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[4.*, 5.0.0)"
}
}
----
The host.json metadata file contains configuration options that affect all functions in a function app instance.
TIP: If the file is not in the project top folder you need to configure your plugins accordingly (like `hostJson` maven attribute).
[[samples]]
== Samples
Here is a list of various Spring Cloud Function Azure Adapter samples you can explore:
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-http-trigger[Http Trigger (Maven)]
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle[Http Trigger (Gradle)]
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-blob-trigger[Blob Trigger (Maven)]
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-timer-trigger[Timer Trigger (Maven)]
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-kafka-trigger[ Kafka Trigger & Output Binding (Maven)].
[[azure.web.adapter]]
== Azure Web Adapter
For, pure, Web-based function applications, you can replace the generic `adapter-azure` with the specialized https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web[spring-cloud-function-adapter-azure-web].
The Azure Web Adapter can deploy any Spring Web application as a native Azure function, using the HttpTrigger internally.
It hides the Azure annotations complexity and relies on the familiar https://docs.spring.io/spring-boot/docs/current/reference/html/web.html[Spring Web] programming model instead.
To enable the Azure Web Adapter, add the adapter dependency to your `pom.xml` or `build.gradle` files:
[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-adapter-azure-web</artifactId>
</dependency>
</dependencies>
----
Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
dependencies {
implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure-web'
}
----
======
The same xref:adapters/azure-intro.adoc#azure.configuration[Configuration] and xref:adapters/azure-intro.adoc#azure.usage[Usage] instructions apply to the `Azure Web Adapter` as well.
== Azure Samples
For further information, explore the following, Azure Web Adapter, sample:
- https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure-web[ Azure Web Adapter (Maven)].
[[azure.usage]]
== Usage
Common instructions for building and deploying both, `Azure Adapter` and `Azure Web Adapter` type of applications.
[[build]]
== Build
[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
./mvnw -U clean package
----
Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
./gradlew azureFunctionsPackage
----
======
[[running-locally]]
== Running locally
To run locally on top of `Azure Functions`, and to deploy to your live Azure environment, you will need `Azure Functions Core Tools` installed along with the Azure CLI (see https://docs.microsoft.com/en-us/azure/azure-functions/create-first-function-cli-java?tabs=bash%2Cazure-cli%2Cbrowser#configure-your-local-environment[here]).
For some configuration you would need the https://learn.microsoft.com/en-us/azure/storage/common/storage-use-emulator[Azurite emulator] as well.
Then run the sample:
[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
./mvnw azure-functions:run
----
Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
./gradlew azureFunctionsRun
----
======
[[running-on-azure]]
== Running on Azure
Make sure you are logged in your Azure account.
----
az login
----
and deploy
[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
./mvnw azure-functions:deploy
----
Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
./gradlew azureFunctionsDeploy
----
======
[[debug-locally]]
== Debug locally
Run the function in debug mode.
[tabs]
======
Maven::
+
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
----
./mvnw azure-functions:run -DenableDebug
----
Gradle::
+
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
----
// If you want to debug your functions, please add the following line
// to the azurefunctions section of your build.gradle.
azurefunctions {
...
localDebug = "transport=dt_socket,server=y,suspend=n,address=5005"
}
----
======
Alternatively and the `JAVA_OPTS` value to your `local.settings.json` like this:
[source,json]
----
{
"IsEncrypted": false,
"Values": {
...
"FUNCTIONS_WORKER_RUNTIME": "java",
"JAVA_OPTS": "-Djava.net.preferIPv4Stack=true -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=127.0.0.1:5005"
}
}
----
Here is snippet for a `VSCode` remote debugging configuration:
[source,json]
----
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Attach to Remote Program",
"request": "attach",
"hostName": "localhost",
"port": "5005"
},
]
}
----
[[functioninvoker-deprecated]]
== FunctionInvoker (deprecated)
WARNING: The legacy `FunctionInvoker` programming model is deprecated and will not be supported going forward.
For additional documentation and samples about the Function Integration approach follow the https://github.com/spring-cloud/spring-cloud-function/tree/main/spring-cloud-function-samples/function-sample-azure/[azure-sample] README and code.
[[relevant-links]]
== Relevant Links
- https://learn.microsoft.com/en-us/azure/developer/java/spring-framework/getting-started-with-spring-cloud-function-in-azure[Spring Cloud Function in Azure]
- https://spring.io/blog/2023/02/24/spring-cloud-function-for-azure-function[Spring Cloud Function for Azure Function (blog)]
- <<spring-cloud-function.adoc#,Spring Cloud Function - Reference Guide>>
- https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-java?tabs=bash%2Cconsumption[Azure Functions Java developer guide]
- https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference?tabs=blob[Azure Functions developer guide]
:sectnums!: