Hoe pluggable Golang-applicatie te bouwen en te profiteren van AWS Lambda-lagen.

Golang - waarom is het uw aandacht waard?

Golang is een open source programmeertaal ontworpen en geïmplementeerd door Google. Het wordt veel gebruikt in moderne toepassingen, vooral in de cloud. Het zijn de meest karakteristieke kenmerken zijn:

  • Golang is statisch getypt - het biedt minder flexibiliteit, maar beschermt u tegen fouten,
  • Het is niet objectgericht. U kunt echter structuren en interfaces maken en dat geeft u 3 van 4 OOP-principes: gegevensabstractie, inkapseling en polymorfisme. Overerving is de enige die ontbreekt,
  • Goroutines! - de grootste implementatie van de lichte draden die ik ooit heb gebruikt. Hiermee kunt u op een supereenvoudige manier een nieuwe thread maken met de go-operator en met behulp van kanalen communiceren tussen verschillende goroutines,
  • Het compileert naar het enkele binaire bestand met alle afhankelijkheden - geen conflicten meer met pakketten!

Persoonlijk beschouw ik Golang als de beste taal die ik dagelijks gebruik. Dit artikel gaat echter niet over het maken van uw eerste functie of het afdrukken van "Hallo wereld". Ik zal je een beetje meer geavanceerde dingen laten zien. Als je een beginner bent en meer wilt weten over Golang, bezoek dan de hoofdpagina.

AWS Lambda & Golang

AWS Lambda is een van de meest populaire serverloze computerservices in de publieke cloud, uitgebracht in november 2014 door Amazon Web Services. Hiermee kunt u uw code uitvoeren als reactie op gebeurtenissen zoals DynamoDB, SNS of HTTP-triggers zonder servers in te richten of te beheren! Weet jij wat echt geweldig is? Sinds januari 2018 ondersteunt het de looptijd van Golang. Werken met AWS Lambda is heel eenvoudig - upload eenvoudig een zippakket met uw code en alle afhankelijkheden (één binair bestand bij gebruik van Golang).

Snel vooruit, 4 jaar later bij 2018 opnieuw: Invent AWS brengt Lambda Layers uit waarmee u gegevens kunt opslaan en beheren die worden gedeeld tussen verschillende functies in de enkele of zelfs meerdere AWS-accounts! Tijdens het gebruik van Python kunt u bijvoorbeeld alle afhankelijkheden in een extra laag plaatsen die later door andere Lambdas kan worden gebruikt. Het is niet nodig om verschillende afhankelijkheden in elk pakket met rits meer te plaatsen! In de Golang-wereld is de situatie anders omdat AWS Lambda vereist dat je gecompileerde binaire bestanden uploadt. Hoe kunnen we profiteren van de AWS Lambda-lagen? Het antwoord is eenvoudig - bouw een modulaire toepassing met behulp van Golang-plug-ins!

Golang-plug-ins - een manier om een ​​modulaire applicatie te bouwen

Golang-plug-ins is de functie die is uitgebracht in de Go1.8 waarmee u gedeelde bibliotheken (.so-bestanden) dynamisch kunt laden. Het geeft u de mogelijkheid om een ​​deel van uw code naar de afzonderlijke bibliotheek te exporteren of de plug-in te gebruiken die door iemand anders is voorbereid en gecompileerd. Het is veelbelovend, maar er zijn een paar beperkingen:

  • Uw plug-in moet één hoofdmodule zijn,
  • U kunt alleen functies en variabelen laden die als ELF-symbolen worden geëxporteerd,
  • Vanwege statisch typen moet u elk geladen symbool naar het juiste type casten. In het slechtste scenario moet u de juiste interface in uw code definiëren,
  • Het werkt alleen voor Linux en MacOS. Persoonlijk beschouw ik dit niet als een nadeel :)

Uw eerste plug-in bouwen en testen

Laten we nu onze eerste plug-in maken. Als voorbeeld maken we een eenvoudige module voor de stringversleuteling. Laten we teruggaan naar de basis en 2 eenvoudige coderingsalgoritmen implementeren - Ceasar en Verman.

  • Caesar-cijfer is het algoritme dat eerst door Julius Ceases werd gebruikt. Het verschuift elke letter in de tekst met het vaste aantal posities. Als u bijvoorbeeld het woord golang met de sleutel 4 wilt coderen, krijgt u ktpek. De ontsleuteling werkt op dezelfde manier. Je hoeft alleen de letters in de tegenovergestelde richting te verplaatsen.
  • Verman-cijfer lijkt op de Ceaser, gebaseerd op hetzelfde verschuivende idee, het verschil is dat u elke letter verschuift met een ander aantal posities. Om de tekst te decoderen, moet de sleutel de posities bevatten die worden gebruikt om de tekst te coderen. Als u bijvoorbeeld het woord golang wilt coderen met de sleutel [-1, 4, 7, 20, 4, -2], krijgt u toekomst.

De volledige implementatie van dit voorbeeld is hier beschikbaar.

Plugin implementatie

Het volgende fragment bevat de implementatie van de twee bovengenoemde algoritmen. Voor elk implementeren we 2 methoden voor het coderen en decoderen van onze tekst:

Zoals u kunt zien, hebben we hier 3 verschillende symbolen geëxporteerd (Golang exporteert alleen deze identificatiegegevens die met de hoofdletter beginnen):

  • EncryptCeasar - func (int, string) tekenreeks die tekst codeert met het Ceasar-algoritme,
  • DecryptCeaser - func (int, string) string die tekst decodeert met behulp van het Caeser-algoritme,
  • VermanCipher - variabele van het type vermanCipher die 2 methoden implementeert: Encrypt: func (string) string en Decrypt: func () (* string, error)

Om deze plug-in te compileren, moet u de volgende opdracht uitvoeren:

ga bouwen -buildmode = plugin -o plugin / cipher.so plugin / cipher.go

Voorlopig is er niets bijzonders - er zijn weinig eenvoudige functies gemaakt en een module is gecompileerd als een plug-in door het argument -buildmode = plugin toe te voegen.

Plug-in laden en testen

Het plezier begint wanneer we gecompileerde plug-ins in onze app willen gebruiken. Laten we een eenvoudig voorbeeld maken:

Eerst moet u het golang-plug-inpakket importeren. Het bevat slechts twee functies - de eerste is voor het laden van de gedeelde bibliotheek en de tweede is voor het vinden van een geëxporteerd symbool. Om uw bibliotheek te laden, moet u de functie Openen gebruiken, waarvoor u het pad naar uw gedeelde plug-in moet opgeven en een variabele van het type Plug-in retourneert. Als het laden van de bibliotheek niet mogelijk is (bijv. Onjuist pad of beschadigd bestand) retourneert deze functie de fout die moet worden afgehandeld.

De volgende stap is om elk geëxporteerd symbool te laden met behulp van de opzoekmethode. Een klein ongemak is dat u elke geëxporteerde functie afzonderlijk moet laden. U kunt echter meerdere functies op dezelfde manier combineren als bij het VermanCipher-symbool. Nadat je alle symbolen hebt geladen die je wilt gebruiken, moet je ze naar het juiste type casten. Golang is een statisch getypte taal, dus er is geen andere manier om deze symbolen te gebruiken zonder te casten. Vergeet niet dat wanneer u een variabele exporteert die een paar methoden implementeert, u deze naar het juiste interfacetype moet casten (ik moest coderingEngine-interface definiëren om dit te verwerken). \ Newline \ newline

Gebruik de volgende opdracht om de app te compileren en uit te voeren:

ga app.go bouwen
./app

In de uitvoer moet u de gecodeerde en gedecodeerde tekst zien als een bewijs dat het algoritme correct werkt.

Gebruik plugin in AWS lambda

Om onze plug-in in de AWS Lambda te gebruiken, moeten we enkele wijzigingen aanbrengen in onze applicatie:

  • AWS Lambda koppelt lagen aan de / opt-directory in de lambda-container, dus we moeten onze plug-in laden vanuit deze directory.
  • We moeten een handlerfunctie maken die door de Lambda-motor wordt gebruikt om ons testevenement af te handelen.

Het volgende fragment bevat onze applicatie aangepast om te worden gebruikt door de Lambda:

Zoals u ziet, lijkt de implementatie erg op de vorige. We hebben alleen de map gewijzigd van waaruit we onze plug-in hebben geladen en de functie-respons toegevoegd in plaats van waarden af ​​te drukken. Raadpleeg de AWS-documentatie als u meer wilt weten over het schrijven van Lambdas in golang.

AWS Lambda-implementatie

Er zijn twee manieren om AWS Lambda-functies en -lagen te implementeren. U kunt een gecomprimeerd pakket handmatig maken en uploaden of het geavanceerdere framework gebruiken, wat het veel eenvoudiger en sneller maakt. Voor de meeste van mijn projecten gebruik ik het Serverless-framework, dus ik heb het eenvoudige serverless.yml-configuratiebestand al voorbereid met deze tool:

service: cipherService
frameworkVersion: "> = 1.28.0 <2.0.0"
provider:
  naam: aws
  looptijd: go1.x
lagen:
  cipherLayer:
    pad: bin / plug-in
    compatibleRuntimes:
      - go1.x
functies:
  motor:
    handler: bin / cipherEngine
    pakket:
      uitsluiten:
        - ./**
      zijn onder andere:
        - ./bin/cipherEngine
    lagen:
      - {Ref: CipherLayerLambdaLayer}

In de lagensectie hebben we een enkele laag gedefinieerd met het pad naar de reeds gemaakte plug-in - deze wordt samen met de lambdafunctie geïmplementeerd. U kunt maximaal 5 verschillende lagen definiëren, welke volgorde erg belangrijk is. Ze zijn gekoppeld aan dezelfde / opt-map, dus lagen met een hoger nummer kunnen bestanden van de eerder gekoppelde lagen overschrijven. Voor elke laag moet u ten minste 2 parameters opgeven: pad naar de map met de laagbron (in uw geval het pad naar de plug-in binary) en de lijst met compatibele runtimes.

Het volgende gedeelte met functies is een plaats waar u de lijst met te implementeren functies definieert. Voor elke functie moet u ten minste het pad naar de gecompileerde toepassing opgeven. Bovendien moeten we de parameter lagen definiëren met de verwijzing naar de hierboven gedefinieerde laag. Hierdoor wordt de laag automatisch aan onze Lambda-functie bevestigd tijdens de implementatie. Het grappige is dat je de naam van je lambda-laag moet converteren naar TitleCased en het achtervoegsel LambdaLayer moet toevoegen als je naar die bron wilt verwijzen. Het lijkt erop dat het serverloze team het op deze manier heeft geïmplementeerd om het conflict op te lossen met betrekking tot de verschillende soorten bronnen.

Zodra ons configuratiebestand serverless.yml gereed is, is het laatste wat u moet doen onze app compileren, invoegen en implementeren. We kunnen daarvoor eenvoudige Makefile gebruiken:

.PHONY: build buildPlugin schone inzet
bouwen:
 dep zorgen -v
 env GOOS = linux go build -ldflags = "- s -w" -o bin / cipherEngine cipherEngine / main.go
buildPlugin:
 env GOOS = linux go build -ldflags = "- s -w" -buildmode = plugin -o bin / plugin / cipher.so ../plugin/cipher.go
schoon:
 rm -rf ./bin ./vendor Gopkg.lock
deploy: clean buildPlugin build
 sls implementeren --verbose

U kunt uw functie bouwen en implementeren door de volgende opdracht uit te voeren:

maken inzetten

Test AWS Lambda

Zoals ik al eerder zei, voert AWS Lambda code uit in reactie op de gebeurtenis. We hebben echter geen triggers voor gebeurtenissen geconfigureerd, dus deze worden zonder onze hulp niet aangeroepen. We moeten dit handmatig doen met behulp van het Serverless-framework of de awscli-tool:

sls roept -f functienaam op
aws lambda roept - functienaam functienaam output_bestand aan

In de reactie zou je dezelfde output als voorheen moeten zien, wat bewijst dat onze lambda-functie correct werkt en de plug-in uit de extra laag laadt. Nu kunt u andere functies maken die dezelfde laag gebruiken of deze zelfs delen met andere AWS-accounts.

Overzicht

Het was erg leuk om Golang-modules te gebruiken en te testen hoe ze te integreren met de onlangs vrijgegeven AWS Lambda-lagen. Pluginbibliotheek is echt geweldig, maar vanwege zijn beperkingen en Golang-specificatie kan het alleen in sommige speciale scenario's worden gebruikt. Ik denk dat het voor de meeste ontwikkelaars die aan de standaardprojecten werken niet nodig of zelfs mogelijk is om plug-ins te gebruiken. Ik denk maar aan twee redenen:

  • Implementeren van gecompliceerde algoritmen die door de andere applicaties kunnen worden gebruikt ex. videocodering of coderingsalgoritmen.
  • Uw algoritme delen met anderen zonder de code te publiceren.