Compare commits
No commits in common. "82790e260d70e452a249edfecb402afce0cf3d4d" and "b915af2ab47738aaf1b1a47ee778ccaf79128af7" have entirely different histories.
82790e260d
...
b915af2ab4
46
main.go
46
main.go
|
|
@ -5,37 +5,20 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kordondev/meeting/throttling"
|
"github.com/kordondev/meeting/throttling"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const burstLimit = 2
|
const burstLimit = 2
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ctx := context.Background()
|
throttles := make(map[string]<-chan time.Time)
|
||||||
throttles := make(map[string]context.Context) // use sync map
|
calledLastHour := make([]string, 0)
|
||||||
|
|
||||||
http.HandleFunc("/", serveIndexFile)
|
http.HandleFunc("/", serveIndexFile)
|
||||||
http.HandleFunc("/input.txt", serveInputFile)
|
http.HandleFunc("/input.txt", serveInputFile)
|
||||||
http.HandleFunc("/result", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/result", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tryResult(w, r, throttles, calledLastHour)
|
||||||
cctxV, ok := throttles[r.RemoteAddr]
|
|
||||||
if !ok {
|
|
||||||
cctx, cancel := context.WithCancel(ctx)
|
|
||||||
throttle := throttling.CreateThrottle(cctx, burstLimit)
|
|
||||||
a := throttleWithCancel{
|
|
||||||
throttle: throttle,
|
|
||||||
cancel: cancel,
|
|
||||||
}
|
|
||||||
cctxV = context.WithValue(ctx, "throttle", a)
|
|
||||||
throttles[r.RemoteAddr] = cctxV
|
|
||||||
go func() {
|
|
||||||
time.Sleep(time.Minute * 20)
|
|
||||||
delete(throttles, r.RemoteAddr)
|
|
||||||
cancel()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
tryResult(cctxV, w, r)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
err := http.ListenAndServe(":3333", nil)
|
err := http.ListenAndServe(":3333", nil)
|
||||||
|
|
@ -44,11 +27,6 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type throttleWithCancel struct {
|
|
||||||
throttle <-chan time.Time
|
|
||||||
cancel context.CancelFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func serveIndexFile(w http.ResponseWriter, r *http.Request) {
|
func serveIndexFile(w http.ResponseWriter, r *http.Request) {
|
||||||
http.ServeFile(w, r, "static/index-with-text.html")
|
http.ServeFile(w, r, "static/index-with-text.html")
|
||||||
}
|
}
|
||||||
|
|
@ -58,18 +36,26 @@ func serveInputFile(w http.ResponseWriter, r *http.Request) {
|
||||||
http.ServeFile(w, r, "static/input.txt")
|
http.ServeFile(w, r, "static/input.txt")
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryResult(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
func tryResult(w http.ResponseWriter, r *http.Request, throttles map[string]<-chan time.Time, calledLastHour []string) {
|
||||||
clientIP := r.RemoteAddr
|
clientIP := r.RemoteAddr
|
||||||
clientResult := r.URL.Query().Get("result")
|
clientResult := r.URL.Query().Get("result")
|
||||||
fmt.Println(clientIP, "called with:", clientResult)
|
fmt.Println(clientIP, clientResult)
|
||||||
throttle := ctx.Value("throttle").(throttleWithCancel).throttle
|
if slices.Contains(calledLastHour, clientIP) {
|
||||||
|
calledLastHour = append(calledLastHour, clientIP)
|
||||||
|
}
|
||||||
|
throttle, ok := throttles[clientIP]
|
||||||
|
if !ok {
|
||||||
|
fmt.Println("Creating new throttle")
|
||||||
|
throttle = throttling.CreateThrottle(r.Context(), burstLimit)
|
||||||
|
throttles[clientIP] = throttle
|
||||||
|
}
|
||||||
payload := throttling.Payload{
|
payload := throttling.Payload{
|
||||||
R: r,
|
R: r,
|
||||||
W: w,
|
W: w,
|
||||||
ClientResult: clientResult,
|
ClientResult: clientResult,
|
||||||
}
|
}
|
||||||
|
|
||||||
throttling.CallFunction(ctx, &CheckResult{}, &payload, throttle)
|
throttling.CallFunction(context.TODO(), &CheckResult{}, &payload, throttle)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckResult struct {
|
type CheckResult struct {
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ type Payload struct {
|
||||||
// CallFunction allows burst rate limiting client calls with the
|
// CallFunction allows burst rate limiting client calls with the
|
||||||
// payloads.
|
// payloads.
|
||||||
func CallFunction(ctx context.Context, client Client, payload *Payload, throttle <-chan time.Time) {
|
func CallFunction(ctx context.Context, client Client, payload *Payload, throttle <-chan time.Time) {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
<-throttle // rate limit our client calls
|
<-throttle // rate limit our client calls
|
||||||
client.Call(payload)
|
client.Call(payload)
|
||||||
}
|
}
|
||||||
|
|
@ -40,18 +43,11 @@ func CreateThrottle(ctx context.Context, burstLimit int) <-chan time.Time {
|
||||||
for t := range ticker.C {
|
for t := range ticker.C {
|
||||||
select {
|
select {
|
||||||
case throttle <- t:
|
case throttle <- t:
|
||||||
{
|
|
||||||
fmt.Println("Add bucket to throttle")
|
|
||||||
}
|
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
{
|
{
|
||||||
fmt.Println("Ticker done")
|
fmt.Println("Ticker done")
|
||||||
return // exit goroutine when surrounding function returns
|
return // exit goroutine when surrounding function returns
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
{
|
|
||||||
fmt.Println("Dropping bucket")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue