Из-за ограниченной грузоподъемности системы бизнес-приложений, чтобы непреднамеренные запросы не оказывали слишком большой нагрузки на систему и тормозили систему бизнес-приложений, каждый интерфейс API имеет ограничение доступа. Политики управления трафиком для интерфейсов API: шунтирование, переход на более раннюю версию и ограничение тока. В этой статье обсуждается текущая стратегия ограничения.Хотя частота доступа и параллелизм интерфейса службы снижены, они заменены высокой доступностью интерфейса службы и системы бизнес-приложений.
Целью текущего ограничения является защита системы путем ограничения скорости одновременного доступа/запросов или ограничения скорости запросов в пределах временного окна.
Алгоритм ограничения тока
Существует два широко используемых алгоритма ограничения тока: алгоритм дырявого ведра и алгоритм ведра с маркерами.
алгоритм дырявого ведра
Алгоритм дырявого ведра — это алгоритм, который часто используется при формировании трафика или ограничении скорости в сетевом мире для отправки трафика. Алгоритм дырявого ведра обеспечивает механизм, с помощью которого пакетный трафик может формироваться для обеспечения стабильного потока в сети.
Идея алгоритма дырявого ведра очень проста, вода (запрос) сначала поступает в дырявое ведро, а дырявое ведро выходит из воды с определенной скоростью (у интерфейса есть скорость отклика), а когда скорость притока воды слишком высок, он будет переполняться напрямую (частота доступа превышает скорость отклика интерфейса), а затем, пока запрос отклонен, видно, что алгоритм дырявого ведра может принудительно ограничивать скорость передачи данных. Схематическая диаграмма выглядит следующим образом:
Поскольку скорость утечки дырявого ведра является фиксированным параметром, даже если в сети нет конфликта ресурсов (нет перегрузки), алгоритм дырявого ведра не может увеличить скорость потока до скорости порта. Таким образом, алгоритм дырявого ведра неэффективен для трафика с пакетными характеристиками.
Алгоритм ведра токенов
Алгоритм Token Bucket — один из наиболее часто используемых алгоритмов в формировании сетевого трафика (Traffic Shaping) и ограничении скорости (Rate Limiting). Как правило, алгоритм корзины маркеров используется для управления объемом данных, отправляемых в сеть, и для разрешения отправки пакетов данных.
Принцип алгоритма ведра токенов заключается в том, что система будет помещать токены в ведро с постоянной скоростью, и если запрос необходимо обработать, то сначала нужно получить токен из ведра. ведро, затем отказ в обслуживании. С принципиальной точки зрения, алгоритм ведра с токенами и алгоритм с дырявым ведром противоположны, один — «вода внутри», другой — «утечка».
Еще одним преимуществом ведер с токенами является простота изменения скорости. Как только скорость необходимо увеличить, скорость токенов, помещаемых в корзину, увеличивается по запросу. Как правило, определенное количество токенов добавляется в корзину через равные промежутки времени (например, 100 миллисекунд), а некоторые варианты алгоритмов рассчитывают количество токенов, которые следует добавить, в режиме реального времени.
Реализация ограничения тока микросервиса Go-kit
На основе вышеприведенного анализа буду реализовывать текущую ограничивающую функцию микросервисов на основе go-kit. консультируясьgokit/kit/ratelimit
Исходный код, обнаружил, что gokit основан на пакете gogolang.org/x/time/rate
Есть встроенная реализация, кроме того, ранее gokit использовал дефолтныйjuju/ratelimit
Схему реализации (которая в настоящее время официально удалена), я буду реализовывать на основе двух методов.
В отличие от двух предыдущих статей, эта реализация будет основана на встроенных типах gokit.endpoint.Middleware
, тип на самом деле является функцией, которая использует шаблон декоратора для инкапсуляции конечной точки. Определяется следующим образом:
# Go-kit Middleware Endpoint
type Middleware func(Endpoint) Endpoint
схема juju/ratelimit
Примером в этой статье будет продолжать улучшаться на основе Кодекса предыдущей статьи (в конце прилагаемого адреса) первые две статьи забыли поставить адрес.
Шаг 1: Создайте текущий ограничитель
Сначала установите последнюю версию, используя следующую командуjuju/ratelimit
Библиотеки:
go get github.com/juju/ratelimit
Затем создайте новый файл go с именемinstrument.go
, чтобы реализовать текущий метод ограничения: параметр — возвращаемое ведро токенов (bkt).endpoint.Middleware
. с помощью ведра токеновTakeAvaiable
Метод получает токен и, если получение прошло успешно, продолжает выполнение, а если получение не удалось, возвращает исключение (то есть ограничение тока). код показывает, как показано ниже:
var ErrLimitExceed = errors.New("Rate limit exceed!")
// NewTokenBucketLimitterWithJuju 使用juju/ratelimit创建限流中间件
func NewTokenBucketLimitterWithJuju(bkt *ratelimit.Bucket) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
if bkt.TakeAvailable(1) == 0 {
return nil, ErrLimitExceed
}
return next(ctx, request)
}
}
}
Шаг 2: Изменить основной
вниз использоватьjuju/ratelimit
Создайте ведро токенов (обновляется каждую секунду с емкостью 3), затем вызовитеStep-1
Реализуйте текущий метод ограничения, чтобы украсить конечную точку. Добавьте следующий код в основной метод.
// add ratelimit,refill every second,set capacity 3
ratebucket := ratelimit.NewBucket(time.Second*1, 3)
endpoint = NewTokenBucketLimitterWithJuju(ratebucket)(endpoint)
После модификации полный код выглядит следующим образом:
func main() {
ctx := context.Background()
errChan := make(chan error)
var logger log.Logger
{
logger = log.NewLogfmtLogger(os.Stderr)
logger = log.With(logger, "ts", log.DefaultTimestampUTC)
logger = log.With(logger, "caller", log.DefaultCaller)
}
var svc Service
svc = ArithmeticService{}
// add logging middleware
svc = LoggingMiddleware(logger)(svc)
endpoint := MakeArithmeticEndpoint(svc)
// add ratelimit,refill every second,set capacity 3
ratebucket := ratelimit.NewBucket(time.Second*1, 3)
endpoint = NewTokenBucketLimitterWithJuju(ratebucket)(endpoint)
r := MakeHttpHandler(ctx, endpoint, logger)
go func() {
fmt.Println("Http Server start at port:9000")
handler := r
errChan <- http.ListenAndServe(":9000", handler)
}()
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
errChan <- fmt.Errorf("%s", <-c)
}()
fmt.Println(<-errChan)
}
Шаг 3: Скомпилируйте и запустите
Скомпилируйте и запустите приложение на консоли, а затем протестируйте его через интерфейс запроса Postman, вы можете увидеть информацию журнала вывода:
ts=2019-02-19T03:20:13.1908613Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T03:20:13.7144627Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T03:20:14.2276079Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T03:20:14.7414288Z caller=server.go:112 err="Rate limit exceed!"
ts=2019-02-19T03:20:15.2091773Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T03:20:16.0261559Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T03:20:16.6406654Z caller=server.go:112 err="Rate limit exceed!"
ts=2019-02-19T03:20:17.1912533Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T03:20:17.7828906Z caller=server.go:112 err="Rate limit exceed!"
Как видно из лога, запрос появляетсяRate limit exceed!
, то есть текущий ограничитель прервет запрос после выдачи токена, и сервис будет недоступен, при продолжении доступа сервис возобновится, то есть текущий ограничитель возобновит заполнение корзины токенов.
Встроенная реализация Gokit
Шаг 1: Создайте текущий ограничитель
Сначала скачайте зависимый go/time/rate
Пакет устанавливается следующим образом (команду go get нельзя использовать напрямую):
git clone https://github.com/golang/time.git [Your GOPATH]/src/golang.org/x
затем вinstrument.go
добавить методNewTokenBucketLimitterWithBuildIn
, в котором используетсяx/time/rate
Реализовать метод ограничения тока:
// NewTokenBucketLimitterWithBuildIn 使用x/time/rate创建限流中间件
func NewTokenBucketLimitterWithBuildIn(bkt *rate.Limiter) endpoint.Middleware {
return func(next endpoint.Endpoint) endpoint.Endpoint {
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
if !bkt.Allow() {
return nil, ErrLimitExceed
}
return next(ctx, request)
}
}
}
Шаг 2: Изменить основной
Измените текущую инкапсуляцию метода ограничения на следующую реализацию:
//add ratelimit,refill every second,set capacity 3
ratebucket := rate.NewLimiter(rate.Every(time.Second*1), 3)
endpoint = NewTokenBucketLimitterWithBuildIn(ratebucket)(endpoint)
Шаг 3: Скомпилируйте и запустите
Скомпилируйте и запустите приложение на консоли, а затем протестируйте его через интерфейс запроса Postman, вы можете увидеть информацию журнала вывода:
ts=2019-02-19T06:03:26.8650217Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T06:03:27.5747177Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T06:03:28.1274404Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T06:03:28.5892068Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T06:03:29.1327522Z caller=server.go:112 err="Rate limit exceed!"
ts=2019-02-19T06:03:29.59453Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T06:03:30.2138805Z caller=server.go:112 err="Rate limit exceed!"
ts=2019-02-19T06:03:30.6257682Z caller=logging.go:41 function=Subtract a=10 b=1 result=9 took=0s
ts=2019-02-19T06:03:31.2772011Z caller=server.go:112 err="Rate limit exceed!"
Из лога видно, что эффект тот же, что иjuju/ratelimit
Тот же план.
Суммировать
В этой статье сначала представлены два широко используемых алгоритма ограничения тока, алгоритм дырявого ведра и алгоритм ведра с маркерами, а затем используются две схемы (juju/ratelimit
и встроенная библиотека gokit) для реализации ограничения тока службы.
В процессе разработки сервиса нам необходимо полностью учитывать доступность сервисов, особенно тех сервисов, которые потребляют системные ресурсы, и добавить текущий механизм ограничения для обеспечения стабильной и надежной работы сервисов.
Картинки из интернета.
- Кодовый адрес этой статьи:GitHub.com/rays на новом/a…
- Ведро жетонов:En. Wikipedia.org/wiki/token_…
- джуджу/ограничение скорости:GitHub.com/Juju/rate…
Эта статья была впервые опубликована в моей публичной учетной записи WeChat [Xi Yiang Bar], пожалуйста, отсканируйте код, чтобы следовать!