Java >> Java Tutorial >  >> Tag >> Spring

Beispiel für Spring Cloud Function mit AWS Lambda

In diesem Beitrag lernen wir Spring Cloud Function kennen und stellen ein Beispiel für Spring Cloud Function auf AWS Lambda bereit. Am Ende dieses Beitrags werden wir mehr Verständnis für serverlose Funktionen haben. Wenn Sie mehr über die serverlose Architektur erfahren möchten, hilft Ihnen dieser Beitrag beim Einstieg.

Was ist Spring Cloud Function?

Spring Cloud Function ist eine der Funktionen von Spring Cloud. Es ermöglicht Entwicklern, Cloud-agnostische Funktionen mit Spring-Features zu schreiben. Diese Funktionen können eigenständige Klassen sein und können problemlos auf jeder Cloud-Plattform bereitgestellt werden, um ein serverloses Framework zu erstellen. Spring Cloud bietet eine Bibliothek spring-cloud-starter-function-web ermöglicht das Erstellen von Funktionen mit Spring-Features und bringt alle notwendigen Abhängigkeiten mit.

Warum die Spring Cloud-Funktion verwenden?

Diese Frage ist eher, wann die Spring Cloud-Funktion verwendet werden soll. Grundsätzlich ermöglicht die Spring Cloud Function-Bibliothek die Erstellung funktionaler Anwendungen, die einfach auf AWS Lambda bereitgestellt werden können. Diese Funktionen folgen dem Java 8-Muster von Supplier , Consumer , und Function .

spring-cloud-starter-function-web Die Bibliothek bietet eine native Interaktion für die Bearbeitung von Anfragen und Streams.

Funktionen der Spring Cloud-Funktion

Der Hauptvorteil von Spring Cloud Function besteht darin, dass es alle Funktionen von Spring Boot wie autoconfiguration bietet , dependency injection . Aber es gibt noch mehr Funktionen:

  • Transparente Typumwandlung von Ein- und Ausgabe
  • POJO-Funktionen
  • REST-Unterstützung, um Funktionen als HTTP-Endpunkte bereitzustellen
  • Streaming von Daten zu/von Funktionen über das Spring Cloud Stream-Framework
  • Bereitstellen von Funktionen als isolierte JAR-Dateien
  • Adapter für AWS Lambda, Google Cloud Platform, Microsoft Azure

Demo

Als Teil dieses Beitrags werden wir Spring Cloud Function erstellen und in AWS Lambda bereitstellen. Sobald wir eine reguläre Spring-Boot-Anwendung erstellt haben, fügen Sie Ihrer Gradle-Datei die folgenden Abhängigkeiten hinzu:

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.cloud:spring-cloud-function-adapter-aws:3.2.1'
	implementation "com.amazonaws:aws-lambda-java-events:${awsLambdaEventsVersion}"
	implementation "com.amazonaws:aws-lambda-java-core:${awsLambdaCoreVersion}"
	runtimeOnly 'com.h2database:h2'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Beachten Sie die Abhängigkeit spring-cloud-function-adapter-aws ermöglicht uns die Integration von Spring Cloud Function mit AWS Lambda.

Eine Hauptklasse für die Anwendung sieht wie folgt aus:

package com.betterjavacode.springcloudfunctiondemo;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.function.context.FunctionalSpringApplication;

@SpringBootApplication
public class SpringcloudfunctiondemoApplication {

	public static void main(String[] args) {
		FunctionalSpringApplication.run(SpringcloudfunctiondemoApplication.class, args);
	}

}

Vergleichen Sie dies mit einer normalen Spring Boot-Anwendung, es gibt einen Unterschied. Wir verwenden FunctionalSpringApplication als Einstiegspunkt. Dies ist ein funktionaler Ansatz zum Schreiben von Beans und hilft bei der Startzeit.

Jetzt können wir drei Arten von Funktionen schreiben Function , Consumer ODER Supplier . Wir werden sehen, was jede Funktion macht und wie wir sie als Teil dieser Demo verwenden können.

Lassen Sie uns außerdem eine POJO-Modellklasse Customer erstellen .

package com.betterjavacode.springcloudfunctiondemo.models;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name= "customer")
public class Customer
{
    @Id
    @GeneratedValue(generator = "UUID")
    private Long id;

    private String name;

    private int customerIdentifier;

    private String email;

    private String contactPerson;

    public Customer(String name, int customerIdentifier, String email, String contactPerson)
    {
        this.name = name;
        this.customerIdentifier = customerIdentifier;
        this.email = email;
        this.contactPerson = contactPerson;
    }

    public String getName ()
    {
        return name;
    }

    public void setName (String name)
    {
        this.name = name;
    }

    public int getCustomerIdentifier ()
    {
        return customerIdentifier;
    }

    public void setCustomerIdentifier (int customerIdentifier)
    {
        this.customerIdentifier = customerIdentifier;
    }

    public String getEmail ()
    {
        return email;
    }

    public void setEmail (String email)
    {
        this.email = email;
    }

    public String getContactPerson ()
    {
        return contactPerson;
    }

    public void setContactPerson (String contactPerson)
    {
        this.contactPerson = contactPerson;
    }

    public Long getId ()
    {
        return id;
    }

    public void setId (Long id)
    {
        this.id = id;
    }
}

Sicherlich wird unsere Frühlings-Cloud-Funktion einige Geschäftslogik in Bezug auf dieses Modell ausführen Customer .

Verbraucherfunktion

Lassen Sie uns einen Consumer erstellen Funktion. Consumer Die Funktion nimmt normalerweise eine Eingabe und führt eine Geschäftslogik aus, die einen Nebeneffekt auf die Daten hat. Es wird keine Ausgabe erzeugt. Es ist also eher ein void Methode.

Für unsere Demo sieht es wie folgt aus:

package com.betterjavacode.springcloudfunctiondemo.functions;

import com.betterjavacode.springcloudfunctiondemo.models.Customer;
import com.betterjavacode.springcloudfunctiondemo.repositories.CustomerRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.function.Consumer;

@Component
public class CustomerConsumer implements Consumer<Map<String, String>>
{
    public static final Logger LOGGER = LoggerFactory.getLogger(CustomerConsumer.class);

    @Autowired
    private CustomerRepository customerRepository;

    @Override
    public void accept (Map<String, String> map)
    {
        LOGGER.info("Creating the customer", map);
        Customer customer = new Customer(map.get("name"), Integer.parseInt(map.get(
                "customerIdentifier")), map.get("email"), map.get("contactPerson"));
        customerRepository.save(customer);
    }

}

Dieser CustomerConsumer Funktion implementiert Consumer Funktionstyp und akzeptiert eine Eingabe vom Typ Map<String, String> . Als Teil des Schnittstellenvertrags muss die Methode accept implementiert werden . Diese Methode benötigt map Geschäftslogik eingeben und ausführen. Eine Sache, die Sie verstehen sollten, ist, dass Spring Cloud Function die Typkonvertierung aus dem Roheingabestrom und den von der Funktion deklarierten Typen übernimmt. Wenn die Funktion keine Typinformationen ableiten kann, wird sie in einen generischen Typ von map konvertiert .

Diese Funktion nimmt eine Karte des DTO-Objekts für den Kunden und speichert sie in der Datenbank. Für die Datenbank verwenden wir die In-Memory-Datenbank von H2. Man kann immer mehr Geschäftslogik hinzufügen, aber zu Demonstrationszwecken zeigen wir ein einfaches Beispiel.

Lieferantenfunktion

Die Lieferantenfunktion verhält sich wie ein GET-Endpunkt. Diese Funktion nimmt keine Eingabe entgegen, sondern gibt Daten zurück.

package com.betterjavacode.springcloudfunctiondemo.functions;

import com.betterjavacode.springcloudfunctiondemo.models.Customer;
import com.betterjavacode.springcloudfunctiondemo.repositories.CustomerRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.function.Supplier;

@Component
public class CustomerSupplier implements Supplier
{
    public static final Logger LOGGER = LoggerFactory.getLogger(CustomerSupplier.class);

    @Autowired
    private CustomerRepository customerRepository;

    @Override
    public Customer get ()
    {
        List customers = customerRepository.findAll();
        LOGGER.info("Getting the customer of our choice - ", customers);
        return customers.get(0);
    }
}

Konfigurieren der Spring Cloud-Funktion mit AWS Lambda

Ein AWS Lambda führt nur eine Funktion aus. Wenn es mehrere Spring Cloud Function-Beans gibt, kann man konfigurieren, welche Funktion über ein Lambda ausgeführt werden soll. Fügen Sie die Eigenschaft in application.properties hinzu wie folgt:

spring.cloud.function.definition=customerConsumer

Man kann einfach eine einzelne JAR-Datei mit AWS Lambda bereitstellen und Spring Profiles verwenden um verschiedene Funktionen in application.properties zu übergeben .

Schattiger Krug bauen

Um die Anwendung in AWS Lambda mit Spring Cloud Function bereitzustellen, benötigen Sie ein Shaded-Jar. Um dieses Glas zu erstellen, verwenden wir das Gradle Shadow-Plugin. Die Build-Datei sieht wie folgt aus:


buildscript {
	ext {
		springBootVersion = '2.6.2'
		wrapperVersion = '1.0.17.RELEASE'
		shadowVersion = '5.1.0'
	}
	repositories {
		mavenLocal()
		jcenter()
		mavenCentral()
		maven { url "https://repo.spring.io/snapshot" }
		maven { url "https://repo.spring.io/milestone" }
	}
	dependencies {
		classpath "com.github.jengelman.gradle.plugins:shadow:${shadowVersion}"
		classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}")
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
		classpath("io.spring.gradle:dependency-management-plugin:1.0.8.RELEASE")
	}
}
apply plugin: 'java'
apply plugin: 'maven-publish'
apply plugin: 'eclipse'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'com.betterjavacode'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'

repositories {
	mavenLocal()
	mavenCentral()
	maven { url "https://repo.spring.io/snapshot" }
	maven { url "https://repo.spring.io/milestone" }
}

ext {
	springCloudFunctionVersion = "3.2.1"
	awsLambdaEventsVersion = "2.0.2"
	awsLambdaCoreVersion = "1.2.1"
}

assemble.dependsOn = [shadowJar]

jar {
	manifest {
		attributes 'Main-Class': 'com.betterjavacode.springcloudfunctiondemo.SpringcloudfunctiondemoApplication'
	}
}

import com.github.jengelman.gradle.plugins.shadow.transformers.*

shadowJar {
	classifier = 'aws'
	dependencies {
		exclude(
				dependency("org.springframework.cloud:spring-cloud-function-web:${springCloudFunctionVersion}"))
	}
	// Required for Spring
	mergeServiceFiles()
	append 'META-INF/spring.handlers'
	append 'META-INF/spring.schemas'
	append 'META-INF/spring.tooling'
	transform(PropertiesFileTransformer) {
		paths = ['META-INF/spring.factories']
		mergeStrategy = "append"
	}
}

dependencyManagement {
	imports {
		mavenBom "org.springframework.cloud:spring-cloud-function-dependencies:${springCloudFunctionVersion}"
	}
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.cloud:spring-cloud-function-adapter-aws:3.2.1'
	implementation "com.amazonaws:aws-lambda-java-events:${awsLambdaEventsVersion}"
	implementation "com.amazonaws:aws-lambda-java-core:${awsLambdaCoreVersion}"
	runtimeOnly 'com.h2database:h2'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

test {
	useJUnitPlatform()
}

Führen Sie den Befehl ./gradlew clean build aus und es wird ein schattiertes Glas bauen. Ein Uber-Jar enthält den Inhalt mehrerer JAR-Dateien aus Abhängigkeiten. Ein schattiertes JAR bietet eine Möglichkeit, ein Uber-Jar zu erstellen und die Pakete von Uber-Jar umzubenennen. Um nun unser JAR in AWS Lambda bereitzustellen, müssen wir sicherstellen, dass eine Abhängigkeit com.amazonaws:aws-lambda-java-core enthalten ist .

Erstellen eines AWS Lambda in AWS

Unabhängig davon erstellen wir ein AWS Lambda in AWS.

Geben Sie einen aussagekräftigen Namen an – SpringCloudFunctionDemo .

Laden Sie das schattierte Glas hoch.

Aktualisieren Sie jetzt die Laufzeiteinstellungen in AWS Lambda, um anzugeben, wie das Lambda unsere Funktion aufruft. Spring bietet eine KlasseFunctionInvoker mit der generischen Methode handleRequest als Teil der Bibliothek spring-cloud-function-aws-adapter .

Wenn wir nun AWS Lambda ausführen, sehen wir die Ausführung unseres consumer Funktion. Wir werden unseren consumer testen Funktion mit einem JSON-Datenladevorgang:

{
  "name": "ABC Company",
  "customerIdentifier": "1",
  "email": "[email protected]",
  "contactPerson": "John Doe"
}

2022-01-23 06:45:08.987  INFO 9 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2022-01-23 06:45:09.391  INFO 9 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2022-01-23 06:45:09.455  INFO 9 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2022-01-23 06:45:10.289  INFO 9 --- [           main] org.hibernate.tuple.PojoInstantiator     : HHH000182: No default (no-argument) constructor for class: com.betterjavacode.springcloudfunctiondemo.models.Customer (class must be instantiated by Interceptor)
2022-01-23 06:45:10.777  INFO 9 --- [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2022-01-23 06:45:10.800  INFO 9 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2022-01-23 06:45:12.832  INFO 9 --- [           main] lambdainternal.LambdaRTEntry             : Started LambdaRTEntry in 8.239 seconds (JVM running for 8.868)
2022-01-23 06:45:12.919  INFO 9 --- [           main] o.s.c.f.adapter.aws.FunctionInvoker      : Locating function: 'customerConsumer'
2022-01-23 06:45:12.931  INFO 9 --- [           main] o.s.c.f.adapter.aws.FunctionInvoker      : Located function: 'customerConsumer'
2022-01-23 06:45:12.940  INFO 9 --- [           main] o.s.c.f.adapter.aws.FunctionInvoker      : Received: {"name":"ABC Company","customerIdentifier":"1","email":"[email protected]","contactPerson":"John Doe"}
2022-01-23 06:45:13.146  INFO 9 --- [           main] o.s.c.f.adapter.aws.AWSLambdaUtils       : Incoming JSON Event: {"name":"ABC Company","customerIdentifier":"1","email":"[email protected]","contactPerson":"John Doe"}
2022-01-23 06:45:13.146  INFO 9 --- [           main] o.s.c.f.adapter.aws.AWSLambdaUtils       : Incoming MAP: {name=ABC Company, customerIdentifier=1, [email protected], contactPerson=John Doe}
2022-01-23 06:45:13.166  INFO 9 --- [           main] o.s.c.f.adapter.aws.AWSLambdaUtils       : Incoming request headers: {id=042ab9bc-211d-fa47-839c-888720ec35d4, timestamp=1642920313144}
2022-01-23 06:45:13.184  INFO 9 --- [           main] c.b.s.functions.CustomerConsumer         : Creating the customer
END RequestId: b8352114-77f6-414c-a2dc-63d522a9eef4
REPORT RequestId: b8352114-77f6-414c-a2dc-63d522a9eef4	Duration: 710.53 ms	Billed Duration: 711 ms	Memory Size: 512 MB	Max Memory Used: 251 MB	Init Duration: 8986.65 ms	

Wie Sie im obigen Protokoll sehen können, gibt es ein Protokoll Creating the customer aus unserem Code. Außerdem sehen Sie die Antwort als Ok von der Lambda-Ausführung.

Der Code für diese Demo ist hier verfügbar.

Schlussfolgerung

AWS Lambda ist ein sehr leistungsfähiger Service zum Erstellen eines serverlosen Frameworks. Mit der Kombination von Spring Cloud und AWS kann man mehrere Funktionen nutzen, um einfachere Dienste für die Handhabung komplexer Geschäftsanforderungen zu erstellen. Hier ist ein weiterer Beitrag zum Verbinden der Spring Boot-Anwendung mit AWS Dynamo DB.


No
Java-Tag