1

I am trying to do a small prototype for a gRPC proxy service (based on github.com/buaazp/fasthttprouter ) which make calls to a gRPC backend

My prototype is based on gRPC helloworld example:

package main

import (
    pb "../proto/helloworld"
    "context"
    "fmt"
    "github.com/buaazp/fasthttprouter"
    grpcpool "github.com/processout/grpc-go-pool"
    "github.com/valyala/fasthttp"
    "google.golang.org/grpc"
    "log"
    "os"
    "time"
)

const (
    address     = "localhost:50051"
)

func main() {
    // grpc client pool

    var factory grpcpool.Factory

    factory = func() (*grpc.ClientConn, error) {
        conn, err := grpc.Dial(address, grpc.WithInsecure())
        if err != nil {
            log.Fatalf("Failed to start gRPC connection: %v", err)
        }
        return conn, err
    }

    pool, err := grpcpool.New(factory, 5, 5, time.Second)

    if err != nil {
        log.Fatalf("Failed to create gRPC pool: %v", err)
    }

    // routers and handlers

    router := fasthttprouter.New()
    router.GET("/without_gr", func(ctx *fasthttp.RequestCtx) {

        // ab -c 100 -n 1000 http://127.0.0.1:1111/without_gr
        // 8500 rps without goroutine

        conn, _ := pool.Get(ctx)
        defer conn.Close()

        if err != nil {
            _, _ = fmt.Fprintf(ctx, "Get pool failed")
            return
        }

        client := pb.NewGreeterClient(conn.ClientConn)

        _, _ = client.SayHello(ctx, &pb.HelloRequest{Name: "Pool ON"})

        _, _ = fmt.Fprintf(ctx, "Welcome to my website! Pool ON")
    })

    router.GET("/with_gr", func(ctx *fasthttp.RequestCtx) {
        go func(ctx *fasthttp.RequestCtx) {

            // ab -c 100 -n 1000 http://127.0.0.1:1111/with_gr
            // 14000 rps with goroutine

            conn, _ := pool.Get(ctx)
            defer conn.Close()

            if err != nil {
                _, _ = fmt.Fprintf(ctx, "Get pool failed")
                return
            }

            client := pb.NewGreeterClient(conn.ClientConn)

            _, _ = client.SayHello(ctx, &pb.HelloRequest{Name: "Pool ON"})

            _, _ = fmt.Fprintf(ctx, "Welcome to my website! Pool ON")
        }(ctx)
    })

    _ = fasthttp.ListenAndServe(":1111", router.Handler)
}

The results are:

  • http call with goroutine gives me 14K rps
  • http call without goroutine only 8K rps

My questions:

  • Is that correct sample?
  • Do i need run all api calls with goroutine wrapping?
Ali Mamedov
  • 5,116
  • 3
  • 33
  • 47
  • Can you highlight the difference between `with_gr` and `without_gr`. Its kind of looking similar to me – Ankit Deshpande May 02 '19 at 19:14
  • @AnkitDeshpande, there was a mistake in my answer. Thx U! – Ali Mamedov May 02 '19 at 19:17
  • 4
    each handler is already in it's own goroutine, dispatching a new goroutine and returning immediately doesn't make any sense, and is faster because it doesn't wait for anything, not to mention it's invalid to do so in the first place. From the `fasthttp` docs: `RequestHandler should avoid holding references to incoming RequestCtx and/or its' members after the return.`, which is exactly what you're doing. – JimB May 02 '19 at 19:43
  • 4
    unrelated to this code, but don't use `ab` for this. You're testing a modern http2 server using a slow http/1.0 client without even turning on keepalive. – JimB May 02 '19 at 19:52

0 Answers0