valyala / fasthttp

Fast HTTP package for Go. Tuned for high performance. Zero memory allocations in hot paths. Up to 10x faster than net/http
MIT License
21.95k stars 1.76k forks source link

Any protection against fault tolerance for fasthttp? #908

Open gitmko0 opened 4 years ago

gitmko0 commented 4 years ago

Something along the lines of this: https://cloud.ibm.com/docs/go?topic=go-fault-tolerance

Is there any possibility to incorporate fasthttp into the code above or we can do one for our own use with a newer version of fasthttp? What kind of fault tolerance features can be done with fasthttp other than timeout settings and max connection etc? anyone tested under extreme loads (overcapacity type)?

kirillDanshin commented 4 years ago

hey @gitmko0

here's the first snippet, translated for fasthttp, and a variant without middlewares

func HystrixHandler(h func(*fasthttp.RequestCtx), command string) func(*fasthttp.RequestCtx) {
  return func(ctx *fasthttp.RequestCtx) {
    hystrix.Do(command, func() error {
      h(ctx)
      return nil
    }, func(err error) error {
      // Handle failures
      return err
    })
  }
}

func HystrixHandlerNoMiddlewares(command string) func(*fasthttp.RequestCtx) {
  return func(ctx *fasthttp.RequestCtx) {
    hystrix.Do(command, func() error {
      ctx.SetBodyString("hey there")
      return nil
    }, func(err error) error {
      // Handle failures
      return err
    })
  }
}

other stuff you can do as you normally would, hystrix is just a standalone package that shouldn't really care what http engine you use. note, that first examples with middlewares allows for custom handler types, e.g. you can use it to handle errors that you can return from handlers, if you'd use func(*fasthttp.RequestCtx) error or something like that

gitmko0 commented 4 years ago

thx. this is a game changer... now we need some hot reloading "golang modules" and we have our own "erlang". what do you guys think? hot reloading golang modules the way forward or something better? definitely not interpreted code... I'm talking about clustering possibilities for this.

I'm thinking using pub/sub and inject modules into golang code and let fasthttp run the updated code. Should this be the way forward?

gitmko0 commented 4 years ago

@kirillDanshin i've tried running it but not sure why the code, when failed is unresponsive, even after timeout.

  1. do you have a full working example of how to use this?
  2. possible to do a hystrix.Go() version example?

trying to explore how to implement it the "proper way" for reverse proxy, in scenario where by "if too many incoming connections, fail gracefully.", but recover in 1s. with reference here : https://github.com/valyala/fasthttp/issues/64

sample code, any comments?


package main

import (
        "log"
        //      "os"
        "github.com/afex/hystrix-go/hystrix"
        "github.com/valyala/fasthttp"
//      "github.com/fasthttp/router"

)
func init(){
        hystrix.ConfigureCommand("mycommand", hystrix.CommandConfig{
                Timeout:               1000,
                MaxConcurrentRequests: 100,
                ErrorPercentThreshold: 25,
        })

}

var proxyClient = &fasthttp.HostClient{
        Addr: "127.0.0.1:8080",
        // set other options here if required - most notably timeouts.
}

func HystrixHandler(h func(*fasthttp.RequestCtx), command string) func(*fasthttp.RequestCtx) {
        return func(ctx *fasthttp.RequestCtx) {
                hystrix.Do(command, func() error {
                        h(ctx)
                        return nil
                }, func(err error) error {
                        // Handle failures
                        return err
                })
        }
}

func ReverseProxyHandler(ctx *fasthttp.RequestCtx) {
        req := &ctx.Request
        resp := &ctx.Response
        prepareRequest(req)
        if err := proxyClient.Do(req, resp); err != nil {
                ctx.Logger().Printf("error when proxying the request: %s", err)
        }
        postprocessResponse(resp)
}

func prepareRequest(req *fasthttp.Request) {
        // do not proxy "Connection" header.
        req.Header.Del("Connection")
        // strip other unneeded headers.

        // alter other request params before sending them to upstream host
}

func postprocessResponse(resp *fasthttp.Response) {
        // do not proxy "Connection" header
        resp.Header.Del("Connection")

        // strip other unneeded headers

        // alter other response data if needed
}

func main() {

//      r := router.New()

//      r.Use(HystrixHandler("mycommand"))
        //if err := fasthttp.ListenAndServe(":80", r.Handler); err != nil {
        if err := fasthttp.ListenAndServe(":80", HystrixHandler(ReverseProxyHandler,"mycommand")); err != nil {
//      if err := fasthttp.ListenAndServe(":80", HystrixHandler(); err != nil {
                log.Fatalf("error in fasthttp server: %s", err)
        }
}