Java >> Java tutorial >  >> Tag >> Spring

Eksempel på Spring Cloud-funktion med AWS Lambda

I dette indlæg lærer vi om Spring Cloud Function og vil implementere et eksempel på Spring Cloud Function på AWS Lambda. Ved slutningen af ​​dette indlæg vil vi have mere forståelse for serverløse funktioner. Hvis du vil lære mere om serverløs arkitektur, vil dette indlæg få dig i gang.

Hvad er Spring Cloud-funktionen?

Spring Cloud-funktion er en af ​​funktionerne i Spring Cloud. Det giver udviklere mulighed for at skrive sky-agnostiske funktioner med Spring-funktioner. Disse funktioner kan være stand-alone klasser, og man kan nemt implementere på enhver cloud platform for at bygge en serverløs ramme. Spring Cloud tilbyder et bibliotek spring-cloud-starter-function-web giver mulighed for at bygge funktioner med Spring-funktioner, og det bringer alle de nødvendige afhængigheder.

Hvorfor bruge Spring Cloud Function?

Dette spørgsmål er mere, hvornår man skal bruge Spring Cloud Function. Grundlæggende giver Spring Cloud Function-biblioteket mulighed for at skabe funktionelle applikationer, der nemt kan implementeres på AWS Lambda. Disse funktioner følger Java 8-mønsteret i Supplier , Consumer og Function .

spring-cloud-starter-function-web biblioteket giver indbygget interaktion til håndtering af anmodninger, streams.

Funktioner i Spring Cloud Function

Den største fordel ved Spring Cloud Function er, at den giver alle funktionerne i Spring Boot som autoconfiguration , dependency injection . Men der er flere funktioner:

  • Transparent typekonverteringer af input og output
  • POJO-funktioner
  • REST-understøttelse for at afsløre funktioner som HTTP-slutpunkter
  • Streaming af data til/fra funktioner via Spring Cloud Stream framework
  • Implementering af funktioner som isolerede jar-filer
  • Adapter til AWS Lambda, Google Cloud Platform, Microsoft Azure

Demo

Som en del af dette indlæg vil vi oprette Spring Cloud Function og implementere det i AWS Lambda. Når vi har oprettet et almindeligt spring boot-program, skal du tilføje følgende afhængigheder i din Gradle-fil:

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'
}

Bemærk afhængigheden spring-cloud-function-adapter-aws giver os mulighed for at integrere Spring Cloud Function med AWS Lambda.

En hovedklasse for applikationen vil se ud som nedenfor:

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);
	}

}

Sammenlign dette med en almindelig Spring Boot-applikation, der er én forskel. Vi bruger FunctionalSpringApplication som et indgangspunkt. Dette er en funktionel tilgang til at skrive bønner og hjælper med opstartstid.

Nu kan vi skrive tre typer funktioner Function , Consumer ELLER Supplier . Vi vil se, hvad hver funktion gør, og hvordan vi kan bruge som en del af denne demo.

Lad os desuden oprette en POJO-modelklasse Customer .

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;
    }
}

Helt sikkert vil vores forårsskyfunktion udføre noget forretningslogik relateret til denne model Customer .

Forbrugerfunktion

Lad os oprette en Consumer fungere. Consumer funktion tager normalt et input og udfører noget forretningslogik, der vil have en bivirkning på dataene. Det vil ikke producere noget output. Så det er mere som en void metode.

For vores demo vil det se ud som nedenfor:

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);
    }

}

Denne CustomerConsumer funktion implementerer Consumer funktionstype og tager input af typen Map<String, String> . Som en del af grænsefladekontrakten skal man implementere metoden accept . Denne metode tager map input og udføre noget forretningslogik. En ting at forstå er, at Spring Cloud Function vil håndtere typekonvertering fra rå inputstrøm og typer, der er erklæret af funktionen. Hvis funktionen ikke er i stand til at udlede informationer, vil den konvertere til en generisk type map .

Denne funktion tager et kort over DTO-objektet til kunden og gemmer det i databasen. Til databasen bruger vi H2 in-memory database. Man kan altid tilføje mere forretningslogik, men til demoformål viser vi et simpelt eksempel.

Leverandørfunktion

Leverandørfunktionen fungerer som et GET-slutpunkt. Denne funktion tager ingen input, men returnerer data.

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);
    }
}

Konfiguration af Spring Cloud-funktion med AWS Lambda

Én AWS Lambda vil kun udføre én funktion. Hvis der er flere Spring Cloud Function bønner, kan man konfigurere, hvilken funktion der skal udføres gennem en lambda. Tilføj ejendommen i application.properties som følger:

spring.cloud.function.definition=customerConsumer

Man kan nemt implementere en enkelt jar-fil med AWS Lambda og bruge Spring Profiles for at videregive forskellige funktioner i application.properties .

Building Shaded Jar

For at implementere applikationen i AWS Lambda med Spring Cloud Function skal du bruge en skraveret krukke. For at bygge denne krukke bruger vi gradle shadow plugin. Byggefilen vil se ud som nedenfor:


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()
}

Kør kommandoen ./gradlew clean build og det vil bygge en skyggefuld krukke. En Uber Jar indeholder indholdet af flere krukker fra afhængigheder. En skraveret krukke giver mulighed for at skabe en uber jar og omdøbe pakkerne fra Uber Jar. For nu at implementere vores jar i AWS Lambda, skal vi sørge for at inkludere en afhængighed com.amazonaws:aws-lambda-java-core .

Oprettelse af en AWS Lambda i AWS

Uanset hvad, lad os skabe en AWS Lambda i AWS.

Angiv et beskrivende navn – SpringCloudFunctionDemo .

Upload den skraverede krukke.

Opdater nu Runtime Settings i AWS Lambda for at indikere, hvordan lambdaen vil påkalde vores funktion. Spring giver en klasseFunctionInvoker med generisk metode handleRequest som en del af biblioteket spring-cloud-function-aws-adapter .

Hvis vi nu kører AWS Lambda, vil vi se udførelsen af ​​vores consumer fungere. Vi tester vores consumer funktion med en JSON-dataindlæsning:

{
  "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	

Som du kan se i ovenstående log, er der en log Creating the customer fra vores kode. Du vil også se svaret som Ok fra Lambda-udførelse.

Koden til denne demo er tilgængelig her.

Konklusion

AWS Lambda er en meget kraftfuld service til at bygge et serverløst framework. Med kombinationen af ​​Spring Cloud og AWS kan man udnytte flere funktioner til at bygge enklere tjenester til håndtering af komplekse forretningskrav. Her er endnu et indlæg om at forbinde Spring Boot-applikationen med AWS Dynamo DB.


No
Java tag