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 }