-
Notifications
You must be signed in to change notification settings - Fork 707
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ratelimit interceptor #181
Ratelimit interceptor #181
Conversation
/sub |
Why are checkdocs failing? |
90223d8
to
5f7557b
Compare
Codecov Report
@@ Coverage Diff @@
## master #181 +/- ##
==========================================
+ Coverage 73.09% 73.28% +0.19%
==========================================
Files 36 37 +1
Lines 1349 1359 +10
==========================================
+ Hits 986 996 +10
Misses 314 314
Partials 49 49
Continue to review full report at Codecov.
|
I rebase the changes on latest master. |
Any plans to merge this ? |
ping :) @domgreen ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation feels very focused on the specific implementation of the github.com/juju/ratelimit
token package.
I think that if we are to have a rate limiting interceptor we should look to have it much more generic so that other implementations can easily be added to the library. Maybe add a second implementation to tease out the details?
ratelimit/DOC.md
Outdated
### Server Side Ratelimit Middleware | ||
It allows to use your own rate limiter (e.g. token bucket, leaky bucket, etc.) to do grpc rate limit. | ||
|
||
`ratelimit/tokenbucket`provides an implementation based on token bucket `github.com/juju/ratelimit`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
double space on token
ratelimit/DOC.md
Outdated
`grpc_ratelimit` a generic server-side ratelimit middleware for gRPC. | ||
|
||
### Server Side Ratelimit Middleware | ||
It allows to use your own rate limiter (e.g. token bucket, leaky bucket, etc.) to do grpc rate limit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we tidy up this sentence?
ratelimit/doc.go
Outdated
|
||
Please see examples for simple examples of use. | ||
*/ | ||
package grpc_ratelimit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's not continue this anti-pattern grpc_ratelimit
> ratelimit
ratelimit/doc.go
Outdated
@@ -0,0 +1,15 @@ | |||
// Copyright 2018 Zheng Dayu. All Rights Reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove :)
ratelimit/examples_test.go
Outdated
) | ||
|
||
// Simple example of server initialization code. | ||
func Example_initialization() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this just be Example
?
I'm really not keen on the way examples are done in this project but think this at least follows the current pattern.
ratelimit/ratelimit.go
Outdated
} | ||
} | ||
|
||
// StreamServerInterceptor returns a new stream server interceptors that performs request rate limit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// StreamServerInterceptor returns a new stream server interceptor that performs rate limiting on the request.
ratelimit/ratelimit.go
Outdated
if ratelimiter.Wait() { | ||
return handler(srv, stream) | ||
} | ||
return status.Errorf(codes.ResourceExhausted, "%s is rejected by grpc_ratelimit middleare, please retry later.", info.FullMethod) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again invert this logic to have the happy path non inverted
ratelimit/ratelimit_test.go
Outdated
} | ||
var ctx context.Context | ||
var req interface{} | ||
var info *grpc.UnaryServerInfo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for the onces that can define them in a var block
var (
...
...
)
ratelimit/ratelimit_test.go
Outdated
|
||
const errMsgFake = "fake error" | ||
|
||
func TestEmptyUnaryServerInterceptor(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TestUnaryServerInterceptor_EmptyCall
ratelimit/ratelimit_test.go
Outdated
assert.EqualError(t, err, errMsgFake) | ||
} | ||
|
||
func TestRateLimitUnaryServerInterceptor(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above
@ceshihao sorry I didnt pick this up earlier lots of other stuff going on with the world of work and open source so great to see some more people adding keeping the project going 👍 and @EngHabu Thanks for the ping. An alternative way to do this could be to pass in the interceptor to the middleware eg.
and limiter just conforms to the limiter interface with a Thoughts? |
@domgreen Thanks for review. I refactor the codes according to your comments. I change the type Limiter interface {
Limit() bool
} It makes everyone can pass his own limiter to the interceptor. |
755c88a
to
4b75c00
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates ... I think this is getting there the main thing I want us to try and move away from is taking dependencies on all rate limit packages so if we can pull that outside and instead allow the user to specify their own we can simplify the code massively.
Thanks again. 👍
ratelimit/examples_test.go
Outdated
@@ -0,0 +1,33 @@ | |||
// Copyright 2018 Zheng Dayu. All Rights Reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove
ratelimit/options.go
Outdated
@@ -0,0 +1,16 @@ | |||
// See LICENSE for licensing terms. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
may as well remove all of this and let people view the LICENSE file ... when i get time will clean the rest of the codebase
ratelimit/options.go
Outdated
|
||
type Option func(*rateLimiter) | ||
|
||
// WithRateLimiter customizes your limiter in the middleware |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be a full sentence ... how does it customize? It does give much info to the consumer.
ratelimit/ratelimit.go
Outdated
} | ||
} | ||
|
||
// UnaryServerInterceptor returns a new unary server interceptors that performs request rate limit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
limit
> limiting
ratelimit/ratelimit.go
Outdated
for _, opt := range opts { | ||
opt(ratelimiter) | ||
} | ||
fmt.Println(ratelimiter.limiter.Limit()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove Println
ratelimit/ratelimit.go
Outdated
} | ||
fmt.Println(ratelimiter.limiter.Limit()) | ||
return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { | ||
fmt.Println(ratelimiter.limiter.Limit()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and here
ratelimit/tokenbucket/tokenbucket.go
Outdated
@@ -0,0 +1,28 @@ | |||
// Copyright 2018 Zheng Dayu. All Rights Reserved. | |||
// See LICENSE for licensing terms. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as above
ratelimit/ratelimit.go
Outdated
fmt.Println(ratelimiter.limiter.Limit()) | ||
for _, opt := range opts { | ||
opt(ratelimiter) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could get rid of the opts here ... from what i can see there is only a single opt ... the ratelimiter that the user is passing in.
Lets change it to be something like:
func UnaryServerInterceptor(limiter Limiter) grpc.UnaryServerInterceptor {
@@ -0,0 +1,22 @@ | |||
package tokenbucket |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are getting the end user to pass in the rate limiter we can remove the whole tokenbucket package from this codebase and not depoend on juju ourselves.
The user would then wrap it using the Limiter interface so that we allow them to use any rate limit algorithm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the package can be kept in order to give user an easy implementation of Limiter
.
And also user can implement his own limiter if it is not enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can give an example without needing to import the dependency, the thinking behind passing in the rate limiter is so that we do not have to maintain the rate limiters but just wrap them to meet the Limiter interface that has been defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌remove it from repo
) | ||
|
||
type Limiter interface { | ||
Limit() bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add documentation on the the type and func :)
Just done a round of review @andyxning @makkalot @EngHabu feel free to add your own comments. 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a small amount of clean up needed
ratelimit/doc.go
Outdated
|
||
It allows to do grpc rate limit by your own rate limiter (e.g. token bucket, leaky bucket, etc.) | ||
|
||
`ratelimit/tokenbucket`provides an implementation based on token bucket `github.com/juju/ratelimit`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we update ... I dont think this is true anymore :)
ratelimit/examples_test.go
Outdated
import ( | ||
"time" | ||
|
||
"github.com/ceshihao/ratelimiter/tokenbucket" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is no longer in the project.
Tio do the example can we create a test rate limiter that meets the interface 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean to add a fake limiter (e.g. allwaysPassLimiter
) in the example instead of the one based on juju/ratelimit
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, lets just add a fake that returns true (or get creative) but one that is not exported ... dont really want the dep just for an example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
3b69c1d
to
821b156
Compare
Add ratelimit interceptor, and implement a token bucket limiter using
github.com/juju/ratelimit
Fix #134