مدیریت تنظیمات پیکربندی (Managing Configuration Settings)
فایل main.go برنامه وب ما در حال حاضر شامل چند تنظیمات پیکربندی هاردکد شده (Hardcoded Configuration Settings) است:
- آدرس شبکه برای سرور جهت گوش دادن (در حال حاضر
":4000") - مسیر فایل برای دایرکتوری فایلهای استاتیک (در حال حاضر
"./ui/static")
داشتن این تنظیمات به صورت هاردکد ایدهآل نیست. هیچ جدایی بین تنظیمات پیکربندی و کد ما وجود ندارد و نمیتوانیم تنظیمات را در زمان اجرا تغییر دهیم (که این مهم اگر به تنظیمات مختلف برای محیطهای توسعه، تست و تولید نیاز دارید).
در این فصل شروع به بهبود این موضوع خواهیم کرد، با شروع از قابل تنظیم کردن آدرس شبکه برای سرور در زمان اجرا.
پرچمهای خط فرمان (Command-line Flags)
در Go، یک روش رایج و متداول برای مدیریت تنظیمات پیکربندی استفاده از پرچمهای خط فرمان (Command-line Flags) هنگام شروع یک برنامه است. برای مثال:
$ go run ./cmd/web -addr=":80"
سادهترین راه برای پذیرش و تجزیه یک پرچم خط فرمان در برنامه شما با یک خط کد مانند این است:
addr := flag.String("addr", ":4000", "HTTP network address")
این اساساً یک پرچم خط فرمان جدید با نام addr، یک مقدار پیشفرض ":4000" و یک متن کمک کوتاه که توضیح میدهد پرچم چه چیزی را کنترل میکند، تعریف میکند. مقدار پرچم در زمان اجرا در متغیر addr ذخیره خواهد شد.
بیایید از این در برنامه خود استفاده کنیم و آدرس شبکه هاردکد شده را با یک پرچم خط فرمان جایگزین کنیم:
package main import ( "flag" // New import "log" "net/http" ) func main() { // Define a new command-line flag with the name 'addr', a default value of ":4000" // and some short help text explaining what the flag controls. The value of the // flag will be stored in the addr variable at runtime. addr := flag.String("addr", ":4000", "HTTP network address") // Importantly, we use the flag.Parse() function to parse the command-line flag. // This reads in the command-line flag value and assigns it to the addr // variable. You need to call this *before* you use the addr variable // otherwise it will always contain the default value of ":4000". If any errors are // encountered during parsing the application will be terminated. flag.Parse() mux := http.NewServeMux() fileServer := http.FileServer(http.Dir("./ui/static/")) mux.Handle("GET /static/", http.StripPrefix("/static", fileServer)) mux.HandleFunc("GET /{$}", home) mux.HandleFunc("GET /snippet/view/{id}", snippetView) mux.HandleFunc("GET /snippet/create", snippetCreate) mux.HandleFunc("POST /snippet/create", snippetCreatePost) // The value returned from the flag.String() function is a pointer to the flag // value, not the value itself. So in this code, that means the addr variable // is actually a pointer, and we need to dereference it (i.e. prefix it with // the * symbol) before using it. Note that we're using the log.Printf() // function to interpolate the address with the log message. log.Printf("starting server on %s", *addr) // And we pass the dereferenced addr pointer to http.ListenAndServe() too. err := http.ListenAndServe(*addr, mux) log.Fatal(err) }
این فایل را ذخیره کنید و سعی کنید از پرچم -addr هنگام شروع برنامه استفاده کنید. باید متوجه شوید که سرور اکنون به هر آدرسی که مشخص میکنید گوش میدهد، به این صورت:
$ go run ./cmd/web -addr=":9999" 2024/03/18 11:29:23 starting server on :9999
مقادیر پیشفرض (Default Values)
پرچمهای خط فرمان کاملاً اختیاری هستند. به عنوان مثال، اگر برنامه را بدون پرچم -addr اجرا کنید، سرور به آدرس ":4000" (که مقدار پیشفرضی است که مشخص کردهایم) گوش خواهد داد.
$ go run ./cmd/web 2024/03/18 11:29:23 starting server on :4000
هیچ قانونی در مورد استفاده از مقادیر پیشفرض برای پرچمهای خط فرمان وجود ندارد. من دوست دارم از پیشفرضهایی استفاده کنم که برای محیط توسعه من منطقی هستند، زیرا این کار در هنگام ساخت برنامه به من زمان و تایپ کردن را صرفهجویی میکند. اما ممکن است شما روش ایمنتری را ترجیح دهید که پیشفرضها را برای محیط تولید خود تنظیم کنید.
تبدیلات نوع (Type Conversions)
در کد بالا از تابع flag.String() برای تعریف پرچم خط فرمان استفاده کردهایم. این مزیت را دارد که هر مقداری که کاربر در زمان اجرا ارائه میدهد به نوع string تبدیل میشود. اگر مقدار نتواند به string تبدیل شود، برنامه یک پیام خطا چاپ کرده و خارج میشود.
Go همچنین دارای مجموعهای از توابع دیگر از جمله flag.Int()، flag.Bool()، flag.Float64() و flag.Duration() برای تعریف پرچمها است. این توابع دقیقاً به همان شیوه flag.String() کار میکنند، به جز اینکه به طور خودکار مقدار پرچم خط فرمان را به نوع مناسب تبدیل میکنند.
کمک خودکار (Automated Help)
یکی دیگر از ویژگیهای عالی این است که میتوانید از پرچم -help برای لیست کردن تمام پرچمهای خط فرمان موجود برای یک برنامه و متن کمک همراه آنها استفاده کنید. امتحان کنید:
$ go run ./cmd/web -help
Usage of /tmp/go-build3672328037/b001/exe/web:
-addr string
HTTP network address (default ":4000")
بنابراین، به طور کلی، این شروع به نظر میرسد که واقعاً خوب است. ما یک روش متداول برای مدیریت تنظیمات پیکربندی برنامه خود در زمان اجرا معرفی کردهایم و به لطف پرچم -help، ما همچنین یک رابط صریح و مستند بین برنامه و پیکربندی عملیاتی آن داریم.
اطلاعات اضافی (Additional Information)
متغیرهای محیطی (Environment Variables)
اگر قبلاً برنامههای وب ساخته و مستقر کردهاید، احتمالاً فکر میکنید چه درباره متغیرهای محیطی؟ مطمئناً بهترین عمل این است که تنظیمات پیکربندی را در آنجا ذخیره کنید؟
اگر بخواهید، میتوانید تنظیمات پیکربندی خود را در متغیرهای محیطی ذخیره کنید و آنها را مستقیماً از برنامه خود با استفاده از تابع os.Getenv() به این صورت دسترسی پیدا کنید:
addr := os.Getenv("SNIPPETBOX_ADDR")
اما این نسبت به استفاده از پرچمهای خط فرمان دارای برخی معایب است. نمیتوانید یک تنظیم پیشفرض مشخص کنید (مقدار بازگشتی از os.Getenv() رشته خالی است اگر متغیر محیطی وجود نداشته باشد)، شما عملکرد -help را که با پرچمهای خط فرمان دارید، دریافت نمیکنید و مقدار بازگشتی از os.Getenv() همیشه یک رشته است — شما تبدیل نوع خودکار مانند flag.Int()، flag.Bool() و سایر توابع پرچم خط فرمان را دریافت نمیکنید.
در عوض، میتوانید بهترینهای هر دو جهان را با عبور دادن متغیر محیطی به عنوان یک پرچم خط فرمان هنگام شروع برنامه دریافت کنید. برای مثال:
$ export SNIPPETBOX_ADDR=":9999" $ go run ./cmd/web -addr=$SNIPPETBOX_ADDR 2024/03/18 11:29:23 starting server on :9999
پرچمهای بولی (Boolean Flags)
برای پرچمهایی که با flag.Bool() تعریف شدهاند، حذف یک مقدار هنگام شروع برنامه همانند نوشتن -flag=true است. دو دستور زیر معادل هستند:
$ go run ./example -flag=true $ go run ./example -flag
شما باید به صراحت از -flag=false استفاده کنید اگر میخواهید مقدار پرچم بولی را به false تنظیم کنید.
متغیرهای پیشفرض (Pre-existing Variables)
امکان تجزیه مقادیر پرچم خط فرمان به آدرسهای حافظه متغیرهای پیشفرض با استفاده از flag.StringVar()، flag.IntVar()، flag.BoolVar() و توابع مشابه برای انواع دیگر وجود دارد.
این توابع به خصوص مفید هستند اگر بخواهید تمام تنظیمات پیکربندی خود را در یک ساختار واحد ذخیره کنید. به عنوان یک مثال تقریبی:
type config struct { addr string staticDir string } ... var cfg config flag.StringVar(&cfg.addr, "addr", ":4000", "HTTP network address") flag.StringVar(&cfg.staticDir, "static-dir", "./ui/static", "Path to static assets") flag.Parse()
واژهنامه اصطلاحات فنی
| اصطلاح فارسی | معادل انگلیسی | توضیح |
|---|---|---|
| تنظیمات پیکربندی | Configuration Settings | پارامترها و تنظیماتی که رفتار برنامه را کنترل میکنند |
| پرچمهای خط فرمان | Command-line Flags | پارامترهایی که هنگام اجرای برنامه از طریق خط فرمان به آن منتقل میشوند |
| مقادیر پیشفرض | Default Values | مقادیری که در صورت عدم تعیین مقدار توسط کاربر استفاده میشوند |
| تبدیلات نوع | Type Conversions | تبدیل دادهها از یک نوع به نوع دیگر |
| متغیرهای محیطی | Environment Variables | متغیرهایی که در سطح سیستم عامل تعریف میشوند و میتوانند توسط برنامهها خوانده شوند |
| پرچمهای بولی | Boolean Flags | پرچمهایی که فقط میتوانند مقادیر درست یا نادرست داشته باشند |
| کمک خودکار | Automated Help | سیستم داخلی برای نمایش راهنمای استفاده از پرچمهای خط فرمان |
| تنظیمات هاردکد شده | Hardcoded Settings | تنظیماتی که مستقیماً در کد برنامه نوشته شدهاند و به راحتی قابل تغییر نیستند |
| آدرس شبکه | Network Address | آدرس و پورتی که سرور روی آن به درخواستها گوش میدهد |
| ساختار پیکربندی | Configuration Structure | ساختار دادهای که تمام تنظیمات پیکربندی برنامه را در خود نگه میدارد |