h2c

HTTP2已经在绝大部分的客户端中支持(浏览器或APP),当外网服务已全部支持http2之后,我们开始考虑内部服务间的调用,最开始直接使用基于tls的认证,内部服务的调用连接复用性很高,tls的处理并没有太影响性能。由于各类原因内部访问需要切换回http的形式,因此考查了h2c的处理方式,发现改造成本比想像中低太多,仅需简单添加几行代码则可支持。

下面是调整后的代码,调整后服务器能支持http1与http2两种形式,客户端调用则是强制指定使用http2。

package main

import (
    "crypto/tls"
    "fmt"
    "net"
    "net/http"
    "time"

    "github.com/vicanso/elton"
    "github.com/vicanso/elton/middleware"
    "golang.org/x/net/http2"
    "golang.org/x/net/http2/h2c"
)

var http2Client = &http.Client{
    // 强制使用http2
    Transport: &http2.Transport{
        // 允许使用http的方式
        AllowHTTP: true,
        // tls的dial覆盖
        DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
            return net.Dial(network, addr)
        },
    },
}

func main() {
    go func() {
        time.Sleep(time.Second)
        resp, err := http2Client.Get("http://127.0.0.1:3000/")
        if err != nil {
            panic(err)
        }
        fmt.Println(resp.Proto)
    }()

    e := elton.New()

    e.Use(middleware.NewDefaultResponder())

    e.GET("/", func(c *elton.Context) error {
        c.Body = "Hello, World!"
        return nil
    })
    // http1与http2均支持
    e.Server = &http.Server{
        Handler: h2c.NewHandler(e, &http2.Server{}),
    }

    err := e.ListenAndServe(":3000")
    if err != nil {
        panic(err)
    }
}

proxy

现在使用的反向代理,大部分都是仅使用http的方式,如果切换为使用http2是否能达到更好的性能。

支持h2c的服务:

package main

import (
    "bytes"
    "flag"
    "net/http"

    "github.com/vicanso/elton"
    "golang.org/x/net/http2"
    "golang.org/x/net/http2/h2c"
)

var enableHTTP2 bool

func main() {
    flag.BoolVar(&enableHTTP2, "http2", false, "enable http2")
    flag.Parse()

    e := elton.New()

    e.GET("/", func(c *elton.Context) error {
        c.BodyBuffer = bytes.NewBufferString("Hello, World!")
        return nil
    })
    if enableHTTP2 {
        e.Server = &http.Server{
            Handler: h2c.NewHandler(e, &http2.Server{}),
        }
    }

    err := e.ListenAndServe(":3000")
    if err != nil {
        panic(err)
    }
}

使用http2转发的代理服务(对外提供的还是http服务):

package main

import (
    "crypto/tls"
    "flag"
    "net"
    "net/url"

    "github.com/vicanso/elton"
    "github.com/vicanso/elton/middleware"
    "golang.org/x/net/http2"
)

var enableHTTP2 bool

func main() {
    flag.BoolVar(&enableHTTP2, "http2", false, "enable http2")
    flag.Parse()
    e := elton.New()

    target, _ := url.Parse("http://127.0.0.1:3000")

    config := middleware.ProxyConfig{
        Target: target,
    }
    if enableHTTP2 {
        config.Transport = &http2.Transport{
            // 允许使用http的方式
            AllowHTTP: true,
            // tls的dial覆盖
            DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
                return net.Dial(network, addr)
            },
        }
    }

    e.GET("/*", middleware.NewProxy(config))

    err := e.ListenAndServe(":3001")
    if err != nil {
        panic(err)
    }
}

不经过代理的压测:

wrk 'http://127.0.0.1:3000/'
Running 10s test @ http://127.0.0.1:3000/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   125.79us   39.80us   2.51ms   83.81%
    Req/Sec    34.40k     3.41k   51.13k    74.75%
  691033 requests in 10.10s, 85.67MB read
Requests/sec:  68419.86
Transfer/sec:      8.48MB

经过代理(http2)的压测:

wrk 'http://127.0.0.1:3001/'
Running 10s test @ http://127.0.0.1:3001/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   503.05us  181.08us   4.34ms   74.34%
    Req/Sec     9.91k     0.93k   12.01k    74.75%
  199120 requests in 10.10s, 24.69MB read
Requests/sec:  19714.88
Transfer/sec:      2.44MB

经过代理(http1)的压测

wrk 'http://127.0.0.1:3001/'
Running 10s test @ http://127.0.0.1:3001/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.01ms    3.39ms  62.64ms   98.40%
    Req/Sec     7.73k     0.97k    8.94k    87.50%
  153890 requests in 10.00s, 19.08MB read
Requests/sec:  15382.86
Transfer/sec:      1.91MB

上面的结果可以看出,使用http2转发的形式,性能上的损耗的确少一些。

Last updated