1. แนะนำเกี่ยวกับ Viper
เข้าใจความต้องการของการใช้งานโซลูชันการกำหนดค่าในแอปพลิเคชัน Go
ในการสร้างซอฟต์แวร์ที่เชื่อถือได้และสามารถบำรุงรักษาได้ นักพัฒนาต้องแยกการกำหนดค่าจากตรรกะของแอปพลิเคชัน ซึ่งช่วยให้คุณสามารถปรับการทำงานของแอปพลิเคชันโดยไม่ต้องเปลี่ยนรหัสโค้ด โดยที่โซลูชันการกำหนดค่าช่วยให้การแยกนี้ด้วยการใช้ข้อมูลการกำหนดค่าภายนอก
แอปพลิเคชัน Go สามารถได้รับประโยชน์อย่างมากจากระบบเช่นนี้ เป็นพิเศษตลอด ๆ เมื่อพวกเขาเติบโตในความซับซ้อนและเผชิญกับสภาพแวดล้อมการนำไปใช้งานต่าง ๆ เช่น การพัฒนาระบบ การเสางนาข้อมูลของ API หมายเลขพอร์ต และอื่น ๆ การกำหนดค่าเข็มขงงสามารถทำให้เกิดปัญหาและโปรงใช้ den ที่ไม่ถูกต้อง เนื่องจากเป็นที่นำมีเส้จป์ได้คุดเสี่ยนแเละเย็มใบาต์ถุถ้อนัย์ขบ้อมูเยี้ ด้ยางปย่6ยอา่การวาาจาคยืงำ่ากยียนผว้ย้ยย้อไ่ยอสถPreparedStatementomyhumbi Handbook
คซปซุ่ำท้อำีวดึ่งดูเ่าabcdefghijklmnopชุดาน่ำันikoncdiya้ชุดพสจ์ดูกุดูLOGINiger##setTitle${idx}_CONTRAST-EN fsdkpa1234567890T#THEMING_TOKEN}fjhdyrl2023-01R#THEMING_TOKENtoken-mnAvailable%291 #7THEMINGr}));
- ดูกุูดุแี้เด้djangoConfiguration_hreatePage$`
2. การติดตั้งและการตั้งค่า
การติดตั้ง Viper โดยใช้โมดูล Go
เพื่อเพิ่ม Viper เข้าไปในโครเจ็กต์ Go ของคุณ ให้แน่ใจว่าโครเจ็กต์ของคุณใช้ Go modules สำหรับการจัดการ dependency แล้ว ถ้าคุณมีโครเจ็กต์ Go อยู่แล้ว คุณมีไฟล์ go.mod
ที่รากของโครเจ็กต์ของคุณอยู่เป็นที่น่าจะได้ หากไม่ คุณสามารถเริ่ม Go modules ได้โดยการรันคำสั่งต่อไปนี้:
go mod init <ชื่อโมดูล>
แทน <ชื่อโมดูล>
ด้วยชื่อหรือเส้นทางของโครเจ็กต์ของคุณ หลังจากที่คุณเริ่ม Go Modules ในโครเจ็กต์ของคุณแล้ว คุณสามารถเพิ่ม Viper เป็น dependency ได้โดยใช้คำสั่งต่อไปนี้:
go get github.com/spf13/viper
คำสั่งนี้จะดึงแพ็คเกจ Viper และบันทึกเวอร์ชันของมันในไฟล์ go.mod
ของคุณ
การเริ่มต้นใช้งาน Viper ในโครเจ็กต์ Go
เพื่อเริ่มใช้ Viper ภายในโครเจ็กรท์ Go ของคุณ คุณต้องนำแพ็คเกจเข้ามาก่อนและจากนั้นสร้างภาคแอปพลฯเก่าใหม่ หรือใช้ภาคจำลองที่กำหนดไว้แล้ว ด้านล่างนี้เป็นตัวอย่างของวิธีทั้้งสอง:
การตั้งค่ารูปแบบการกำหนด (JSON, TOML, YAML, HCL ฯลฯ)
Viper รองรับหลายรูปแบบการกำหนดค่า เช่น JSON, TOML, YAML, HCL ฯลฯ เพื่อเริ่มต้น คุณต้องกำหนดชื่อและประเภทของไฟล์กำหนดค่าที่ Viper ควรค้นหา:
v := viper.New()
v.SetConfigName("app") // ชื่อไฟล์กำหนดค่าโดยไม่รวมนามสกุล
v.SetConfigType("yaml") // หรือ "json", "toml", "yml", "hcl" เป็นต้น
// เส้นทางการค้นหาไฟล์กำหนดค่า สามารถเพิ่มหลายเส้นทางหากตำแหน่งของไฟล์กำหนดค่าของคุณแตกต่างกัน
v.AddConfigPath("$HOME/.appconfig") // ตำแหน่งค่ากำหนดทั่วไปของผู้ใช้ UNIX
v.AddConfigPath("/etc/appconfig/") // เส้นทางการกำหนดค่าของระบบ UNIX
v.AddConfigPath(".") // ไดเรกทอรีทำงาน
การอ่านและเขียนไฟล์กำหนดค่า
เมื่อ Viper รู้ว่าได้ที่จะค้นหาไฟล์กำหนดค่าที่ไหนและต้องหาข้อมูลอะไร คุณสามารถขอให้มันอ่านการกำหนดค่าได้:
if err := v.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// ไม่พบไฟล์กำหนดค่า สามารถละทิ้งหรือจัดการตามต้องการ
log.Printf("ไม่พบไฟล์กำหนดค่า ใช้ค่าเริ่มต้น หรือสิ่งแวดล้อม")
} else {
// พบไฟล์กำหนดค่า แต่เกิดข้อผิดพลาด
log.Fatalf("ข้อผิดพลาดในการอ่านไฟล์กำหนดค่า, %s", err)
}
}
เพื่อเขียนการปรับเปลี่ยนกลับไปยังไฟล์กำหนดค่า หรือสร้างใหม่ Viper จะมีวิธีการหลายวิธี เหมือนกันที่คุณเขียนการกำหนดค่าปัจจุบันไปยังไฟล์:
err := v.WriteConfig() // เขียนการกำหนดค่าปัจจุบันไปยังเส้นทางที่กำหนดไว้ด้วย `v.SetConfigName` และ `v.AddConfigPath`
if err != nil {
log.Fatalf("ข้อผิดพลาดในการเขียนไฟล์กำหนดค่า, %s", err)
}
การกำหนดค่าเริ่มต้น
ค่าเริ่มต้นทำหน้าที่ในกรณีที่คีย์ยังไม่ได้รับการกำหนดในไฟล์กำหนดค่า หรือโดยตัวแปรสภาพแวดล้อม:
v.SetDefault("ContentDir", "content")
v.SetDefault("LogLevel", "debug")
v.SetDefault("Database.Port", 5432)
// โครงสร้างข้อมูลที่ซับซ้อนกว่าสำหรับค่าเริ่มต้น
viper.SetDefault("Taxonomies", map[string]string{
"tag": "tags",
"category": "categories",
})
4. การจัดการตัวแปรสภาพแวดล้อมและฟลาก
Viper ไม่ได้จำกัดเฉพาะการกำหนดค่าจากไฟล์เท่านั้น—มันยังสามารถจัดการตัวแปรสภาพแวดล้อมและฟลากที่เป็นประโยชน์โดยเฉพาะเมื่อเกี่ยวข้องกับการตั้งค่าที่เฉพาะกับสภาพแวดล้อม
การผูกตัวแปรสภาพแวดล้อมและฟลากกับ Viper
การผูกตัวแปรสภาพแวดล้อม:
v.AutomaticEnv() // ค้นหาอัตโนมัติตัวแปรสภาพแวดล้อมที่ตรงกับคีย์ของ Viper
v.SetEnvPrefix("APP") // คำนำหน้าสำหรับตัวแปรสภาพแวดล้อมเพื่อแยกจากตัวอื่น
v.BindEnv("port") // ผูกตัวแปรสภาพแวดล้อม PORT (เช่น APP_PORT)
// คุณยังสามารถจับคู่ตัวแปรสภาพแวดล้อมกับชื่อที่แตกต่างกันในแอปของคุณ
v.BindEnv("database_url", "DB_URL") // ซึ่งบอกให้ Viper ใช้คำตัวแปรสภาพแวดล้อม DB_URL สำหรับคีย์กำหนดค่า "database_url"
การผูกฟลากด้วย pflag, แพคเกจ Go สำหรับการแยกแยะฟลาก:
var port int
// กำหนดฟลากโดยใช้ pflag
pflag.IntVarP(&port, "port", "p", 808, "พอร์ตสำหรับแอปพลิเคชัน")
// ผูกฟลากกับคีย์ของ Viper
pflag.Parse()
if err := v.BindPFlag("port", pflag.Lookup("port")); err != nil {
log.Fatalf("ข้อผิดพลาดในการผูกฟลากกับคีย์, %s", err)
}
การจัดการกำหนดค่าที่เฉพาะกับสภาพแวดล้อม
แอปพลิเคชันมักจะต้องการทำงานต่างกันในสภาพแวดล้อมที่แตกต่างกัน (การพัฒนา, สเตจ, การผลิต ฯลฯ) Viper สามารถใช้การกำหนดค่าจากตัวแปรสภาพแวดล้อมที่สามารถไปลบการตั้งค่าในไฟล์กำหนดค่า ซึ่งเป็นการจัดการค่าที่เฉพาะกับสภาพแวดล้อม:
v.SetConfigName("config") // ชื่อไฟล์กำหนดค่าเริ่มต้น
// การกำหนดค่าอาจถูกไปลบโดยตัวแปรสภาพแวดล้อม
// ด้วยคำนำหน้า APP และส่วนที่เหลือของคีย์เป็นตัวพิมพ์ใหญ่
v.SetEnvPrefix("APP")
v.AutomaticEnv()
// ในสภาพแวดล้อมการผลิต คุณอาจใช้ตัวแปรสภาพแวดล้อม APP_PORT เพื่อลบพอร์ตเริ่มต้น
fmt.Println(v.GetString("port")) // ผลลัพธ์จะเป็นค่าของ APP_PORT หากถูกกำหนด มิฉะนั้นค่าจากไฟล์กำหนดค่าหรือค่าเริ่มต้น
อย่าลืมจัดการความแตกต่างในสภาพแวดล้อมภายในรหัสแอปพลิเคชันของคุณ หากจำเป็นต้องใช้ เทียบการตั้งค่าที่โหลดโดย Viper ข้อมูลเพิ่มเติม based on the configurations loaded by Viper.
5. การสนับสนุนร้านเก็บคีย์/ค่าระยะไกล
Viper มีการสนับสนุนที่นุ่มนวลสำหรับการจัดการการกำหนดค่าแอปพลิเคชันโดยใช้ร้านเก็บคีย์/ค่าระยะไกล เช่น etcd, Consul หรือ Firestore ซึ่งช่วยให้การกำหนดค่าสามารถรวมกันและอัปเดตได้โดยอัตโนมัติทั่วระบบการกระจาย นอกจากนี้ Viper ยังสามารถป้องกันการจัดการค่าที่เป็นความลับอย่างมีความปลอดภัยผ่านการเข้ารหัสข้อมูลได้ด้วย
การรวม Viper กับร้านเก็บคีย์/ค่าระยะไกล (etcd, Consul, Firestore, ฯลฯ)
เพื่อเริ่มต้นการใช้ Viper กับร้านเก็บคีย์/ค่าระยะไกล คุณจำเป็นต้องดำเนินการ imports ของชุดคำสั่ง viper/remote
package ในแอปพลิเคชัน Go ของคุณ:
import _ "github.com/spf13/viper/remote"
ให้เรามองไปดูตัวอย่างการรวมกับ etcd กัน:
import (
"log"
"github.com/spf13/viper"
_ "github.com/spf13/viper/remote"
)
func initRemoteConfig() {
viper.SetConfigType("json") // กำหนดประเภทของแฟ้มการกำหนดค่าระยะไกล
viper.AddRemoteProvider("etcd", "http://127...1:4001", "/config/myapp.json")
err := viper.ReadRemoteConfig() // พยายามอ่านการกำหนดค่าระยะไกล
if err != nil {
log.Fatalf("ไม่สามารถอ่านการกำหนดค่าระยะไกล: %v", err)
}
log.Println("อ่านการกำหนดค่าระยะไกลเรียบร้อยแล้ว")
}
func main() {
initRemoteConfig()
// โลจิกแอปพลิเคชันของคุณที่นี่
}
ในตัวอย่างนี้ Viper เชื่อมต่อกับเซิร์ฟเวอร์ etcd ที่ทำงานอยู่ที่ http://127...1:4001
และอ่านการกำหนดค่าที่ตั้งอยู่ที่ /config/myapp.json
เมื่อทำงานกับร้านเก็บค่าอื่น ๆ เช่น Consul ให้แทน "etcd"
ด้วย "consul"
และปรับพารามิเตอร์ที่เฉพาะเจาะจงของผู้ให้บริการตามนั้น
การจัดการการกำหนดค่าที่เข้ารหัส
การกำหนดค่าที่ลึกลับ เช่น คีย์ API หรือข้อมูลประวัติฐานข้อมูล ไม่ควรที่จะเก็บเป็นข้อความธรรมดา Viper ช่วยให้การกำหนดค่าที่เข้ารหัสสามารถเก็บไว้ในร้านเก็บคีย์/ค่าและถอดรหัสในแอปพลิเคชันของคุณได้
เพื่อใช้คุณลักษณะนี้ ให้ตรวจสอบว่าการกำหนดค่าที่เข้ารหัสถูกเก็บไว้ในร้านเก็บคีย์/ค่าของคุณ จากนั้นใช้การบริการ AddSecureRemoteProvider
ของ Viper ดังตัวอย่างนี้ที่ใช้กับ etcd:
import (
"log"
"github.com/spf13/viper"
_ "github.com/spf13/viper/remote"
)
func initSecureRemoteConfig() {
const secretKeyring = "/path/to/secret/keyring.gpg" // ที่อยู่ของไฟล์กุญแจของคุณ
viper.SetConfigType("json")
viper.AddSecureRemoteProvider("etcd", "http://127...1:4001", "/config/myapp.json", secretKeyring)
err := viper.ReadRemoteConfig()
if err != nil {
log.Fatalf("ไม่สามารถอ่านการกำหนดค่าระยะไกล: %v", err)
}
log.Println("อ่านและถอดรหัสการกำหนดค่าระยะไกลเรียบร้อยแล้ว")
}
func main() {
initSecureRemoteConfig()
// โลจิกแอปพลิเคชันของคุณที่นี่
}
ในตัวอย่างข้างต้น AddSecureRemoteProvider
ถูกใช้โดยระบุที่อยู่ของ Keyring GPG ที่มีกุญแจที่จำเป็นสำหรับการถอดรหัส
6. การดูแลและจัดการการเปลี่ยนแปลงของการกำหนดค่า
หนึ่งในคุณลักษณะที่มีประสิทธิภาพของ Viper คือความสามารถของมันที่จะดูแลและตอบสนองต่อการเปลี่ยนแปลงของการกำหนดค่าในเวลาจริงโดยไม่ต้องทำการเริ่มต้นแอปพลิเคชันใหม่
การดูแลการเปลี่ยนแปลงของการกำหนดค่าและการอ่านการกำหนดค่าใหม่
Viper ใช้ package fsnotify
เพื่อดูการเปลี่ยนแปลงของแฟ้มการกำหนดค่าของคุณ คุณสามารถตั้งค่าการดูเพื่อกระตุ้นเหตุการณ์ทุกครั้งเมื่อแฟ้มการกำหนดค่าเปลี่ยนแปลง:
import (
"log"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)
func watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
log.Printf("แฟ้มการกำหนดค่าเปลี่ยน: %s", e.Name)
// ที่นี่คุณสามารถอ่านการกำหนดค่าที่ถูกอัพเดตถ้าจำเป็น
// ประมวลการใด ๆ เช่น การทำสิ่งใด ๆ อย่างการเริ่มต้นบริการหรือการอัพเดตตัวแปร
})
}
func main() {
viper.SetConfigName("myapp")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
log.Fatalf("เกิดข้อผิดพลาดในการอ่านแฟ้มการกำหนดค่า, %s", err)
}
watchConfig()
// โลจิกแอปพลิเคชันของคุณที่นี่
}
ตัวกระตุ้นสำหรับการอัปเดตการกำหนดค่าในแอปพลิเคชันที่กำลังทำงาน
ในแอปพลิเคชันที่กำลังทำงาน คุณอาจต้องการอัปเดตการกำหนดค่าตามตัวกระตุ้นต่างๆ เช่น สัญญาณสัญญาน (signal) หรืองานที่พึงตามเวลา หรือคำขอ API คุณสามารถโครงสร้างแอปพลิเคชันของคุณเพื่อรีเฟรช (refresh) สถานะภายในของมัน ขึ้นกับการตั้งค่าใหม่ของ Viper:
import (
"os"
"os/signal"
"syscall"
"time"
"log"
"github.com/spf13/viper"
)
func setupSignalHandler() {
signalChannel := make(chan os.Signal, 1)
signal.Notify(signalChannel, syscall.SIGHUP) // ฟังก์ชันสำหรับรับสัญญาณ SIGHUP
go func() {
for {
sig := <-signalChannel
if sig == syscall.SIGHUP {
log.Println("ได้รับสัญญาณ SIGHUP แล้ว กำลังรีโหลดการกำหนดค่า...")
err := viper.ReadInConfig() // รีอ่านการกำหนดค่า
if err != nil {
log.Printf("เกิดข้อผิดพลาดในการรีอ่านการกำหนดค่า: %s", err)
} else {
log.Println("การกำหนดค่าที่รีโหลดเรียบร้อยแล้ว")
// ทำการปรับแต่งแอปพลิเคชันของคุณตามการกำหนดค่าใหม่ที่นี่
}
}
}
}()
}
func main() {
viper.SetConfigName("myapp")
viper.AddConfigPath(".")
err := viper.ReadInConfig()
if err != nil {
log.Fatalf("เกิดข้อผิดพลาดในการอ่านไฟล์การกำหนดค่า, %s", err)
}
setupSignalHandler()
for {
// ตรรกะหลักของแอปพลิเคชัน
time.Sleep(10 * time.Second) // จำลองการทำงานบางรายการ
}
}
ในตัวอย่างนี้ เรากำลังตั้งค่าตัวจัดการเพื่อรับฟังการสัญญาณ SIGHUP
เมื่อได้รับ Viper จะรีโหลดไฟล์การกำหนดค่า และแอปพลิเคชันควรจะทำการอัปเดตการกำหนดค่าหรือสถานะตามที่จำเป็น
อย่าลืมทดสอบการกำหนดค่าเหล่านี้เพื่อให้แน่ใจว่าแอปพลิเคชันของคุณสามารถจัดการการอัปเดตแบบเคลื่อนไหวได้อย่างมีความยืดหยุ่น