golang lambda
Sometimes I get this weird urge not to self-flagellate. When that happens, I stop fucking around with Java, Javascript or Python and I turn to Go. Go is fast, light-weight and statically typed. Its package management system is a dream. You can get started with Go in about three seconds, which is 2 million times faster than the time it takes to work out what Microsoft was thinking when they created Typescript.
有时我得到这种奇怪的冲动,不要自我鞭打。 当发生这种情况时,我不再使用Java,Javascript或Python,而转向Go。 Go是一种快速,轻巧和静态输入的程序。 它的包裹管理系统是一个梦想。 您可以在三秒钟内开始使用Go,这比计算Microsoft创建Typescript时所考虑的时间要快200万倍。
This is the first in a series of articles where we’ll write and deploy an entire backend using Golang, DynamoDB, AWS Lambda and API Gateway. My intent is to show you just how much better it is to write backends in Go. If I am successful in convincing you, then this will have been a worthwhile process. If I’m not, it’s still worthwhile, because then you’ll have learned that you hate yourself and you can seek out professional assistance.
这是系列文章中的第一篇,我们将使用Golang,DynamoDB,AWS Lambda和API Gateway编写和部署整个后端。 我的目的是向您展示在Go中编写后端要好得多。 如果我成功地说服你,那么这将是一个有价值的过程。 如果不是,那还是值得的,因为那样您就会了解到自己讨厌自己,可以寻求专业帮助。
我们将建立什么 (What we will build)
Eventually, the backend for a fantasy hunting log app where you can add new monsters, view all entries, and record kills.
最终,这是一个幻想狩猎日志应用程序的后端,您可以在其中添加新的怪物,查看所有条目并记录杀死事件。
In this specific article, we will create the first lambda (save-and-update) without any dynamo integration, deploy it to AWS and prove it works.
在这篇特定的文章中,我们将创建第一个lambda(保存并更新)而不进行任何dynamo集成,将其部署到AWS并证明其有效。
你需要什么 (What you need)
Go. I couldn’t explain the installation process any better than the official docs, so check those out here.
走。 我无法比官方文档更好地解释安装过程,因此请在此处查看 。
You also need an AWS account and some sort of code editor (I use VSCode).
您还需要一个AWS账户和某种代码编辑器(我使用VSCode)。
创建Lambda (Creating the Lambda)
Before we do any coding, we need to create a lambda in AWS. Go to the lambda console (here) and click ‘Create function’. Enter a name for your function and make sure you select Go 1.x
from the runtime dropdown.
在进行任何编码之前,我们需要在AWS中创建一个lambda。 转到lambda控制台( 在此处 ),然后单击“创建功能”。 输入函数的名称,并确保从运行时下拉列表中选择Go 1.x
Leave everything else the way it is and click ‘Create function’ at the bottom. Once created, scroll down to the ‘Basic Settings’ window and hit ‘Edit’.
保留一切,然后单击底部的“创建功能”。 创建完成后,向下滚动到“基本设置”窗口,然后点击“编辑”。
Change the ‘handler’ from hello
to main
since our file is called main.go
.
因为我们的文件名为main.go
所以将“ handler”从hello
更改为main
。
Save that, and that’s it for now.
保存,仅此而已。
Writing the Code
编写代码
To start, we’ll create a lambda that receives an HTTP request with our monster
as the JSON payload. Then we will convert that JSON into our monster type and return only the name. If we get these three things working first then we can focus on dynamo without worrying about bugs elsewhere in the program.
首先,我们将创建一个lambda来接收一个以monster
作为JSON负载的HTTP请求。 然后,我们将该JSON转换为我们的Monster类型,并仅返回名称。 如果我们首先使这三样东西起作用,那么我们就可以专注于发电机,而不必担心程序中其他地方的错误。
Assuming you’ve installed Go already, create a directory somewhere on your computer called fantasy-hunt
and cd
into it. Then, create a folder called save-and-update
.
假设您已经安装了Go,请在计算机上的某个位置创建一个名为fantasy-hunt
,并将其插入cd
。 然后,创建一个名为save-and-update
的文件夹。
This directory will contain a main.go
file, which is where we’ll write the main entry point code for our save-and-update
lambda. Later we will have a get-all
lambda which will have its own main.go
file.
该目录将包含一个main.go
文件,在该文件中,我们将为save-and-update
lambda编写主入口点代码。 稍后,我们将有一个get-all
的λ,这将有自己的main.go
文件。
Types
种类
Before we start writing any main code, though, I usually like to define the types our program will use. Designing strong data types is the best way to speed up development and avoid runtime bugs. Data is everything. Without it, there’s not much point in programming.
但是,在开始编写任何主要代码之前,我通常希望定义程序将使用的类型。 设计强大的数据类型是加快开发速度并避免运行时错误的最佳方法。 数据就是一切。 没有它,编程就没有多大意义。
Our program will only have one type: monster
. Create a types
folder in your fantasy-hunt
directory, and then create a monster.go
file inside it, which should look something like this.
我们的程序只有一种类型: monster
。 在fantasy-hunt
目录中创建一个types
文件夹,然后在其中创建一个monster.go
文件,该文件应如下所示。
package types
type Monster struct {
ID string `json:"id"`
Name string `json:"name"`
Hunted bool `json:"hunted"`
}
This is how you define a complex type in Go, which are somewhat like classes in other languages, but are called structs
in Go. This one is a struct called Monster, which has three properties: ID, Name and Hunted.
这就是您在Go中定义复杂类型的方式,这种类型有点像其他语言中的类,但是在Go中称为structs
。 这是一个名为Monster的结构,它具有三个属性:ID,Name和Hunted。
Each of these properties is made up of three things, a name, a type and a string for mapping the object to json. The name and type sections are hopefully self-explanatory. The json part, i.e. `json:”id”`
tells Go’s json
library which part of a json payload that field should map to. So, for example, if we attempted to convert a json payload into a monster object, the json library would read each of these and workout that the id
field in the json corresponds to the Id
field of the struct, and so on.
这些属性中的每一个都由三部分组成:名称,类型和用于将对象映射到json的字符串。 名称和类型部分希望是不言自明的。 json部分,即`json:”id”`
告诉Go的json
库该字段应映射到json负载的哪一部分。 因此,例如,如果我们尝试将json有效负载转换为怪物对象,则json库将读取其中的每个内容,并进行锻炼,使json中的id
字段对应于该结构的Id
字段,依此类推。
You’ve probably noticed that the struct name and all the property names are capitalised. This is important. In Go, access to fields, objects and methods is controlled through capitalisation. If any of these names start with a capital letter, it means it is accessible outside of the immediate file where it is defined. If it starts with a lowercase letter, it isn’t. So here, because Monster
and all of its properties are capitalised, it means we can reference this type from another file and still access everything on it. The json
library also depends on the capitalisation, because it has to access the fields on the struct when marshaling (we will see marshaling in a second).
您可能已经注意到结构名称和所有属性名称都大写。 这个很重要。 在Go中,通过大写控制对字段,对象和方法的访问。 如果这些名称中的任何一个以大写字母开头,则意味着可以在定义它的直接文件之外访问它。 如果以小写字母开头,则不是。 所以在这里,因为Monster
及其所有属性都大写,这意味着我们可以从另一个文件中引用此类型,并且仍然可以访问其中的所有内容。 json
库还取决于大小写,因为封送处理时必须访问结构上的字段(我们将在第二秒看到封送处理)。
The last thing is the package declaration at the top. This simply defines a sort of namespace for the code. Since it’s in the types
folder, we just call it types.
最后一件事是顶部的包声明。 这只是为代码定义了一种名称空间。 由于它位于types
文件夹中,因此我们将其称为类型。
The main file
主文件
The main file is a little more complicated, so we will take it line by line.
主文件稍微复杂一点,因此我们将逐行介绍它。
package main
import (
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-lambda-go/events"
"net/http"
"encoding/json"
"../types"
)
func main() {
lambda.Start(Handler)
}
func Handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
var monster types.Monster
err := json.Unmarshal([]byte(req.Body), &monster)
if err != nil {
return response("Couldn't unmarshal json into monster struct", http.StatusBadRequest), nil
}
return response(monster.Name, http.StatusOK), nil
}
func response(body string, statusCode int) events.APIGatewayProxyResponse {
return events.APIGatewayProxyResponse {
StatusCode: statusCode,
Body: string(body),
Headers: map[string]string {
"Access-Control-Allow-Origin": "*",
},
}
}
- Line 1 1号线
Another package declaration. Because this is the main file, we will call the package main, even though the folder is called save-and-update
.
另一个包声明。 因为这是主文件,所以即使文件夹称为save-and-update
,我们也将其称为包main。
- Lines 3–9 第3–9行
Import statements. Go’s package management system lets you directly pull github repo’s and use them in your code. That’s what we do here for the AWS lambda and events packages. Once you’ve defined them in your code, you have to run go get <github-url>
in your terminal to download them to your machine, e.g. go get github.com/aws/aws-lambda-go/lambda
导入语句。 Go的包管理系统使您可以直接拉github repo并将其用于代码中。 这就是我们在这里为AWS lambda和事件包所做的。 一旦在代码中定义了它们,就必须在终端中运行go get <github-url>
以将它们下载到您的机器上,例如go get github.com/aws/aws-lambda-go/lambda
The non-github imports don’t require you to install anything. The ones on line 6 & 7 are from the Go standard library which comes pre-installed with Go. We use net/http
to access all the HTTP StatusCode constants. We use encoding/json
to parse and convert JSON into our struct types.
非github导入不需要您安装任何东西。 第6和7行上的代码来自Go预先安装的Go标准库。 我们使用net/http
访问所有HTTP StatusCode常量。 我们使用encoding/json
进行解析并将JSON转换为我们的结构类型。
Line 8 imports our types package.
第8行导入我们的类型包。
- Lines 12–14 第12-14行
Every main Go package needs a main function. This is the function that is called when we run a Go program. This one only calls lambda.Start(Handler)
which tells AWS that our Handler
function (what we will see next) is what should be called when the lambda gets called.
每个主要的Go软件包都需要一个主要功能。 这是我们运行Go程序时调用的函数。 这个仅调用lambda.Start(Handler)
,它告诉AWS当调用lambda时应调用我们的Handler
函数(接下来将要看到的内容)。
- Line 17 17号线
This is our Handler function declaration. It receives one parameter, req
of type ApiGatewayProxyRequest
from the events
package. We use this type because we’ll be integrating with API Gateway later. It returns two things; a variable of type ApiGatewayProxyResponse
and an error. This is a common pattern in Go. Functions will often return the actual value and an error value, and if the error value is not nil
(like null in other languages) then we can assume that the function has failed somewhere. We’ll see that used in a second.
这是我们的Handler函数声明。 它从events
包中接收一个参数,即ApiGatewayProxyRequest
类型的req
。 我们使用此类型,因为稍后将与API Gateway集成。 它返回两件事; 类型为ApiGatewayProxyResponse
的变量和错误。 这是Go中的常见模式。 函数通常会返回实际值和错误值,如果错误值不为nil
(如其他语言中的null),那么我们可以假定函数在某处发生了故障。 我们将在一秒钟内看到它。
- Line 18 18号线
We create a variable of type Monster
.
我们创建一个Monster
类型的变量。
- Line 20 20号线
This is where we convert the JSON payload to our monster type, which is what Unmarshal
does. The first parameter has to be a byte array, so we convert the Body
field of our req
parameter to a byte array by calling []byte(req.Body)
. The second parameter is the monster object we created.
这是我们将JSON有效负载转换为我们的怪物类型的地方,这正是Unmarshal
所做的。 第一个参数必须是字节数组,因此我们可以通过调用[]byte(req.Body)
将req
参数的Body
字段转换为字节数组。 第二个参数是我们创建的怪物对象。
The &
symbol here tells Go that we want to pass our monster object by reference. This means that any changes that get made to the monster object inside the Unmarshal
function should also get made to the monster object we have in our Handler
function, i.e., when Unmarshal
is finished doing its thing, our monster field will be filled in without having to explicitly return it.
这里的&
符号告诉Go我们想通过引用传递怪物对象。 这意味着对Unmarshal
函数中的Unmarshal
对象所做的任何更改也应该对我们在Handler
函数中具有的Monster对象进行更改,即,当Unmarshal
完成其工作时,我们的Monster域将被填充而无需明确返回它。
The Unmarshal
function could return an error, so that’s why we’re putting the result of it into an err
object. We use the special :=
operator, which effectively tells Go to create the object, work out the type on its own and then fill it in, all at the same time, without us having to explicitly do that.
Unmarshal
函数可能返回错误,因此这就是我们将其结果放入err
对象的原因。 我们使用特殊的:=
运算符,该运算符有效地告诉Go创建对象,自行计算类型,然后同时填充它,而无需我们显式地进行操作。
- Lines 22–24 22-24行
This is typically how error handling is done in Go. If no error occurred during the Unmarshal
function, err
would be nil
, which means the if statement would not execute. If it does execute, it returns the result of calling our response
function, which we will look at in a second. We also have to specify , nil
at the end of this line, because this function is supposed to return 2 things, but we’re handling the error gracefully so we can just make the err object nil.
这通常是Go中错误处理的方式。 如果在Unmarshal
函数执行过程中未发生任何错误,则err
将为nil
,这意味着if语句将不会执行。 如果确实执行,它将返回调用我们的response
函数的结果,我们将在稍后查看。 我们还必须在此行的末尾指定, nil
,因为该函数应该返回2个东西,但是我们正在优雅地处理错误,因此我们可以将err对象设置为nil。
- Line 26 26号线
This calls the response function and passes it our monster’s name and the http OK status code, because by this point we’ve successfully finished what we wanted to do.
这将调用响应函数并将其传递给我们的怪物名称和http OK状态代码,因为到此为止,我们已经成功完成了我们想做的事情。
- Lines 29–37 第29–37行
Here we are creating a function which takes in a body and a status code and then returns the type of value that amazon lambda expects us to return, an events.ApiGatewayProxyResponse
. Then it returns it, filling in the status code, the body, and a map of headers for CORS purposes.
在这里,我们正在创建一个函数,该函数接受主体和状态码,然后返回amazon lambda期望我们返回的值的类型,即events.ApiGatewayProxyResponse
。 然后返回它,并出于CORS的目的填写状态代码,正文和标头映射。
Uploading the Lambda to AWS
将Lambda上载到AWS
Now that the code is completed, we can build and deploy our lambda. The first step to this is compiling our code into a linux binary (aws lambda requires this).
现在代码已完成,我们可以构建和部署lambda。 第一步是将我们的代码编译成linux二进制文件(aws lambda要求这样做)。
In your terminal, navigate to your save-and-update
folder and run the following command:
在您的终端中,导航到“ save-and-update
文件夹并运行以下命令:
GOOS=linux GOARCH=amd64 go build -v main.go
GOOS=linux GOARCH=amd64 go build -v main.go
This should spit out a binary file into the folder called main
.
这应该将二进制文件吐出到名为main
的文件夹中。
Using your GUI, find that file and zip it up into an archive called main.zip
. Then navigate back to your lambda’s console and find the Function code
section. Select the Actions
dropdown and choose Upload a .zip file
.
使用您的GUI,找到该文件并将其压缩到名为main.zip
的存档中。 然后导航回lambda的控制台并找到“ Function code
部分。 选择“ Actions
下拉列表,然后选择“上Upload a .zip file
。
Find your main.zip
archive and upload it.
找到您的main.zip
存档并上传。
Once that’s complete, we can test that the lambda is working as expected. Find the Test
button at the top of the page. It should open a modal where you can configure a test event. Find the Event template dropdown and search for Amazon API Gateway AWS Proxy
. This is the type of event that will be sent to your lambda when it’s called from an API Gateway.
完成后,我们可以测试lambda是否按预期工作。 找到页面顶部的“ Test
按钮。 它应该打开一个模式,您可以在其中配置测试事件。 查找事件模板下拉列表,然后搜索Amazon API Gateway AWS Proxy
。 这是从API网关调用时将发送到您的lambda的事件类型。
The event payload should now be filled in. Find the body
property at the top and edit it so that you’re passing in this:
现在应该填写事件有效负载。在顶部找到body
属性,然后对其进行编辑,以使您能够通过此操作:
"{ \"id\": \"1\", \"name\": \"A Scary Monster\", \"hunted\":true }"
This is what our monster JSON payload looks like. Give your test event a name and save it.
这就是我们的怪物JSON有效负载。 给您的测试事件命名并保存。
Now, if you hit the Test
button, your lambda should execute, and you should see a response like this:
现在,如果您单击“ Test
按钮,则应执行lambda,并应看到如下所示的响应:
{
"statusCode": 200,
"headers": {
"Access-Control-Allow-Origin": "*"
},
"multiValueHeaders": null,
"body": "A Scary Monster"
}
And that’s it.
就是这样。
There’s still a bit to be done before this app can be used by fantasy hunters, but hopefully you’ve had a nice taster of Go and it’s made you hungry for more. So far, you’ve learned about packages, structs, functions, multiple returns, error handling, compiling and deploying to AWS. With that grounding, we can begin to get into more complex stuff. But it actually never really gets too complicated, because Go is great. Go loves you. You should love it back.
幻想猎人可以使用这个程序之前,还有很多工作要做,但是希望您对Go有了一个很好的品尝,并且让您渴望更多。 到目前为止,您已经了解了包,结构,函数,多次返回,错误处理,编译和部署到AWS。 基于此基础,我们可以开始研究更复杂的内容。 但是实际上它从来不会变得太复杂,因为Go很棒。 去爱你。 你应该爱它。
You can find all the code for this article in this repo.
您可以在此 仓库中 找到本文的所有代码 。
golang lambda