Skip to content

Commit

Permalink
Merge pull request #21 from dongxuny/master
Browse files Browse the repository at this point in the history
Rename ratelimit boot config
  • Loading branch information
dongxuny authored Oct 24, 2021
2 parents 0497afe + 1be197f commit 8b1e7c1
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 86 deletions.
60 changes: 35 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -450,8 +450,8 @@ Send application metadata as header to client and GRPC Gateway.
| grpc.interceptors.rateLimit.enabled | Enable rate limit interceptor | boolean | false |
| grpc.interceptors.rateLimit.algorithm | Provide algorithm, tokenBucket and leakyBucket are available options | string | tokenBucket |
| grpc.interceptors.rateLimit.reqPerSec | Request per second globally | int | 0 |
| grpc.interceptors.rateLimit.methods.name | gRPC full name | string | "" |
| grpc.interceptors.rateLimit.methods.reqPerSec | Request per second by method name | int | 0 |
| grpc.interceptors.rateLimit.paths.path | gRPC full name | string | "" |
| grpc.interceptors.rateLimit.paths.reqPerSec | Request per second by gRPC full method name | int | 0 |

## Development Status: Stable

Expand All @@ -476,29 +476,39 @@ Run unit test with **make test** command.
github workflow will automatically run unit test and golangci-lint for testing and lint validation.

## Dependencies
- github.com/ghodss/yaml v1.0.0
- github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0
- github.com/markbates/pkger v0.17.1
- github.com/prometheus/client_golang v1.10.0
- github.com/rookie-ninja/rk-common v1.2.1
- github.com/rookie-ninja/rk-entry v1.0.3
- github.com/rookie-ninja/rk-logger v1.2.3
- github.com/rookie-ninja/rk-prom v1.1.3
- github.com/rookie-ninja/rk-query v1.2.4
- github.com/soheilhy/cmux v0.1.5
- github.com/stretchr/testify v1.7.0
- go.opentelemetry.io/contrib v1.0.0
- go.opentelemetry.io/otel v1.0.1
- go.opentelemetry.io/otel/exporters/jaeger v1.0.1 // indirect
- go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.0.1 // indirect
- go.opentelemetry.io/otel/sdk v1.0.1
- go.opentelemetry.io/otel/trace v1.0.1
- go.uber.org/zap v1.16.0
- golang.org/x/net v0.0.0-20210614182718-04defd469f4e
- golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
- google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced
- google.golang.org/grpc v1.38.0
- google.golang.org/protobuf v1.26.0
```
module github.com/rookie-ninja/rk-grpc
go 1.14
require (
github.com/ghodss/yaml v1.0.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0
github.com/juju/ratelimit v1.0.1
github.com/markbates/pkger v0.17.1
github.com/prometheus/client_golang v1.10.0
github.com/rookie-ninja/rk-common v1.2.1
github.com/rookie-ninja/rk-entry v1.0.3
github.com/rookie-ninja/rk-logger v1.2.3
github.com/rookie-ninja/rk-prom v1.1.3
github.com/rookie-ninja/rk-query v1.2.4
github.com/soheilhy/cmux v0.1.5
github.com/stretchr/testify v1.7.0
go.opentelemetry.io/contrib v1.0.0
go.opentelemetry.io/otel v1.0.1
go.opentelemetry.io/otel/exporters/jaeger v1.0.1
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.0.1
go.opentelemetry.io/otel/sdk v1.0.1
go.opentelemetry.io/otel/trace v1.0.1
go.uber.org/ratelimit v0.2.0
go.uber.org/zap v1.16.0
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced
google.golang.org/grpc v1.38.0
google.golang.org/protobuf v1.26.0
)
```

## Contributing
We encourage and support an active, healthy community of contributors —
Expand Down
23 changes: 14 additions & 9 deletions boot/grpc_entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"github.com/rookie-ninja/rk-grpc/interceptor/meta"
"github.com/rookie-ninja/rk-grpc/interceptor/metrics/prom"
"github.com/rookie-ninja/rk-grpc/interceptor/panic"
rkgrpclimit "github.com/rookie-ninja/rk-grpc/interceptor/ratelimit"
"github.com/rookie-ninja/rk-grpc/interceptor/ratelimit"
"github.com/rookie-ninja/rk-grpc/interceptor/tracing/telemetry"
"github.com/rookie-ninja/rk-prom"
"github.com/rookie-ninja/rk-query"
Expand Down Expand Up @@ -114,8 +114,13 @@ type gwRule struct {
// 36: Grpc.Interceptors.Meta.TracingTelemetry.Exporter.Jaeger.CollectorEndpoint: Jaeger collector endpoint.
// 37: Grpc.Interceptors.Meta.TracingTelemetry.Exporter.Jaeger.CollectorUsername: Jaeger collector user name.
// 38: Grpc.Interceptors.Meta.TracingTelemetry.Exporter.Jaeger.CollectorPassword: Jaeger collector password.
// 39: Grpc.Logger.ZapLogger.Ref: Zap logger reference, see rkentry.ZapLoggerEntry for details.
// 40: Grpc.Logger.EventLogger.Ref: Event logger reference, see rkentry.EventLoggerEntry for details.
// 39: Grpc.Interceptors.RateLimit.Enabled: Enable rate limit interceptor.
// 40: Grpc.Interceptors.RateLimit.Algorithm: Algorithm of rate limiter.
// 41: Grpc.Interceptors.RateLimit.ReqPerSec: Request per second.
// 42: Grpc.Interceptors.RateLimit.Paths.Path: Name of gRPC full method.
// 43: Grpc.Interceptors.RateLimit.Paths.ReqPerSec: Request per second by method.
// 44: Grpc.Logger.ZapLogger.Ref: Zap logger reference, see rkentry.ZapLoggerEntry for details.
// 45: Grpc.Logger.EventLogger.Ref: Event logger reference, see rkentry.EventLoggerEntry for details.
type BootConfigGrpc struct {
Grpc []struct {
Name string `yaml:"name" json:"name"`
Expand Down Expand Up @@ -157,10 +162,10 @@ type BootConfigGrpc struct {
Enabled bool `yaml:"enabled" json:"enabled"`
Algorithm string `yaml:"algorithm" json:"algorithm"`
ReqPerSec int `yaml:"reqPerSec" json:"reqPerSec"`
Methods []struct {
Name string `yaml:"name" json:"name"`
Paths []struct {
Path string `yaml:"path" json:"path"`
ReqPerSec int `yaml:"reqPerSec" json:"reqPerSec"`
} `yaml:"methods" json:"methods"`
} `yaml:"paths" json:"paths"`
} `yaml:"rateLimit" json:"rateLimit"`
TracingTelemetry struct {
Enabled bool `yaml:"enabled" json:"enabled"`
Expand Down Expand Up @@ -521,9 +526,9 @@ func RegisterGrpcEntriesWithConfig(configFilePath string) map[string]rkentry.Ent
}
opts = append(opts, rkgrpclimit.WithReqPerSec(element.Interceptors.RateLimit.ReqPerSec))

for i := range element.Interceptors.RateLimit.Methods {
method := element.Interceptors.RateLimit.Methods[i]
opts = append(opts, rkgrpclimit.WithReqPerSecByMethod(method.Name, method.ReqPerSec))
for i := range element.Interceptors.RateLimit.Paths {
e := element.Interceptors.RateLimit.Paths[i]
opts = append(opts, rkgrpclimit.WithReqPerSecByPath(e.Path, e.ReqPerSec))
}

entry.AddUnaryInterceptors(rkgrpclimit.UnaryServerInterceptor(opts...))
Expand Down
4 changes: 2 additions & 2 deletions boot/grpc_entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ grpc:
enabled: true
algorithm: leakyBucket
reqPerSec: 1
methods:
- name: "ut-method"
paths:
- path: "ut-method"
reqPerSec: 1
`

Expand Down
4 changes: 2 additions & 2 deletions example/boot/full/boot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,6 @@ grpc:
enabled: false
algorithm: "leakyBucket"
reqPerSec: 0
methods:
- name: "/rk.api.v1.RkCommonService/Healthy"
paths:
- path: "/rk.api.v1.RkCommonService/Healthy"
reqPerSec: 0
8 changes: 4 additions & 4 deletions example/interceptor/ratelimit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import "github.com/rookie-ninja/rk-grpc/interceptor/ratelimit"
grpc.ChainUnaryInterceptor(
rkgrpclimit.UnaryServerInterceptor(
rkgrpclimit.WithReqPerSec(100),
rkgrpclimit.WithReqPerSecByMethod("/Greeter/SayHello", 0),
rkgrpclimit.WithReqPerSecByPath("/Greeter/SayHello", 0),
),
),
}
Expand All @@ -57,7 +57,7 @@ import "github.com/rookie-ninja/rk-grpc/interceptor/ratelimit"
grpc.ChainStreamInterceptor(
rkgrpclimit.StreamServerInterceptor(
rkgrpclimit.WithReqPerSec(100),
rkgrpclimit.WithReqPerSecByMethod("/Greeter/SayHello", 0),
rkgrpclimit.WithReqPerSecByPath("/Greeter/SayHello", 0),
),
),
}
Expand All @@ -68,10 +68,10 @@ import "github.com/rookie-ninja/rk-grpc/interceptor/ratelimit"
| ---- | ---- | ---- |
| WithEntryNameAndType(entryName, entryType string) | entryName=grpc, entryType=grpc | entryName and entryType will be used to distinguish options if there are multiple interceptors in single process. |
| WithReqPerSec(int) | int | Global rate limit per second. |
| WithReqPerSecByMethod(method string, reqPerSec int) | "", 0 | Request limiter by gRPC method. |
| WithReqPerSecByPath(path string, reqPerSec int) | "", 0 | Request limiter by gRPC method. |
| WithAlgorithm(algo string) | tokenBucket | Algorithm of rate limiter. |
| WithGlobalLimiter(l Limiter) | nil | Provider user defined limiter. |
| WithLimiterByMethod(method string, l Limiter) | "", nil | Provider user defined limiter by method. |
| WithLimiterByPath(path string, l Limiter) | "", nil | Provider user defined limiter by gRPC method. |

### Context Usage
| Name | Functionality |
Expand Down
Binary file removed example/interceptor/ratelimit/img/client-arch.png
Binary file not shown.
Binary file removed example/interceptor/ratelimit/img/server-arch.png
Binary file not shown.
8 changes: 4 additions & 4 deletions example/interceptor/ratelimit/server/greeter-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ func main() {
// resource exhausted.
rkgrpclimit.WithReqPerSec(0),
//
// Provide request per second with method name.
// Provide request per second with path name.
// The name should be gRPC full method name. if provide value of zero,
// then no requests will be pass through and user will receive an error with resource exhausted.
// rkgrpclimit.WithReqPerSecByMethod("/Greeter/SayHello", 0),
// rkgrpclimit.WithReqPerSecByPath("/Greeter/SayHello", 0),
//
// Provide user function of limiter
// rkgrpclimit.WithGlobalLimiter(func(ctx context.Context) error {
// return nil
// }),
//
// Provide user function of limiter by method name.
// Provide user function of limiter by path name.
// The name should be gRPC full method name.
// rkgrpclimit.WithLimiterByMethod("/Greeter/SayHello", func(ctx context.Context) error {
// rkgrpclimit.WithLimiterByPath("/Greeter/SayHello", func(ctx context.Context) error {
// return nil
// }),
),
Expand Down
60 changes: 30 additions & 30 deletions interceptor/ratelimit/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ package rkgrpclimit
import (
"context"
juju "github.com/juju/ratelimit"
rkerror "github.com/rookie-ninja/rk-common/error"
"github.com/rookie-ninja/rk-common/error"
"github.com/rookie-ninja/rk-grpc/interceptor"
uber "go.uber.org/ratelimit"
"strings"
Expand Down Expand Up @@ -70,12 +70,12 @@ var optionsMap = make(map[string]*optionSet)
// Create new optionSet with rpc type and options.
func newOptionSet(rpcType string, opts ...Option) *optionSet {
set := &optionSet{
EntryName: rkgrpcinter.RpcEntryNameValue,
EntryType: rkgrpcinter.RpcEntryTypeValue,
reqPerSec: DefaultLimit,
reqPerSecByMethod: make(map[string]int, DefaultLimit),
algorithm: TokenBucket,
limiter: make(map[string]Limiter),
EntryName: rkgrpcinter.RpcEntryNameValue,
EntryType: rkgrpcinter.RpcEntryTypeValue,
reqPerSec: DefaultLimit,
reqPerSecByPath: make(map[string]int, DefaultLimit),
algorithm: TokenBucket,
limiter: make(map[string]Limiter),
}

for i := range opts {
Expand All @@ -94,7 +94,7 @@ func newOptionSet(rpcType string, opts ...Option) *optionSet {
set.setLimiter(GlobalLimiter, l.Limit)
}

for k, v := range set.reqPerSecByMethod {
for k, v := range set.reqPerSecByPath {
if v < 1 {
l := &ZeroRateLimiter{}
set.setLimiter(k, l.Limit)
Expand All @@ -116,7 +116,7 @@ func newOptionSet(rpcType string, opts ...Option) *optionSet {
set.setLimiter(GlobalLimiter, l.Limit)
}

for k, v := range set.reqPerSecByMethod {
for k, v := range set.reqPerSecByPath {
if v < 1 {
l := &ZeroRateLimiter{}
set.setLimiter(k, l.Limit)
Expand All @@ -142,41 +142,41 @@ func newOptionSet(rpcType string, opts ...Option) *optionSet {

// options which is used while initializing extension interceptor
type optionSet struct {
EntryName string
EntryType string
reqPerSec int
reqPerSecByMethod map[string]int
algorithm string
limiter map[string]Limiter
EntryName string
EntryType string
reqPerSec int
reqPerSecByPath map[string]int
algorithm string
limiter map[string]Limiter
}

// Wait until rate limit pass through
func (set *optionSet) Wait(ctx context.Context, method string) (time.Duration, error) {
func (set *optionSet) Wait(ctx context.Context, path string) (time.Duration, error) {
now := time.Now()

limiter := set.getLimiter(method)
limiter := set.getLimiter(path)
if err := limiter(ctx); err != nil {
return now.Sub(now), err
}

return now.Sub(time.Now()), nil
}

func (set *optionSet) getLimiter(method string) Limiter {
if v, ok := set.limiter[method]; ok {
func (set *optionSet) getLimiter(path string) Limiter {
if v, ok := set.limiter[path]; ok {
return v
}

return set.limiter[GlobalLimiter]
}

// Set limiter if not exists
func (set *optionSet) setLimiter(method string, l Limiter) {
if _, ok := set.limiter[method]; ok {
func (set *optionSet) setLimiter(path string, l Limiter) {
if _, ok := set.limiter[path]; ok {
return
}

set.limiter[method] = l
set.limiter[path] = l
}

// Option option for optionSet
Expand All @@ -199,11 +199,11 @@ func WithReqPerSec(reqPerSec int) Option {
}
}

// WithReqPerSecByMethod Provide request per second by method.
func WithReqPerSecByMethod(method string, reqPerSec int) Option {
// WithReqPerSecByPath Provide request per second by path.
func WithReqPerSecByPath(path string, reqPerSec int) Option {
return func(opt *optionSet) {
if reqPerSec >= 0 {
opt.reqPerSecByMethod[method] = reqPerSec
opt.reqPerSecByPath[path] = reqPerSec
}
}
}
Expand All @@ -224,13 +224,13 @@ func WithGlobalLimiter(l Limiter) Option {
}
}

// WithLimiterByMethod provide user defined Limiter by method.
func WithLimiterByMethod(method string, l Limiter) Option {
// WithLimiterByPath provide user defined Limiter by path.
func WithLimiterByPath(path string, l Limiter) Option {
return func(opt *optionSet) {
if !strings.HasPrefix(method, "/") {
method = "/" + method
if !strings.HasPrefix(path, "/") {
path = "/" + path
}

opt.limiter[method] = l
opt.limiter[path] = l
}
}
Loading

0 comments on commit 8b1e7c1

Please sign in to comment.