Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

สถาปัตยกรรมแบบไร้เซิร์ฟเวอร์:AWS Lambda, Upstash Redis &Go สำหรับแอปที่ปรับขนาดได้และคุ้มค่า

บทนำ

แพลตฟอร์มการประมวลผลแบบไร้เซิร์ฟเวอร์นั้นยอดเยี่ยม แต่ถ้าไม่มีฐานข้อมูลแบบไร้เซิร์ฟเวอร์ แพลตฟอร์มเหล่านั้นก็มีข้อจำกัดมากเกินไป

ในขณะที่ฉันกำลังสร้างแพลตฟอร์มสำหรับหลักสูตรที่กำลังจะมาถึง The Elements of CI/CD ฉันต้องการฐานข้อมูลแบบไร้เซิร์ฟเวอร์เนื่องจากฉันตัดสินใจใช้ AWS Lambda เป็นเซิร์ฟเวอร์ของฉันสำหรับบางสิ่ง ข้อกำหนดที่ฉันมีคือ:

  1. จ่ายตามราคา . ฉันไม่ต้องการจ่ายต่อชั่วโมงหรือต่อโหนด แต่จ่ายตามการใช้งาน (คำขอ พื้นที่เก็บข้อมูล ฯลฯ) การเริ่มใช้งานควรมีราคาถูกมาก และเมื่อการใช้งานเพิ่มขึ้น ต้นทุนก็จะเพิ่มขึ้นตามสัดส่วน
  2. เวลาแฝงต่ำ . ไม่มีใครชอบการตอบสนองที่ช้า ดังนั้นการสืบค้นฐานข้อมูลควรรวดเร็วจากภายในภูมิภาค AWS (เช่น 01 ).
  3. ประสบการณ์นักพัฒนาที่ยอดเยี่ยม (DevX) . การมีอินเทอร์เฟซที่ดีสำหรับฐานข้อมูลโดยไม่ต้องเรียนรู้ DSL เฉพาะกลุ่มอื่น หรือเสียเวลาหลายชั่วโมงกับการเล่นเว็บไซต์เป็นที่ต้องการ

Upstash Redis เป็นไปตามข้อกำหนดข้างต้นทั้งหมด และทำงานได้ดีมาก

  • จ่ายตามการใช้งาน?
    • ✅ ราคาไม่แพงจริงๆ สำหรับการเริ่มต้นและในวงกว้าง!
  • เวลาแฝงต่ำ?
    • 14 เวลาแฝง 1 มิลลิวินาทีจากการสืบค้นจากภายใน AWS Lambda!
  • DevX เยี่ยมมาก?
    • ✅เป็น Redis มาตรฐาน ใช่แล้ว

ในบทความนี้ เราจะดูวิธีใช้ Upstash Redis จากภายใน AWS Lambda เพื่อให้แน่ใจว่ารวดเร็วเพียงพอสำหรับความต้องการของเรา ขณะเดียวกันก็รักษาโค้ดของเราให้ดูแลรักษาได้เพื่อให้สามารถทดสอบภายในเครื่องหรือปรับใช้กับแพลตฟอร์มอื่น หากจำเป็น

เรากำลังดำเนินการอะไร

เพื่อความง่าย เราจะใช้ตำแหน่งข้อมูล API เพียง 3 รายการ:

  1. รหัส <24 จุดสิ้นสุดที่ยอมรับ 38 เป็นพารามิเตอร์การค้นหาใน 42 หรือภายในค่าแบบฟอร์มที่ส่งด้วย 57 คำขอ ตำแหน่งข้อมูลนี้จะสร้าง ID เซสชัน เก็บไว้ใน Redis และตั้งค่าคุกกี้สำหรับการเข้าชมครั้งต่อไปด้วย 68 แค่ช่วยให้ทดสอบได้ง่ายขึ้น🙃
  2. รหัส <77 ตำแหน่งข้อมูลกำหนดให้ผู้ใช้ที่เข้าสู่ระบบ (เช่น มีคุกกี้ที่มีรหัสเซสชัน) และส่งคืนการตอบสนอง JSON พร้อมบทเรียนทั้งหมดที่ผู้ใช้เสร็จสิ้นและเวลาใด
  3. รหัส <89 ตำแหน่งข้อมูลกำหนดให้ผู้ใช้ที่เข้าสู่ระบบ (เช่น มีคุกกี้ที่มีรหัสเซสชัน) และทำเครื่องหมายบทเรียนที่แสดงโดย 90 เสร็จสมบูรณ์ตามเวลาปัจจุบัน

หมายเหตุ:ในโค้ดด้านล่างมีบางสิ่งที่ขาดหายไป ดังนั้นโค้ดนี้จึงไม่ใช่โค้ดที่พร้อมสำหรับการคัดลอกและวางได้ ตัวอย่างเช่น เราควรตรวจสอบว่า 103 ที่ให้มานั้น มีอยู่แล้วก่อนที่จะอัปเดต ตำแหน่งข้อมูลการเข้าสู่ระบบควรยอมรับรหัสผ่านและทำการตรวจสอบแบบ Salted/Hash อย่างเหมาะสมก่อนสร้างรหัสเซสชัน ฯลฯ

1. ตั้งค่า

  • โค้ดทั้งหมดที่มีรายละเอียดด้านล่างนี้มีอยู่ใน 117 ของฉันด้วย พื้นที่เก็บข้อมูลหากคุณต้องการดูว่าทุกอย่างเข้ากันอย่างไร

ดังที่คุณจะเห็นด้านล่าง เรากำลังสร้างจุดเข้าใช้งานสองจุด นั่นคือ คำสั่งปฏิบัติการสองคำสั่ง อันหนึ่งสำหรับเซิร์ฟเวอร์ภายในเครื่องปกติ และอีกอันสำหรับ AWS Lambda ด้วยวิธีนี้ เราจะสามารถทดสอบตรรกะทั้งหมดของเราภายในเครื่อง และด้วยการทดสอบหน่วย/การรวมมาตรฐานหากเราต้องการ

ความแตกต่างเพียงอย่างเดียวแสดงอยู่ในส่วนที่ 1.2 และ 1.3 ด้านล่าง

1.1 พื้นที่ทำงาน

ก่อนที่เราจะเจาะลึกโค้ด เรามาตั้งค่าไดเร็กทอรีการทำงานสำหรับ Go กันก่อน

  1. ดาวน์โหลดไป
  2. สร้างบัญชีและฐานข้อมูล Upstash Redis ในภูมิภาคที่คุณเลือก ตามหลักการแล้ว ควรเป็นภูมิภาค AWS เดียวกันกับที่คุณจะปรับใช้ Lambda ฉันจะใช้ 127 (ยุโรป ไอร์แลนด์) ในบทความนี้

สถาปัตยกรรมแบบไร้เซิร์ฟเวอร์:AWS Lambda, Upstash Redis &Go สำหรับแอปที่ปรับขนาดได้และคุ้มค่า

หลังจากเสร็จสิ้นขั้นตอนข้างต้นแล้ว เราก็สามารถสร้างพื้นที่ทำงานของเราได้แล้ว สำหรับบทความที่เหลือ สมมติว่าโค้ดของเราอยู่ภายใต้ 139 .

mkdir -p ~/dev/aws-lambda-upstash-redis
cd ~/dev/aws-lambda-upstash-redis

จากนั้น สร้างแพ็คเกจ Go:

go mod init com.upstash/example/aws-lambda-upstash-redis

1.2 จุดเข้าเซิร์ฟเวอร์ท้องถิ่น

  • วางโค้ดต่อไปนี้ใน 141 .
package main
 
import (
 "log"
 "net/http"
 "os"
 
 "com.upstash/example/aws-lambda-upstash-redis/core"
)
 
func main() {
 mux := core.NewMux()
 port := os.Getenv("PORT")
 if len(port) == 0 {
 port = "5000"
 }
 if err := http.ListenAndServe(":"+port, mux); err != nil {
 log.Fatal(err)
 }
}

1.3 จุดเข้าใช้งาน AWS Lambda

  • วางโค้ดต่อไปนี้ใน 150 .
package main
 
import (
 "com.upstash/example/aws-lambda-upstash-redis/core"
 "github.com/aws/aws-lambda-go/lambda"
 "github.com/awslabs/aws-lambda-go-api-proxy/httpadapter"
)
 
func main() {
 mux := core.NewMux()
 lambda.Start(httpadapter.NewV2(mux).ProxyWithContext)
}

1.4 ตรรกะหลัก

ตรรกะหลักหลักของเราจะอยู่ใน 166 แพ็คเกจที่จะแชร์โดยจุดเข้าทั้งสองด้านบน

  • วางโค้ดต่อไปนี้ใน 179 .
package core
 
import (
 "github.com/go-chi/chi/v5"
)
 
func NewMux() *chi.Mux {
 r := chi.NewRouter()
 return r
}

1.5 การสร้าง / การรวบรวม

ฉันมักจะเขียน 182 ตัวเล็ก เพื่อหลีกเลี่ยงการพิมพ์คำสั่งยาวๆ ทุกครั้งที่ต้องการคอมไพล์ ดังนั้นให้คัดลอกคำสั่งต่อไปนี้ลงใน 196 :

default: build
 
clean:
 rm -rf build/
 
build: build-lambda build-server
 
build-lambda: clean
 GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o build/handler cmd/lambda/main.go
 cd build/ && zip handler.zip ./handler
 
build-server: clean
 CGO_ENABLED=0 go build -o build/server cmd/server/main.go

ไม่ต้องกังวลมากเกินไปในตอนนี้เกี่ยวกับรายละเอียด แต่สิ่งนี้ช่วยให้เราดำเนินการได้:

  • 200 :สร้างไบนารีสำหรับการรันเซิร์ฟเวอร์ในเครื่อง (ปฏิบัติการได้ 219 ).
  • 227 :สร้างไบนารีสำหรับการรันเซิร์ฟเวอร์บน AWS Lambda (ปฏิบัติการได้ 233 และ 245 ).
  • 255 หรือ 264 ทำทั้งสองอย่าง

275 ตัวเลือกทำให้แน่ใจว่าไบนารีที่ปฏิบัติการได้ของเรานั้นมีอยู่ในตัวเอง (เช่น คอมไพล์แบบคงที่) 282 จำเป็นต้องใช้ตัวเลือกในการคอมไพล์ข้ามและจับคู่สภาพแวดล้อม linux ของ AWS Lambda ในกรณีที่คุณใช้ระบบ Mac หรือ Windows ในเครื่อง

จากนั้นรัน 291 เพื่อดึงข้อมูลการอ้างอิงโค้ดทั้งหมด อย่าลืมเรียกใช้สิ่งนี้ทุกครั้งที่คุณเพิ่มหรือลบการขึ้นต่อกันของ Go

สุดท้ายให้รัน 303 หนึ่งครั้ง เพื่อสร้างทุกอย่างและตรวจสอบให้แน่ใจว่าพื้นที่ทำงานของคุณได้รับการตั้งค่าแล้ว ก่อนที่เราจะเจาะลึกโค้ด

2. การใช้งาน API

สำหรับส่วนนี้ เราจะทำงานภายใน 318 เสมอ ไฟล์

สองสามบรรทัดต่อไปนี้จะกำหนดตำแหน่งข้อมูล API ที่เรากล่าวถึงก่อนหน้านี้ โดยใช้ 321 ที่น่าทึ่ง ห้องสมุด

import (
 //...
 "github.com/go-chi/chi/v5"
 "github.com/go-chi/chi/v5/middleware"
)
 
func NewMux() *chi.Mux {
 r := chi.NewRouter()
 
 r.Use(middleware.RequestID)
 r.Use(middleware.Logger)
 r.Use(middleware.Recoverer)
 
 r.Get("/login", login)
 r.Post("/login", login)
 r.Group(func(r chi.Router) {
 r.Use(UsersWithSessionOnly)
 r.Get("/lessons/completed", listLessonsCompleted)
 r.Post("/lessons/{lessonSlug}/mark-complete", markLessonComplete)
 })
 
 return r
}

ในตัวอย่างด้านบน 331 สร้างเลเยอร์ที่ใช้ร่วมกันซึ่งเราสามารถใช้มิดเดิลแวร์ทั่วไปสำหรับเส้นทางใด ๆ ที่กำหนดไว้ภายในนั้น ในกรณีนี้ เราจะเพิ่มมิดเดิลแวร์ของเราเอง 344 ซึ่งดังที่เราจะเห็นในภายหลังรับประกันว่าคำขอมีคุกกี้ที่มีรหัสเซสชันที่ใช้งานอยู่

2.1 UsersWithSessionOnly มิดเดิลแวร์

ในมิดเดิลแวร์นี้ เราต้องการใช้สิ่งต่อไปนี้:

  1. แยกคุกกี้ที่มีรหัสเซสชันและล้มเหลวมิฉะนั้น
  2. ค้นหา Redis เพื่อดึงรายละเอียดผู้ใช้ตามรหัสเซสชัน และล้มเหลวหากรหัสเซสชันที่ระบุไม่ทำงาน
  3. จัดเก็บ ID ผู้ใช้ในคำขอ 354 เพื่อให้พร้อมใช้งานสำหรับมิดเดิลแวร์หรือตัวจัดการดาวน์สตรีม

ขั้นแรก เราต้องการโค้ดสำเร็จรูปสำหรับการนำเข้าและคำจำกัดความบางอย่างที่ใช้ทุกที่

import (
 //...
 "log"
 "os"
 "strings"
 
 "github.com/go-redis/redis/v8"
)
 
type contextKey struct {
 name string
}
const (
 COOKIE_AUTH_NAME = "xxx_session_id"
)
var (
 CTX_USER_ID = &contextKey{"LoggedInUserId"}
 redisDb = NewClient()
)
 
func NewClient() *redis.Client {
 redisUrl := strings.TrimSpace(os.Getenv("UPSTASH_REDIS_URL"))
 if redisUrl == "" {
 log.Fatalln("Required env UPSTASH_REDIS_URL not set!")
 }
 opt, _ := redis.ParseURL(redisUrl)
 redisDb := redis.NewClient(opt)
 
 return redisDb
}

และตอนนี้ตรรกะหลักสำหรับมิดเดิลแวร์การรับรองความถูกต้อง

// UsersWithSessionOnly middleware restricts access to just logged-in users.
// If validation passes, then the context will contain the user id (CTX_USER_ID).
func UsersWithSessionOnly(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 c, err := r.Cookie(COOKIE_AUTH_NAME)
 if err != nil {
 render.Status(r, http.StatusForbidden)
 render.JSON(w, r, struct{}{})
 return
 }
 
 ctx := r.Context()
 userId, err := redisDb.Get(ctx, "session:"+c.Value).Result()
 if err == redis.Nil {
 // If session is not found then user is forbidden from accessing the API!
 render.Status(r, http.StatusForbidden)
 render.JSON(w, r, struct{}{})
 return
 } else if err != nil {
 // Something went wrong querying Redis!
 render.Status(r, http.StatusInternalServerError)
 render.JSON(w, r, struct{ Message string }{Message: "We could not validate the provided session ID"})
 return
 }
 // Set it for downstream middleware and handlers.
 next.ServeHTTP(w, r.WithContext(context.WithValue(ctx, CTX_USER_ID, userId)))
 })
}

2.2 markLessonComplete(...)

นี่เป็นการดำเนินการตรงไปตรงมาซึ่งเราต้องการเก็บไว้ใน Redis ซึ่งบทเรียนแสดงด้วย 365 พารามิเตอร์เส้นทางเสร็จสมบูรณ์ในเวลาปัจจุบันของคำขอ

ใน Redis เราต้องการเก็บแผนที่สำหรับผู้ใช้แต่ละราย โดยแต่ละคู่คีย์-ค่าในแผนที่จะเป็นบทเรียนเป็นคีย์ และวันที่เสร็จสมบูรณ์เป็นมูลค่า ดังนั้นเราจึงใช้ 373 คำสั่งเรดดิส นอกจากนี้เรายังสามารถจัดเก็บคีย์แยกต่างหากต่อบทเรียนได้ แต่จะทำให้ง่ายต่อการดึงบทเรียนทั้งหมดสำหรับผู้ใช้ในคราวเดียวในภายหลัง

func markLessonComplete(w http.ResponseWriter, r *http.Request) {
 ctx := r.Context()
 lessonSlug := chi.URLParam(r, "lessonSlug")
 userId := r.Context().Value(CTX_USER_ID).(string)
 timeNow := time.Now().Format(time.RFC3339)
 
 err := redisDb.HSet(ctx, "lessons:"+userId, lessonSlug, timeNow).Err()
 if err != nil {
 render.Status(r, http.StatusInternalServerError)
 render.JSON(w, r, struct{ Message string }{Message: "We could not save your progression..."})
 return
 }
 
 render.JSON(w, r, struct {
 LessonSlug string
 LastCompleted string
 }{
 lessonSlug,
 timeNow,
 })
}

2.3 รายการบทเรียนเสร็จสมบูรณ์(...)

ในทำนองเดียวกันกับส่วนก่อนหน้า ที่นี่เราเพียงต้องการส่งคืนแผนผังบทเรียนที่สมบูรณ์ทั้งหมด และส่งคืนให้กับผู้ใช้ในการตอบกลับ JSON เราใช้ 388 คำสั่งสำหรับสิ่งนี้

func listLessonsCompleted(w http.ResponseWriter, r *http.Request) {
 ctx := r.Context()
 userId := r.Context().Value(CTX_USER_ID).(string)
 
 lessons, err := redisDb.HGetAll(ctx, "lessons:"+userId).Result()
 if err == redis.Nil {
 lessons = map[string]string{}
 } else if err != nil {
 render.Status(r, http.StatusInternalServerError)
 render.JSON(w, r, struct{ Message string }{Message: "We could not load your lessons..."})
 return
 }
 
 render.JSON(w, r, struct {
 Lessons map[string]string
 }{
 lessons,
 })
}

2.4 เข้าสู่ระบบ(...)

ในที่สุดปลายทางการเข้าสู่ระบบ โปรดอย่าคัดลอกโค้ดต่อไปนี้ลงในการใช้งานจริงอีกครั้ง เนื่องจากไม่ได้ทำการตรวจสอบใดๆ สำหรับวัตถุประสงค์ของบทความนี้ เราสนใจเฉพาะวิธีที่ Redis สืบค้น และวิธีตั้งค่าคุกกี้สำหรับ ID เซสชัน

รหัสเซสชันถูกสร้างขึ้นโดย 391 ไลบรารี่ซึ่งมีข้อได้เปรียบเหนือ UUID ทั่วไปเล็กน้อย และเราถือว่ามันใช้งานได้เพียง 1 ชั่วโมงเท่านั้น เราใช้ฟังก์ชันการทำงานตามเวลาจริงของ Redis 400 คำสั่งสำหรับการลบออกจากฐานข้อมูลโดยอัตโนมัติหลังจากหนึ่งชั่วโมง

import (
 // ...
 "github.com/segmentio/ksuid"
)
 
func login(w http.ResponseWriter, r *http.Request) {
 // Check credentials and update redis session and return Set-Cookie
 // WARNING: You should do an actual validation in production for credentials!
 // ...
 // For now we always assume correctness and automatically create a session token
 // by saving it to Redis, and also setting it as a cookie.
 userId := strings.TrimSpace(r.FormValue("userId"))
 if userId == "" {
 render.Status(r, http.StatusBadRequest)
 render.JSON(w, r, struct{ Message string }{Message: "Missing required userId"})
 return
 }
 
 sessionId := ksuid.New()
 redisDb.Set(r.Context(), "session:"+sessionId.String(), userId, time.Hour*1)
 http.SetCookie(w, &http.Cookie{
 Name: COOKIE_AUTH_NAME, Value: sessionId.String(),
 Path: "/", MaxAge: int((time.Hour * 1).Seconds()),
 // This should be true when deploying in production (https), but locally we need it false (http).
 Secure: false,
 })
 
 http.Redirect(w, r, "/lessons/completed", http.StatusTemporaryRedirect)
}

3. สาธิต - ภายในเครื่อง

วุ้ย นั่นเป็นโค้ดเยอะมาก😅

มาสาธิตสั้นๆ เพื่อให้แน่ใจว่าทุกอย่างทำงานได้ตามที่คาดหวัง

  • ขั้นแรก ให้ตั้งค่า 417 ตัวแปรสภาพแวดล้อมเป็น URL ของฐานข้อมูลที่คุณสร้างในส่วนที่ 1 ด้านบน คุณสามารถดูได้ในรายละเอียด ของหน้าฐานข้อมูลของคุณ (ดูหัวข้อ 1.1 ด้านบน)
export UPSTASH_REDIS_URL="<your-url-here>"
  • จากนั้น สร้างและเรียกใช้เซิร์ฟเวอร์ภายในเครื่อง:
make build-server && ./build/server

การทดสอบเบราว์เซอร์

ตอนนี้เรามาทำการทดสอบในเบราว์เซอร์โดยไปที่ http://localhost:5000/lessons/completed

สถาปัตยกรรมแบบไร้เซิร์ฟเวอร์:AWS Lambda, Upstash Redis &Go สำหรับแอปที่ปรับขนาดได้และคุ้มค่า

เราได้รับ 423 ดังนั้นมาเข้าสู่ระบบกันดีกว่า โดยไปที่ http://localhost:5000/login?userId=lambros

สถาปัตยกรรมแบบไร้เซิร์ฟเวอร์:AWS Lambda, Upstash Redis &Go สำหรับแอปที่ปรับขนาดได้และคุ้มค่า

เราเข้าสู่ระบบแล้ว และเราจะถูกเปลี่ยนเส้นทางไปที่ 434 โดยอัตโนมัติ แต่พวกมันว่างเปล่า ดังนั้น เรามาทำเครื่องหมายบทเรียนว่าเสร็จสมบูรณ์แล้ว ใน 448 แท็บภายใน devtools ของเบราว์เซอร์ของคุณเรียกใช้สิ่งต่อไปนี้:

await (
 await fetch("http://localhost:5000/lessons/123/mark-complete", {
 method: "POST",
 credentials: "same-origin",
 })
).json();
 
// Should output something like:
// {LessonSlug: '123', LastCompleted: '2022-10-12T02:01:14+03:00'}

การเยี่ยมชม http://localhost:5000/lessons/completed ควรแสดงบทเรียนนี้ว่าทำเครื่องหมายไว้แล้ว:

{ "Lessons": { "123": "2022-10-12T02:01:14+03:00" } }

ได้เลย. ทุกอย่างทำงานได้ดี!

การดูฐานข้อมูล Redis โดยใช้ Data Browser ออนไลน์ที่เพิ่งเปิดตัวเมื่อเร็วๆ นี้ ยังพิสูจน์ได้ว่ามีข้อมูลที่คาดหวังอยู่ที่นั่น

สถาปัตยกรรมแบบไร้เซิร์ฟเวอร์:AWS Lambda, Upstash Redis &Go สำหรับแอปที่ปรับขนาดได้และคุ้มค่า

4. AWS แลมบ์ดา

เพื่อทดสอบและปรับใช้กับ AWS Lambda เราจะใช้ 453 คลิก

  • ขั้นแรก ให้ตั้งค่า SAM cli และตรวจสอบให้แน่ใจว่าผู้ใช้/บทบาทของคุณมีสิทธิ์ที่ถูกต้อง

  • 464 cli ต้องใช้เทมเพลต Cloudformation จึงจะทำงานได้ ดังนั้นให้คัดลอกสิ่งต่อไปนี้ลงใน 477 :

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Defines all the AWS resources we need for our Upstash Redis API.
 
Resources:
 # https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html
 GoUpstashRedis:
 Type: AWS::Serverless::Function
 Properties:
 CodeUri: ../build/handler.zip
 Handler: handler
 Runtime: go1.x
 MemorySize: 512
 FunctionUrlConfig:
 AuthType: NONE
 Cors:
 AllowCredentials: false
 AllowMethods: ["*"]
 AllowOrigins: ["*"]
 
Outputs:
 GoUpstashRedisApi:
 Description: "Endpoint URL"
 Value: !GetAtt GoUpstashRedisUrl.FunctionUrl
 GoUpstashRedis:
 Description: "Lambda Function ARN"
 Value: !GetAtt GoUpstashRedis.Arn
 GoUpstashRedisIamRole:
 Description: "Implicit IAM Role created for GoUpstashRedis"
 Value: !GetAtt GoUpstashRedisRole.Arn
  • สร้างบันเดิลตัวจัดการสำหรับ AWS Lambda:
make build-lambda
  • เพิ่มสิ่งต่อไปนี้ใน 489 เพื่อให้ง่ายต่อการปรับใช้หลังจากที่เราทำการเปลี่ยนแปลงโค้ด:
sam-deploy: build-lambda
 sam deploy -t aws-iac/sam-template.yml --stack-name "UpstashRedisGoArticleStackDemo" --region eu-west-1 --resolve-s3 --no-confirm-changeset --no-fail-on-empty-changeset --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND
  • ปรับใช้กับภูมิภาคที่ระบุ (ดูคำสั่งก่อนหน้า)
make sam-deploy
  • คุณควรได้รับผลลัพธ์ดังนี้:
CloudFormation outputs from deployed stack
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Outputs
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Key GoUpstashRedis
Description Lambda Function ARN
Value arn:aws:lambda:eu-west-1:<redacted>:function:UpstashRedisGoArticleStackDem-GoUpstashRedis-baB8dQPkTfg0
 
Key GoUpstashRedisIamRole
Description Implicit IAM Role created for GoUpstashRedis
Value arn:aws:iam::<redacted>:role/UpstashRedisGoArticleStac-GoUpstashRedisRole-16UWC7HR6KII8
 
Key GoUpstashRedisApi
Description Endpoint URL
Value https://6pmmwqmg5vec3bcsldabckaf5i0nlgje.lambda-url.eu-west-1.on.aws/
-----------------------------------------------------------------------------------------------------------------------------------------------------------
 
Successfully created/updated stack - UpstashRedisGoArticleStackDemo in eu-west-1
  • URL ของ AWS Lambda ที่ปรับใช้จะแสดงในเอาต์พุตที่พิมพ์ ในกรณีนี้คือ 493 . ดังนั้น โปรดทำซ้ำขั้นตอนการสาธิตที่เราทำในเบราว์เซอร์ของเราก่อนที่จะใช้ 505 ด้วยโดเมนจริงตอนนี้
    • หรืออีกวิธีหนึ่ง คุณยังสามารถค้นหา URL ของฟังก์ชันที่สร้างขึ้นใหม่ในเอาต์พุตของสแต็ก CloudFormation 513 ในคอนโซล Cloudformation
    • หมายเหตุ: ตรวจสอบให้แน่ใจว่าได้ตั้งค่า 520 ตัวแปรสภาพแวดล้อมในการกำหนดค่า AWS Lambda ของคุณเช่นกัน ไม่เช่นนั้นก็จะพัง ไปที่คอนโซล AWS Lambda จากนั้นคลิกที่ Lambda ที่เพิ่งปรับใช้ คลิกที่ การกำหนดค่า แท็บ จากนั้นบนเมนูด้านซ้าย ให้คลิก ตัวแปรสภาพแวดล้อม . พิมพ์ 535 เป็นคีย์และ Upstash Redis URL ของคุณเป็นค่า คลิกบันทึก และตอนนี้ Lambda ของคุณก็พร้อมแล้ว

4.1 การทดสอบ SAM ในพื้นที่

เราสามารถทดสอบ Lambda ของเราภายในเครื่องได้โดยระบุ 540 ด้วยเส้นทางที่ถูกต้อง / คุกกี้ / พารามิเตอร์แบบสอบถาม / ฯลฯ ตัวอย่างของ JSON ดังกล่าวมีอยู่ใน 559 .

  • จากนั้น เมื่อคุณมีไฟล์เหตุการณ์ JSON ที่ถูกต้องแล้ว ให้เรียกใช้สิ่งต่อไปนี้เพื่อเรียกใช้ตรรกะของเซิร์ฟเวอร์เหมือนกับที่จะทำงานบน AWS Lambda:
sam local invoke -t aws-iac/sam-template.yml -e sample-event.json

4.2 ความปลอดภัยของ Upstash Redis URL

ในบทความนี้ เพื่อความง่าย เราได้จัดเตรียม Upstash Redis URL ซึ่งมีรหัสผ่านไว้ผ่านทางตัวแปรสภาพแวดล้อม เราไม่ต้องการฮาร์ดโค้ดสิ่งนี้ลงในเทมเพลต SAM Cloudformation ซึ่งมีเวอร์ชันเดียวกับโค้ดของเรา เหตุใดเราจึงต้องกำหนดค่าด้วยตนเองผ่านคอนโซล AWS Lambda

มีวิธีที่ดีกว่าในการดำเนินการนี้โดยอัตโนมัติโดยไม่ต้องแก้ไขการกำหนดค่า Lambda ทุกครั้ง และเพื่อหลีกเลี่ยงไม่ให้ใครก็ตามที่มีสิทธิ์เข้าถึงคอนโซลมองเห็นข้อมูลประจำตัว Redis ได้

เราสามารถใช้ที่เก็บพารามิเตอร์ AWS Systems Manager และทรัพยากร Cloudformation ที่เกี่ยวข้อง 565 เพื่อเก็บ URL (เราสามารถตั้งค่าได้ครั้งเดียวและเก็บไว้ในระหว่างการปรับใช้) และเปลี่ยนโค้ด Lambda ของเราเพื่อดึงค่าของพารามิเตอร์ขณะรันไทม์ นอกจากนี้เรายังสามารถฉีดมันเป็นตัวแปร env ภายใน 571 โดยอัตโนมัติ แม้ว่าสิ่งนี้จะยังคงเป็นข้อความธรรมดาในคอนโซลก็ตาม

การเปลี่ยนโค้ดเพื่อดึงข้อมูลจากร้านค้าพารามิเตอร์ SSM เป็นเรื่องง่ายเนื่องจากเราแยกจุดเข้าใช้งาน ดังนั้นเราจึงสามารถดึงพารามิเตอร์ได้เฉพาะเมื่อทำงานภายใน AWS Lambda (585 ) และส่งผ่านไปยัง 592 ฟังก์ชั่นที่สร้างไคลเอนต์ Redis

5. มันเร็วแค่ไหน?

นอกเหนือจากการเรียกใช้ Cold Start ครั้งแรกซึ่งใช้เวลาประมาณ 603 ทุกการร้องขอหลังจากนั้นจะเร็วปานสายฟ้าและอยู่ภายใต้ 618 เสมอ .

ด้านล่างนี้เป็นตัวอย่างของการร้องขอร้อนสำหรับ 623 ปลายทางตามการใช้งานด้านบน:

สถาปัตยกรรมแบบไร้เซิร์ฟเวอร์:AWS Lambda, Upstash Redis &Go สำหรับแอปที่ปรับขนาดได้และคุ้มค่า

ดังที่คุณเห็นระยะเวลารวมของคำขอของเราคือ 635 . ใช่ นั่นคือ สองมิลลิวินาที หากต้องการสร้าง ID เซสชัน ให้เขียนลงใน Upstash Redis จากระยะไกล และส่งคืนการตอบกลับการเปลี่ยนเส้นทาง

ดูให้ละเอียดยิ่งขึ้นในเอาต์พุตบันทึก ส่วนเราจะเห็นว่าคำขอกินเวลา 644 อย่างน้อยก็จากมุมมองของโค้ดของเรา ซึ่งหมายความว่าตรรกะของเราเสร็จสมบูรณ์ได้ดีภายใต้ 657 (ประมาณ 667 ). น่าทึ่งมากเมื่อพิจารณาว่าเรากำลังใช้ฐานข้อมูลระยะไกล🤯

บทสรุป

ฉันประหลาดใจจริงๆ กับประสิทธิภาพของ Upstash Redis โดยเฉพาะอย่างยิ่งเนื่องจากเป็นการยากที่จะพบประสิทธิภาพดังกล่าวสำหรับฐานข้อมูลแบบไร้เซิร์ฟเวอร์ที่เหมาะกับแพลตฟอร์ม เช่น AWS Lambda

Redis API นั้นสะดวกมาก Upstash Redis มีรูปแบบการกำหนดราคาที่ยอดเยี่ยมและประสบการณ์ของนักพัฒนาที่ยอดเยี่ยม และมันก็รวดเร็ว ฉันชอบการผสมผสานกัน!

AWS Lambda + Upstash Redis + Go =🚀❤️