mirror of
				https://github.com/TeaOSLab/EdgeCommon.git
				synced 2025-11-04 13:10:24 +08:00 
			
		
		
		
	
		
			
	
	
		
			107 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			107 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package scheduling
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
							 | 
						||
| 
								 | 
							
									"github.com/iwind/TeaGo/maps"
							 | 
						||
| 
								 | 
							
									"math/rand"
							 | 
						||
| 
								 | 
							
									"net/http"
							 | 
						||
| 
								 | 
							
									"time"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Sticky调度算法
							 | 
						||
| 
								 | 
							
								type StickyScheduling struct {
							 | 
						||
| 
								 | 
							
									Scheduling
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									count   uint32
							 | 
						||
| 
								 | 
							
									mapping map[string]CandidateInterface // code => candidate
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 启动
							 | 
						||
| 
								 | 
							
								func (this *StickyScheduling) Start() {
							 | 
						||
| 
								 | 
							
									this.mapping = map[string]CandidateInterface{}
							 | 
						||
| 
								 | 
							
									for _, c := range this.Candidates {
							 | 
						||
| 
								 | 
							
										for _, code := range c.CandidateCodes() {
							 | 
						||
| 
								 | 
							
											this.mapping[code] = c
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									this.count = uint32(len(this.Candidates))
							 | 
						||
| 
								 | 
							
									rand.Seed(time.Now().UnixNano())
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 获取下一个候选对象
							 | 
						||
| 
								 | 
							
								func (this *StickyScheduling) Next(call *shared.RequestCall) CandidateInterface {
							 | 
						||
| 
								 | 
							
									if this.count == 0 {
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									typeCode := call.Options.GetString("type")
							 | 
						||
| 
								 | 
							
									param := call.Options.GetString("param")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if call.Request == nil {
							 | 
						||
| 
								 | 
							
										return this.Candidates[uint32(rand.Int())%this.count]
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									code := ""
							 | 
						||
| 
								 | 
							
									if typeCode == "cookie" {
							 | 
						||
| 
								 | 
							
										cookie, err := call.Request.Cookie(param)
							 | 
						||
| 
								 | 
							
										if err == nil {
							 | 
						||
| 
								 | 
							
											code = cookie.Value
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									} else if typeCode == "header" {
							 | 
						||
| 
								 | 
							
										code = call.Request.Header.Get(param)
							 | 
						||
| 
								 | 
							
									} else if typeCode == "argument" {
							 | 
						||
| 
								 | 
							
										code = call.Request.URL.Query().Get(param)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									matched := false
							 | 
						||
| 
								 | 
							
									var c CandidateInterface = nil
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										if !matched && c != nil {
							 | 
						||
| 
								 | 
							
											codes := c.CandidateCodes()
							 | 
						||
| 
								 | 
							
											if len(codes) == 0 {
							 | 
						||
| 
								 | 
							
												return
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											if typeCode == "cookie" {
							 | 
						||
| 
								 | 
							
												call.AddResponseCall(func(resp http.ResponseWriter) {
							 | 
						||
| 
								 | 
							
													http.SetCookie(resp, &http.Cookie{
							 | 
						||
| 
								 | 
							
														Name:    param,
							 | 
						||
| 
								 | 
							
														Value:   codes[0],
							 | 
						||
| 
								 | 
							
														Path:    "/",
							 | 
						||
| 
								 | 
							
														Expires: time.Now().AddDate(0, 1, 0),
							 | 
						||
| 
								 | 
							
													})
							 | 
						||
| 
								 | 
							
												})
							 | 
						||
| 
								 | 
							
											} else {
							 | 
						||
| 
								 | 
							
												call.AddResponseCall(func(resp http.ResponseWriter) {
							 | 
						||
| 
								 | 
							
													resp.Header().Set(param, codes[0])
							 | 
						||
| 
								 | 
							
												})
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if len(code) == 0 {
							 | 
						||
| 
								 | 
							
										c = this.Candidates[uint32(rand.Int())%this.count]
							 | 
						||
| 
								 | 
							
										return c
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									found := false
							 | 
						||
| 
								 | 
							
									c, found = this.mapping[code]
							 | 
						||
| 
								 | 
							
									if !found {
							 | 
						||
| 
								 | 
							
										c = this.Candidates[uint32(rand.Int())%this.count]
							 | 
						||
| 
								 | 
							
										return c
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									matched = true
							 | 
						||
| 
								 | 
							
									return c
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// 获取简要信息
							 | 
						||
| 
								 | 
							
								func (this *StickyScheduling) Summary() maps.Map {
							 | 
						||
| 
								 | 
							
									return maps.Map{
							 | 
						||
| 
								 | 
							
										"code":        "sticky",
							 | 
						||
| 
								 | 
							
										"name":        "Sticky算法",
							 | 
						||
| 
								 | 
							
										"description": "利用Cookie、URL参数或者HTTP Header来指定后端服务器",
							 | 
						||
| 
								 | 
							
										"networks":    []string{"http"},
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 |