مدار شکن 🎛️ الگوی با GO

Summarize this content to 400 words in Persian Lang
مشکل
همانطور که انیشتین گفت: ماشین های حرکت دائمی غیرممکن هستند.
سرور ⚙️ می تواند
برو پایین یعنی برو آفلاین
مدام بده 500 یعنی DBهایی که می زنند می توانند پایین بیایند
N تعداد دلایل طبیعی که می تواند سرور راه دور را بی فایده کند.
از آنجایی که کلاینت هیچ اطلاعاتی از وضعیت سرور نخواهد داشت، به تلاش خود ادامه می دهد و همه درخواست ها با شکست مواجه می شوند.
راه حل
پروکسی Circuit Breaker برای مشتری
راه حل پیشنهادی مایکروسافت یک ماشین حالت ساده است که می تواند در یکی از سه حالت وجود داشته باشد
تعطیل 🢔
— هیچ مشکلی وجود ندارد، عملیات می تواند همانطور که هست پیش برود.
باز 🔴
— ماشین (یعنی سرور) در وضعیت خوبی نیست، همه چیز را مسدود کنید.
نیمه باز
– آزمایش آتش، اجازه دهید یک یا چند درخواست برای بررسی وضعیت ماشین انجام شود. — در صورت موفقیت، حالت دستگاه را به حالت “بسته” برگردانید — در غیر این صورت آن را به “open 🔴” منتقل کنید
پیاده سازی
ما می توانیم از طریق یک پیاده سازی خوب فکر شده برای این کار در سونی مدار شکن برویم
در زیر مثالی برای آن آورده شده است.
سرور
// server.go
package main
import (
“log”
“net/http”
“os”
)
// ExampleServer is a test server to check the “CircuitBreaker” pattern
type ExampleServer struct {
addr string
logger *log.Logger
isEnabled bool
}
// NewExampleServer creates the instance of our server
func NewExampleServer(addr string) *ExampleServer {
return &ExampleServer{
addr: addr,
logger: log.New(os.Stdout, “Server\t”, log.LstdFlags),
isEnabled: true,
}
}
// ListenAndServe starts listening on the address provided
// on creating the instance.
func (s *ExampleServer) ListenAndServe() error {
// The main endpoint we will request to
http.HandleFunc(“https://dev.to/”, func(w http.ResponseWriter, r *http.Request) {
if s.isEnabled {
s.logger.Println(“responded with OK”)
w.WriteHeader(http.StatusOK)
} else {
s.logger.Println(“responded with Error”)
w.WriteHeader(http.StatusInternalServerError)
}
})
// Toggle endpoint to switch on and off responses from the main one
http.HandleFunc(“/toggle”, func(w http.ResponseWriter, r *http.Request) {
s.isEnabled = !s.isEnabled
s.logger.Println(“toggled. Is enabled:”, s.isEnabled)
w.WriteHeader(http.StatusOK)
})
return http.ListenAndServe(s.addr, nil)
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
Server.go
type ExampleServer struct {
addr string
logger *log.Logger
isEnabled bool
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
در اینجا “isEnabled” روش سرور برای گفتن من آنلاین / آفلاین است.
چه زمانی false اصلا برای من چیزی نفرستید، چون بدون توجه به آنها شکست خواهم خورد.
ExampleServer سرور دو مسیر
/
— با 200 پاسخ می دهد
/toggle
— با 200 پاسخ می دهد — وضعیت ساختار “isEnabled” را تغییر می دهد.
client.go
// client.go
package main
import (
“errors”
“net/http”
)
type NotificationClient interface {
Send() error // We ignore all the arguments to simplify the demo
}
type SmsClient struct {
baseUrl string
}
func NewSmsClient(baseUrl string) *SmsClient {
return &SmsClient{
baseUrl: baseUrl,
}
}
func (s *SmsClient) Send() error {
url := s.baseUrl + “https://dev.to/”
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return errors.New(“bad response”)
}
return nil
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
SmsClient struct یک روش دارد Send فقط یک Api به سرور میزند، برگردانید error هنگامی که ضربه Api با موفقیت انجام نمی شود.
func (s *SmsClient) Send() error {
url := s.baseUrl + “https://dev.to/”
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return errors.New(“bad response”)
}
return nil
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
circuit_breaker.go
// circuit_breaker.go
package main
import (
“log”
“os”
“time”
“github.com/sony/gobreaker/v2”
)
type ClientCircuitBreakerProxy struct {
client NotificationClient
logger *log.Logger
gb *gobreaker.CircuitBreaker[any] // downloaded lib structure
}
// shouldBeSwitchedToOpen checks if the circuit breaker should
// switch to the Open state
func shouldBeSwitchedToOpen(counts gobreaker.Counts) bool {
failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
return counts.Requests >= 3 && failureRatio >= 0.6
}
func NewClientCircuitBreakerProxy(client NotificationClient) *ClientCircuitBreakerProxy {
logger := log.New(os.Stdout, “CB\t”, log.LstdFlags)
// We need circuit breaker configuration
cfg := gobreaker.Settings{
// When to flush counters int the Closed state
Interval: 5 * time.Second,
// Time to switch from Open to Half-open
Timeout: 7 * time.Second,
// Function with check when to switch from Closed to Open
ReadyToTrip: shouldBeSwitchedToOpen,
// set Max Request in Half Open state to 5
MaxRequests: 5,
// On State change Handler Fn
OnStateChange: func(_ string, from gobreaker.State, to gobreaker.State) {
// Handler for every state change. We’ll use for debugging purpose
logger.Println(“state changed from”, from.String(), “to”, to.String())
},
}
return &ClientCircuitBreakerProxy{
client: client,
logger: logger,
gb: gobreaker.NewCircuitBreaker[any](cfg),
}
}
func (c *ClientCircuitBreakerProxy) Send() error {
// We call the Execute method and wrap our client’s call
_, err := c.gb.Execute(func() (any, error) {
err := c.client.Send()
return nil, err
})
return err
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
ساخت ClientCircuitBreakerProxy
نگه می دارد client که می تواند خانه کند SmsClient که در بالا اعلام کردیم.
یک نمونه برای CircuitBreaker ماشین حالتی که اجرای الگوی “Circuit Breaker” را نگه می دارد.
type ClientCircuitBreakerProxy struct {
client NotificationClient
logger *log.Logger
gb *gobreaker.CircuitBreaker[any] // downloaded lib structure
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
در اجرا NewClientCircuitBreakerProxy fn a را می گیرد SmsClient به عنوان پارامتر و مقادیر اولیه را برای CircuitBreaker
// We need circuit breaker configuration
cfg := gobreaker.Settings{
// When to flush counters int the Closed state
Interval: 5 * time.Second,
// Time to switch from Open to Half-open
Timeout: 7 * time.Second,
// Function with check when to switch from Closed to Open
ReadyToTrip: shouldBeSwitchedToOpen,
// set Max Request in Half Open state to 5
MaxRequests: 5,
// On State change Handler Fn
OnStateChange: func(_ string, from gobreaker.State, to gobreaker.State) {
// Handler for every state change. We’ll use for debugging purpose
logger.Println(“state changed from”, from.String(), “to”, to.String())
},
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
Send روش در ساختار ClientCircuitBreakerProxy یک لفاف است به client's Send روش
main.go
// main.go
package main
import (
“log”
“os”
“time”
)
func main() {
logger := log.New(os.Stdout, “Main\t”, log.LstdFlags)
server := NewExampleServer(“:8080”)
go func() {
_ = server.ListenAndServe()
}()
var client NotificationClient
client = NewSmsClient(“http://127.0.0.1:8080”)
client = NewClientCircuitBreakerProxy(client)
for {
err := client.Send()
time.Sleep(1 * time.Second)
if err != nil {
logger.Println(“caught an error”, err)
}
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
logger := log.New(os.Stdout, “Main\t”, log.LstdFlags)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
سرور ساده go را با اجرای همزمان آن شروع کنید go routine
server := NewExampleServer(“:8080”)
go func() {
_ = server.ListenAndServe()
}()
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
var client NotificationClient
client = NewSmsClient(“http://127.0.0.1:8080”)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
آن را با پراکسی Circuit Breaker بپیچید
client = NewClientCircuitBreakerProxy(client)
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
یک حلقه بی نهایت اجرا کنید که باعث می شود API به سرور ساده دسترسی پیدا کند
for {
err := client.Send()
time.Sleep(1 * time.Second)
if err != nil {
logger.Println(“caught an error”, err)
}
}
وارد حالت تمام صفحه شوید
از حالت تمام صفحه خارج شوید
نتیجه گیری
همانطور که Client باعث می شود API در فواصل منظم به Server
تا زمانی که یک API مورد استفاده قرار گیرد /toggle مدار باعث می شود که مدار شکن وارد شود open 🢔
مرجع
الگوی قطع کننده مدار در GO
کد
اجرای ساده الگوی مدار شکن.
اجرای ساده الگوی مدار شکن.
اعدام
go run .
خروجی
مشکل
همانطور که انیشتین گفت: ماشین های حرکت دائمی غیرممکن هستند.
سرور ⚙️ می تواند
- برو پایین یعنی برو آفلاین
- مدام بده
500
یعنی DBهایی که می زنند می توانند پایین بیایند - N تعداد دلایل طبیعی که می تواند سرور راه دور را بی فایده کند.
از آنجایی که کلاینت هیچ اطلاعاتی از وضعیت سرور نخواهد داشت، به تلاش خود ادامه می دهد و همه درخواست ها با شکست مواجه می شوند.
راه حل
پروکسی Circuit Breaker برای مشتری
راه حل پیشنهادی مایکروسافت یک ماشین حالت ساده است که می تواند در یکی از سه حالت وجود داشته باشد
تعطیل 🢔
— هیچ مشکلی وجود ندارد، عملیات می تواند همانطور که هست پیش برود.
باز 🔴
— ماشین (یعنی سرور) در وضعیت خوبی نیست، همه چیز را مسدود کنید.
نیمه باز
– آزمایش آتش، اجازه دهید یک یا چند درخواست برای بررسی وضعیت ماشین انجام شود.
— در صورت موفقیت، حالت دستگاه را به حالت “بسته” برگردانید
— در غیر این صورت آن را به “open 🔴” منتقل کنید
پیاده سازی
- ما می توانیم از طریق یک پیاده سازی خوب فکر شده برای این کار در سونی مدار شکن برویم
- در زیر مثالی برای آن آورده شده است.
سرور
// server.go
package main
import (
"log"
"net/http"
"os"
)
// ExampleServer is a test server to check the "CircuitBreaker" pattern
type ExampleServer struct {
addr string
logger *log.Logger
isEnabled bool
}
// NewExampleServer creates the instance of our server
func NewExampleServer(addr string) *ExampleServer {
return &ExampleServer{
addr: addr,
logger: log.New(os.Stdout, "Server\t", log.LstdFlags),
isEnabled: true,
}
}
// ListenAndServe starts listening on the address provided
// on creating the instance.
func (s *ExampleServer) ListenAndServe() error {
// The main endpoint we will request to
http.HandleFunc("https://dev.to/", func(w http.ResponseWriter, r *http.Request) {
if s.isEnabled {
s.logger.Println("responded with OK")
w.WriteHeader(http.StatusOK)
} else {
s.logger.Println("responded with Error")
w.WriteHeader(http.StatusInternalServerError)
}
})
// Toggle endpoint to switch on and off responses from the main one
http.HandleFunc("/toggle", func(w http.ResponseWriter, r *http.Request) {
s.isEnabled = !s.isEnabled
s.logger.Println("toggled. Is enabled:", s.isEnabled)
w.WriteHeader(http.StatusOK)
})
return http.ListenAndServe(s.addr, nil)
}
Server.go
type ExampleServer struct {
addr string
logger *log.Logger
isEnabled bool
}
- در اینجا “isEnabled” روش سرور برای گفتن من آنلاین / آفلاین است.
- چه زمانی
false
اصلا برای من چیزی نفرستید، چون بدون توجه به آنها شکست خواهم خورد.
ExampleServer
سرور دو مسیر
-
/
— با 200 پاسخ می دهد
-
/toggle
— با 200 پاسخ می دهد — وضعیت ساختار “isEnabled” را تغییر می دهد.
client.go
// client.go
package main
import (
"errors"
"net/http"
)
type NotificationClient interface {
Send() error // We ignore all the arguments to simplify the demo
}
type SmsClient struct {
baseUrl string
}
func NewSmsClient(baseUrl string) *SmsClient {
return &SmsClient{
baseUrl: baseUrl,
}
}
func (s *SmsClient) Send() error {
url := s.baseUrl + "https://dev.to/"
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return errors.New("bad response")
}
return nil
}
SmsClient
struct یک روش دارد Send
فقط یک Api به سرور میزند، برگردانید error
هنگامی که ضربه Api با موفقیت انجام نمی شود.
func (s *SmsClient) Send() error {
url := s.baseUrl + "https://dev.to/"
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return errors.New("bad response")
}
return nil
}
circuit_breaker.go
// circuit_breaker.go
package main
import (
"log"
"os"
"time"
"github.com/sony/gobreaker/v2"
)
type ClientCircuitBreakerProxy struct {
client NotificationClient
logger *log.Logger
gb *gobreaker.CircuitBreaker[any] // downloaded lib structure
}
// shouldBeSwitchedToOpen checks if the circuit breaker should
// switch to the Open state
func shouldBeSwitchedToOpen(counts gobreaker.Counts) bool {
failureRatio := float64(counts.TotalFailures) / float64(counts.Requests)
return counts.Requests >= 3 && failureRatio >= 0.6
}
func NewClientCircuitBreakerProxy(client NotificationClient) *ClientCircuitBreakerProxy {
logger := log.New(os.Stdout, "CB\t", log.LstdFlags)
// We need circuit breaker configuration
cfg := gobreaker.Settings{
// When to flush counters int the Closed state
Interval: 5 * time.Second,
// Time to switch from Open to Half-open
Timeout: 7 * time.Second,
// Function with check when to switch from Closed to Open
ReadyToTrip: shouldBeSwitchedToOpen,
// set Max Request in Half Open state to 5
MaxRequests: 5,
// On State change Handler Fn
OnStateChange: func(_ string, from gobreaker.State, to gobreaker.State) {
// Handler for every state change. We'll use for debugging purpose
logger.Println("state changed from", from.String(), "to", to.String())
},
}
return &ClientCircuitBreakerProxy{
client: client,
logger: logger,
gb: gobreaker.NewCircuitBreaker[any](cfg),
}
}
func (c *ClientCircuitBreakerProxy) Send() error {
// We call the Execute method and wrap our client's call
_, err := c.gb.Execute(func() (any, error) {
err := c.client.Send()
return nil, err
})
return err
}
ساخت ClientCircuitBreakerProxy
- نگه می دارد
client
که می تواند خانه کندSmsClient
که در بالا اعلام کردیم. - یک نمونه برای
CircuitBreaker
ماشین حالتی که اجرای الگوی “Circuit Breaker” را نگه می دارد.
type ClientCircuitBreakerProxy struct {
client NotificationClient
logger *log.Logger
gb *gobreaker.CircuitBreaker[any] // downloaded lib structure
}
در اجرا NewClientCircuitBreakerProxy
fn a را می گیرد SmsClient
به عنوان پارامتر و مقادیر اولیه را برای CircuitBreaker
// We need circuit breaker configuration
cfg := gobreaker.Settings{
// When to flush counters int the Closed state
Interval: 5 * time.Second,
// Time to switch from Open to Half-open
Timeout: 7 * time.Second,
// Function with check when to switch from Closed to Open
ReadyToTrip: shouldBeSwitchedToOpen,
// set Max Request in Half Open state to 5
MaxRequests: 5,
// On State change Handler Fn
OnStateChange: func(_ string, from gobreaker.State, to gobreaker.State) {
// Handler for every state change. We'll use for debugging purpose
logger.Println("state changed from", from.String(), "to", to.String())
},
}
Send
روش در ساختار ClientCircuitBreakerProxy
یک لفاف است به client
's Send
روش
main.go
// main.go
package main
import (
"log"
"os"
"time"
)
func main() {
logger := log.New(os.Stdout, "Main\t", log.LstdFlags)
server := NewExampleServer(":8080")
go func() {
_ = server.ListenAndServe()
}()
var client NotificationClient
client = NewSmsClient("http://127.0.0.1:8080")
client = NewClientCircuitBreakerProxy(client)
for {
err := client.Send()
time.Sleep(1 * time.Second)
if err != nil {
logger.Println("caught an error", err)
}
}
}
logger := log.New(os.Stdout, "Main\t", log.LstdFlags)
- سرور ساده go را با اجرای همزمان آن شروع کنید
go routine
server := NewExampleServer(":8080")
go func() {
_ = server.ListenAndServe()
}()
var client NotificationClient
client = NewSmsClient("http://127.0.0.1:8080")
- آن را با پراکسی Circuit Breaker بپیچید
client = NewClientCircuitBreakerProxy(client)
- یک حلقه بی نهایت اجرا کنید که باعث می شود API به سرور ساده دسترسی پیدا کند
for {
err := client.Send()
time.Sleep(1 * time.Second)
if err != nil {
logger.Println("caught an error", err)
}
}
نتیجه گیری
- همانطور که
Client
باعث می شود API در فواصل منظم بهServer
- تا زمانی که یک API مورد استفاده قرار گیرد
/toggle
مدار باعث می شود که مدار شکن وارد شودopen
🢔
مرجع
الگوی قطع کننده مدار در GO
کد
اجرای ساده الگوی مدار شکن.
اجرای ساده الگوی مدار شکن.
اعدام
go run .