github.com/vicanso/pike@v1.0.1-0.20210630235453-9099e041f6ec/upstream/upstream.go (about)

     1  // MIT License
     2  
     3  // Copyright (c) 2020 Tree Xie
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    21  // SOFTWARE.
    22  
    23  // Upstream相关处理函数,根据策略选择适当的upstream server
    24  
    25  package upstream
    26  
    27  import (
    28  	"crypto/tls"
    29  	"net"
    30  	"net/http"
    31  	"net/url"
    32  	"sync"
    33  	"time"
    34  
    35  	"github.com/vicanso/elton"
    36  	"github.com/vicanso/elton/middleware"
    37  	"github.com/vicanso/hes"
    38  	"github.com/vicanso/pike/config"
    39  	"github.com/vicanso/pike/log"
    40  	"github.com/vicanso/pike/util"
    41  	us "github.com/vicanso/upstream"
    42  	"go.uber.org/zap"
    43  	"golang.org/x/net/http2"
    44  )
    45  
    46  type (
    47  	UpstreamServerConfig struct {
    48  		// 服务地址,如 http://127.0.0.1:8080
    49  		Addr string
    50  		// 是否备用
    51  		Backup bool
    52  	}
    53  	UpstreamServerStatus struct {
    54  		Addr    string
    55  		Healthy bool
    56  	}
    57  	UpstreamServerOption struct {
    58  		Name        string
    59  		HealthCheck string
    60  		Policy      string
    61  		// 是否启用h2c(http/2 over tcp)
    62  		EnableH2C bool
    63  		// 设置可接受的编码
    64  		AcceptEncoding string
    65  		// OnStatus on status
    66  		OnStatus OnStatus
    67  		Servers  []UpstreamServerConfig
    68  	}
    69  	upstreamServer struct {
    70  		servers      []UpstreamServerConfig
    71  		Proxy        elton.Handler
    72  		HTTPUpstream *us.HTTP
    73  		Option       *UpstreamServerOption
    74  	}
    75  	upstreamServers struct {
    76  		m *sync.Map
    77  	}
    78  	StatusInfo struct {
    79  		Name   string
    80  		URL    string
    81  		Status string
    82  	}
    83  	// OnStatus on status listener
    84  	OnStatus func(StatusInfo)
    85  )
    86  
    87  var defaultUpstreamServers = NewUpstreamServers(nil)
    88  var (
    89  	ErrUpstreamNotFound = &hes.Error{
    90  		StatusCode: http.StatusServiceUnavailable,
    91  		Message:    "Available Upstream Not Found",
    92  	}
    93  )
    94  
    95  // newTransport new a transport for http
    96  func newTransport(h2c bool) http.RoundTripper {
    97  	if h2c {
    98  		return &http2.Transport{
    99  			// 允许使用http的方式
   100  			AllowHTTP: true,
   101  			// tls的dial覆盖
   102  			DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
   103  				return net.Dial(network, addr)
   104  			},
   105  		}
   106  	}
   107  	return &http.Transport{
   108  		// TODO 暂时不配置proxy,后续再确认是否需要
   109  		// Proxy: http.ProxyFromEnvironment,
   110  		DialContext: (&net.Dialer{
   111  			Timeout:   30 * time.Second,
   112  			KeepAlive: 30 * time.Second,
   113  			DualStack: true,
   114  		}).DialContext,
   115  		ForceAttemptHTTP2: true,
   116  		MaxIdleConns:      500,
   117  		// 调整默认的每个host的最大连接因为缓存服务与backend可能会突发性的大量调用
   118  		MaxIdleConnsPerHost:   50,
   119  		IdleConnTimeout:       90 * time.Second,
   120  		TLSHandshakeTimeout:   10 * time.Second,
   121  		ExpectContinueTimeout: 1 * time.Second,
   122  	}
   123  }
   124  
   125  // newTargetPicker create a target pick function
   126  func newTargetPicker(uh *us.HTTP) middleware.ProxyTargetPicker {
   127  	return func(c *elton.Context) (*url.URL, middleware.ProxyDone, error) {
   128  		httpUpstream, done := uh.Next()
   129  		if httpUpstream == nil {
   130  			return nil, nil, ErrUpstreamNotFound
   131  		}
   132  		var proxyDone middleware.ProxyDone
   133  		// 返回了done(如最少连接数的策略)
   134  		if done != nil {
   135  			proxyDone = func(_ *elton.Context) {
   136  				done()
   137  			}
   138  		}
   139  		return httpUpstream.URL, proxyDone, nil
   140  	}
   141  }
   142  
   143  // newProxyMid new a proxy middleware
   144  func newProxyMid(opt UpstreamServerOption, uh *us.HTTP) elton.Handler {
   145  	return middleware.NewProxy(middleware.ProxyConfig{
   146  		Transport:    newTransport(opt.EnableH2C),
   147  		TargetPicker: newTargetPicker(uh),
   148  	})
   149  }
   150  
   151  // NewUpstreamServer new an upstream server
   152  func NewUpstreamServer(opt UpstreamServerOption) *upstreamServer {
   153  	uh := &us.HTTP{
   154  		Policy: opt.Policy,
   155  		Ping:   opt.HealthCheck,
   156  	}
   157  	for _, server := range opt.Servers {
   158  		// 添加失败的则忽略(地址配置有误则会添加失败)
   159  		if server.Backup {
   160  			_ = uh.AddBackup(server.Addr)
   161  		} else {
   162  			_ = uh.Add(server.Addr)
   163  		}
   164  	}
   165  	// 如果有添加on status事件
   166  	if opt.OnStatus != nil {
   167  		uh.OnStatus(func(status int32, upstream *us.HTTPUpstream) {
   168  			opt.OnStatus(StatusInfo{
   169  				Name:   opt.Name,
   170  				URL:    upstream.URL.String(),
   171  				Status: us.ConvertStatusToString(status),
   172  			})
   173  		})
   174  	}
   175  	// 先执行一次health check,获取当前可用服务列表
   176  	uh.DoHealthCheck()
   177  	// 后续需要定时检测upstream是否可用
   178  	go uh.StartHealthCheck()
   179  	return &upstreamServer{
   180  		servers:      opt.Servers,
   181  		HTTPUpstream: uh,
   182  		Option:       &opt,
   183  		Proxy:        newProxyMid(opt, uh),
   184  	}
   185  }
   186  
   187  // NewUpstreamServers new upstream servers
   188  func NewUpstreamServers(opts []UpstreamServerOption) *upstreamServers {
   189  	m := &sync.Map{}
   190  	for _, opt := range opts {
   191  		m.Store(opt.Name, NewUpstreamServer(opt))
   192  	}
   193  	return &upstreamServers{
   194  		m: m,
   195  	}
   196  }
   197  
   198  // Reset reset the upstream servers, remove not exists upstream servers and create new upstream server. If the upstream server is exists, then destroy the old one and add the new one.
   199  func (us *upstreamServers) Reset(opts []UpstreamServerOption) {
   200  	servers := util.MapDelete(us.m, func(key string) bool {
   201  		// 如果不存在的,则删除
   202  		exists := false
   203  		for _, opt := range opts {
   204  			if opt.Name == key {
   205  				exists = true
   206  				break
   207  			}
   208  		}
   209  		return !exists
   210  	})
   211  	for _, item := range servers {
   212  		server, _ := item.(*upstreamServer)
   213  		if server != nil {
   214  			server.Destroy()
   215  		}
   216  	}
   217  	for _, opt := range opts {
   218  		server := NewUpstreamServer(opt)
   219  		currentServer := us.Get(opt.Name)
   220  		// 先添加再删除
   221  		us.m.Store(opt.Name, server)
   222  		// 判断原来是否已存在此upstream server
   223  		// 如果存在,则删除
   224  		if currentServer != nil {
   225  			currentServer.Destroy()
   226  		}
   227  
   228  	}
   229  }
   230  
   231  // Get get upstream server by name
   232  func (us *upstreamServers) Get(name string) *upstreamServer {
   233  	value, ok := us.m.Load(name)
   234  	if !ok {
   235  		return nil
   236  	}
   237  	server, ok := value.(*upstreamServer)
   238  	if !ok {
   239  		return nil
   240  	}
   241  	return server
   242  }
   243  
   244  // Destroy destory the upstream server
   245  func (u *upstreamServer) Destroy() {
   246  	// 停止定时检测
   247  	u.HTTPUpstream.StopHealthCheck()
   248  }
   249  
   250  // GetServerStatusList get sever status list
   251  func (u *upstreamServer) GetServerStatusList() []UpstreamServerStatus {
   252  	statusList := make([]UpstreamServerStatus, 0)
   253  	availableServers := u.HTTPUpstream.GetAvailableUpstreamList()
   254  	for _, item := range u.servers {
   255  		healthy := false
   256  		for _, availableServer := range availableServers {
   257  			if availableServer.URL.String() == item.Addr {
   258  				healthy = true
   259  			}
   260  		}
   261  		statusList = append(statusList, UpstreamServerStatus{
   262  			Addr:    item.Addr,
   263  			Healthy: healthy,
   264  		})
   265  	}
   266  
   267  	return statusList
   268  }
   269  
   270  // Get get upstream server by name
   271  func Get(name string) *upstreamServer {
   272  	return defaultUpstreamServers.Get(name)
   273  }
   274  
   275  func onStatus(si StatusInfo) {
   276  	log.Default().Info("upstream status change",
   277  		zap.String("name", si.Name),
   278  		zap.String("status", si.Status),
   279  		zap.String("addr", si.URL),
   280  	)
   281  }
   282  
   283  func convertConfigs(configs []config.UpstreamConfig, fn OnStatus) []UpstreamServerOption {
   284  	opts := make([]UpstreamServerOption, 0)
   285  	for _, item := range configs {
   286  		servers := make([]UpstreamServerConfig, 0)
   287  		for _, server := range item.Servers {
   288  			servers = append(servers, UpstreamServerConfig{
   289  				Addr:   server.Addr,
   290  				Backup: server.Backup,
   291  			})
   292  		}
   293  		opts = append(opts, UpstreamServerOption{
   294  			Name:           item.Name,
   295  			HealthCheck:    item.HealthCheck,
   296  			Policy:         item.Policy,
   297  			EnableH2C:      item.EnableH2C,
   298  			AcceptEncoding: item.AcceptEncoding,
   299  			Servers:        servers,
   300  			OnStatus:       fn,
   301  		})
   302  	}
   303  	return opts
   304  }
   305  
   306  // Reset reset the upstream server
   307  func Reset(configs []config.UpstreamConfig) {
   308  	ResetWithOnStats(configs, onStatus)
   309  }
   310  
   311  // ResetWithOnStats reset with on stats
   312  func ResetWithOnStats(configs []config.UpstreamConfig, fn OnStatus) {
   313  	defaultUpstreamServers.Reset(convertConfigs(configs, fn))
   314  }