large refactoring, bug fixes, performance improvement

This commit is contained in:
yusing
2024-03-18 04:51:59 +00:00
parent eee6ff4f15
commit a52b1bcadd
27 changed files with 1530 additions and 419 deletions

View File

@@ -2,20 +2,57 @@ package main
import (
"fmt"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"github.com/golang/glog"
"github.com/sirupsen/logrus"
)
/**
A small mod on net/http/httputil.ReverseProxy
Before mod:
root@http-benchmark-client:~# wrk -t 10 -c 200 -d 10s --latency http://bench.6uo.me/bench
Running 10s test @ http://bench.6uo.me/bench
10 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.02ms 4.34ms 102.70ms 94.90%
Req/Sec 8.06k 1.17k 9.99k 79.86%
Latency Distribution
50% 2.38ms
75% 4.00ms
90% 5.93ms
99% 11.90ms
808813 requests in 10.10s, 78.68MB read
Requests/sec: 80079.47
Transfer/sec: 7.79MB
After mod:
root@http-benchmark-client:~# wrk -t 10 -c 200 -d 10s --latency http://bench.6uo.me/bench
Running 10s test @ http://bench.6uo.me/bench
10 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.77ms 5.64ms 118.14ms 99.07%
Req/Sec 16.59k 2.22k 19.65k 87.30%
Latency Distribution
50% 1.11ms
75% 1.85ms
90% 2.74ms
99% 6.68ms
1665286 requests in 10.10s, 200.11MB read
Requests/sec: 164880.11
Transfer/sec: 19.81MB
**/
type HTTPRoute struct {
Alias string
Url *url.URL
Path string
PathMode string
Proxy *httputil.ReverseProxy
Proxy *ReverseProxy
l logrus.FieldLogger
}
func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
@@ -24,8 +61,14 @@ func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
return nil, err
}
proxy := httputil.NewSingleHostReverseProxy(url)
proxy.Transport = transport
var tr *http.Transport
if config.NoTLSVerify {
tr = transportNoTLS
} else {
tr = transport
}
proxy := NewSingleHostReverseProxy(url, tr)
if !isValidProxyPathMode(config.PathMode) {
return nil, fmt.Errorf("invalid path mode: %s", config.PathMode)
@@ -37,22 +80,21 @@ func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
Path: config.Path,
Proxy: proxy,
PathMode: config.PathMode,
l: hrlog.WithFields(logrus.Fields{
"alias": config.Alias,
"path": config.Path,
"path_mode": config.PathMode,
}),
}
director := proxy.Director
proxy.Director = nil
initRewrite := func(pr *httputil.ProxyRequest) {
director(pr.Out)
}
rewrite := initRewrite
var rewrite func(*ProxyRequest)
switch {
case config.Path == "", config.PathMode == ProxyPathMode_Forward:
break
rewrite = proxy.Rewrite
case config.PathMode == ProxyPathMode_Sub:
rewrite = func(pr *httputil.ProxyRequest) {
initRewrite(pr)
rewrite = func(pr *ProxyRequest) {
proxy.Rewrite(pr)
// disable compression
pr.Out.Header.Set("Accept-Encoding", "identity")
// remove path prefix
@@ -61,9 +103,7 @@ func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
route.Proxy.ModifyResponse = func(r *http.Response) error {
contentType, ok := r.Header["Content-Type"]
if !ok || len(contentType) == 0 {
if glog.V(3) {
glog.Infof("[Path sub] unknown content type for %s", r.Request.URL.String())
}
route.l.Debug("unknown content type for", r.Request.URL.String())
return nil
}
// disable cache
@@ -76,28 +116,27 @@ func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
case strings.HasPrefix(contentType[0], "application/javascript"):
err = utils.respJSSubPath(r, config.Path)
default:
glog.V(4).Infof("[Path sub] unknown content type(s): %s", contentType)
route.l.Debug("unknown content type(s): ", contentType)
}
if err != nil {
err = fmt.Errorf("[Path sub] failed to remove path prefix %s: %v", config.Path, err)
err = fmt.Errorf("failed to remove path prefix %s: %v", config.Path, err)
route.l.WithField("action", "path_sub").Error(err)
r.Status = err.Error()
r.StatusCode = http.StatusInternalServerError
}
return err
}
default:
rewrite = func(pr *httputil.ProxyRequest) {
initRewrite(pr)
rewrite = func(pr *ProxyRequest) {
proxy.Rewrite(pr)
pr.Out.URL.Path = strings.TrimPrefix(pr.Out.URL.Path, config.Path)
}
}
if glog.V(3) {
route.Proxy.Rewrite = func(pr *httputil.ProxyRequest) {
if logLevel == logrus.DebugLevel {
route.Proxy.Rewrite = func(pr *ProxyRequest) {
rewrite(pr)
r := pr.In
glog.Infof("[Request] %s %s%s", r.Method, r.Host, r.URL.Path)
glog.V(5).InfoDepthf(1, "Headers: %v", r.Header)
route.l.Debug("Request headers: ", pr.In.Header)
}
} else {
route.Proxy.Rewrite = rewrite
@@ -114,7 +153,7 @@ func (p *httpLoadBalancePool) Pick() *HTTPRoute {
}
func (r *HTTPRoute) RemoveFromRoutes() {
routes.HTTPRoutes.Delete(r.Alias)
httpRoutes.Delete(r.Alias)
}
// dummy implementation for Route interface
@@ -144,24 +183,30 @@ func redirectToTLS(w http.ResponseWriter, r *http.Request) {
func findHTTPRoute(host string, path string) (*HTTPRoute, error) {
subdomain := strings.Split(host, ".")[0]
routeMap, ok := routes.HTTPRoutes.UnsafeGet(subdomain)
if !ok {
return nil, fmt.Errorf("no matching route for subdomain %s", subdomain)
routeMap, ok := httpRoutes.UnsafeGet(subdomain)
if ok {
return routeMap.FindMatch(path)
}
return routeMap.FindMatch(path)
return nil, fmt.Errorf("no matching route for subdomain %s", subdomain)
}
func httpProxyHandler(w http.ResponseWriter, r *http.Request) {
route, err := findHTTPRoute(r.Host, r.URL.Path)
if err != nil {
err = fmt.Errorf("[Request] failed %s %s%s, error: %v",
err = fmt.Errorf("request failed %s %s%s, error: %v",
r.Method,
r.Host,
r.URL.Path,
err,
)
logrus.Error(err)
http.Error(w, err.Error(), http.StatusNotFound)
return
}
route.Proxy.ServeHTTP(w, r)
}
// alias -> (path -> routes)
type HTTPRoutes = SafeMap[string, *pathPoolMap]
var httpRoutes HTTPRoutes = NewSafeMap[string](newPathPoolMap)