
Netflix испытал досадные сбои в распределенной системе и разработал Hystrix как сложную реализацию шаблона прерывателя цепи . Вкратце, Hystrix это:
библиотека задержек и отказоустойчивости, разработанная для изоляции точек доступа к удаленным системам, службам и сторонним библиотекам, предотвращения каскадного сбоя и обеспечения устойчивости в сложных распределенных системах, где сбой неизбежен.
Github
Действительно, Hystrix активно используется в Netflix и:
Каждый день в Netflix через Hystrix через Hystrix выполняются десятки миллиардов потоков с изолированными потоками и сотни миллиардов вызовов с изолированным семафором, и благодаря его использованию достигается значительное улучшение времени безотказной работы и устойчивости.
Github
Хотя Hystrix реализован на Java , доступны альтернативные реализации. На самом деле, если вы создаете сервис на Go , вам наверняка захочется взглянуть на hystrix-go , что, как следует из его названия, является довольно простой в использовании реализацией Go для Hystrix.
В качестве примера, вот код Go, который запрашивает стороннюю службу (в данном случае, JIRA, для получения открытых вопросов):
Простая функция для получения открытых вопросов
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
func retrieveOpenIssues(userConfig UserDefinedConfig) JiraSearchResponse { url := fmt.Sprintf("%s/rest/api/2/search", userConfig.Host) query := fmt.Sprintf("project = %s AND resolution = Unresolved ORDER BY priority DESC", userConfig.Project) fields := []string{"summary", "key", "status", "assignee", "description", "issuetype", "created"} jsonStr, _ := json.Marshal(JiraSearchRequest{query, 0, fields}) responseChannel := make(chan JiraSearchResponse) go func() { body := httpRequest(url, "POST", jsonStr, userConfig) var searchRes JiraSearchResponse json.Unmarshal(body, &searchRes) responseChannel <- searchRes }() return <-responseChannel} |
Этот код работает достаточно хорошо, пока в JIRA не начнут возникать проблемы , и в этом случае тайм-аут в конечном итоге вызовет довольно неприятную ошибку в вызывающем коде. На самом деле, встроенная паника в Go произойдет:
Иди в панике!
|
1
2
3
4
5
6
|
panic: Post https://jira.acme.com/rest/api/2/search: dial tcp 94.144.0.13:443: i/o timeoutgoroutine 24 [running]:runtime.panic(0x325620, 0xc208024bd0) /usr/local/go/src/pkg/runtime/panic.c:279 +0xf5... |
Конечно, я мог бы естественным образом встроить некоторый защитный код для обработки этих ошибок, или я мог бы использовать платформу, смоделированную после Hystrix, для этого. Поскольку я ленивый , я склонен использовать опыт других людей, и Hystrix-Go не исключение!
Естественно, вам сначала нужно импортировать Hystrix-Go:
импорт Hystrix-Go
|
1
|
import "github.com/afex/hystrix-go/hystrix" |
hystrix-go очень просто реализовать в вашем коде — вы просто реализуете свою логику внутри функции hystrix.Go (которая внутренне создает goroutine ). Более того, вы создаете резервную функцию, которая вызывается в состоянии ошибки ( например, время ожидания и т . Д. ).
Hystrix-Go очень прост в использовании
|
1
2
3
4
5
6
7
|
hystrix.Go("some command", func() error { // normal path code return nil}, func(err error) error { // do this when errors occur return nil}) |
Обратите внимание, что функция hystrix.Go принимает три аргумента, два последних — анонимные функции : первый — ваша обычная логика, а последний — желаемая запасная логика. Естественно, вы можете использовать Go Channel для получения данных от любой функции.
Первый аргумент string — это ключ, который можно сопоставить с уникальной конфигурацией. Например, вы можете установить отдельные тайм-ауты и даже пороги ошибок до того, как сработает резерв
Hystrix-Go также прост в настройке
|
1
2
3
4
5
|
hystrix.ConfigureCommand("unique_command", hystrix.CommandConfig{ Timeout: 1000, MaxConcurrentRequests: 100, ErrorPercentThreshold: 25,}) |
В приведенном выше случае время ожидания указывается в миллисекундах; следовательно, команда unique_command вызовет условие unique_command через 1 секунду. Библиотека хорошо протестирована, и я рекомендую прочитать некоторые из ее тестов, чтобы увидеть срабатывание различных вариантов восстановления.
Обладая знаниями о том, как добавить запасной вариант и настроить любые связанные с ним условия, очень легко включить допустимую отказоустойчивость в любой код go. Например, я могу взять этот вызывающий код JIRA и обернуть его внутри функции hystrix.Go следующим образом:
Теперь с большим количеством Hystrix!
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
func retrieveOpenIssues(userConfig UserDefinedConfig) JiraSearchResponse { url := fmt.Sprintf("%s/rest/api/2/search", userConfig.Host) query := fmt.Sprintf("project = %s AND resolution = Unresolved ORDER BY priority DESC", userConfig.Project) fields := []string{"summary", "key", "status", "assignee", "description", "issuetype", "created"} jsonStr, _ := json.Marshal(JiraSearchRequest{query, 0, fields}) responseChannel := make(chan JiraSearchResponse) hystrix.ConfigureCommand("Get all Issues", hystrix.CommandConfig{Timeout: 2000}) hystrix.Go("Get all Issues", func() error { body := httpRequest(url, "POST", jsonStr, userConfig) var searchRes JiraSearchResponse json.Unmarshal(body, &searchRes) responseChannel <- searchRes return nil }, func(err error) error { var searchRes JiraSearchResponse responseChannel <- searchRes return nil }) return <-responseChannel} |
В этом случае я также указал, что ключевая функция «Получить все проблемы» запустит тайм-аут через 2 секунды. Моя обычная логика, использующая HTTP GET , помещается в первую анонимную функцию, а моя запасная логика — во вторую. Эта резервная логика просто возвращает пустой ответ, чтобы не полностью влиять на нисходящие сервисы, полагаясь на какой-то ответ. То есть, если JIRA работает медленно, не работает или просто не отвечает, а не генерируется паника, возвращается пустой ответ. Это якобы запрещает каскадную цепочку ошибок.
Конечно, мой запасной код может использовать другую логику, если сочтет это необходимым; Более того, мой запасной код может определять тип сгенерированной ошибки. Например, мой резервный код может реагировать по-разному в случае тайм-аута, в отличие от проблемы параллелизма. Объект error переданный в резервную функцию, может быть запрошен достаточно легко: if err.Error() == "max concurrency" .
Netflix добился успеха с Hystrix в большой, высоко распределенной системе. Если вы хотите встроить допуск в любые сервисы Go, то я не могу рекомендовать Hystrix-Go достаточно: его очень легко настроить и использовать.
| Ссылка: | Отказоустойчивость в Go от нашего партнера JCG Эндрю Гловера в блоге The Disco Blog . |