mirror of
				https://github.com/TeaOSLab/EdgeNode.git
				synced 2025-11-04 07:40:56 +08:00 
			
		
		
		
	实现stale cache读取
This commit is contained in:
		@@ -22,6 +22,7 @@ type Item struct {
 | 
				
			|||||||
	Type       ItemType `json:"type"`
 | 
						Type       ItemType `json:"type"`
 | 
				
			||||||
	Key        string   `json:"key"`
 | 
						Key        string   `json:"key"`
 | 
				
			||||||
	ExpiredAt  int64    `json:"expiredAt"`
 | 
						ExpiredAt  int64    `json:"expiredAt"`
 | 
				
			||||||
 | 
						StaleAt    int64    `json:"staleAt"`
 | 
				
			||||||
	HeaderSize int64    `json:"headerSize"`
 | 
						HeaderSize int64    `json:"headerSize"`
 | 
				
			||||||
	BodySize   int64    `json:"bodySize"`
 | 
						BodySize   int64    `json:"bodySize"`
 | 
				
			||||||
	MetaSize   int64    `json:"metaSize"`
 | 
						MetaSize   int64    `json:"metaSize"`
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ import (
 | 
				
			|||||||
	_ "github.com/mattn/go-sqlite3"
 | 
						_ "github.com/mattn/go-sqlite3"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"sync/atomic"
 | 
						"sync/atomic"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -104,6 +105,12 @@ func (this *FileList) Init() error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 检查staleAt字段
 | 
				
			||||||
 | 
						err = this.checkStaleAtField()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 读取总数量
 | 
						// 读取总数量
 | 
				
			||||||
	row := this.db.QueryRow(`SELECT COUNT(*) FROM "` + this.itemsTableName + `"`)
 | 
						row := this.db.QueryRow(`SELECT COUNT(*) FROM "` + this.itemsTableName + `"`)
 | 
				
			||||||
	if row.Err() != nil {
 | 
						if row.Err() != nil {
 | 
				
			||||||
@@ -122,7 +129,7 @@ func (this *FileList) Init() error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.insertStmt, err = this.db.Prepare(`INSERT INTO "` + this.itemsTableName + `" ("hash", "key", "headerSize", "bodySize", "metaSize", "expiredAt", "host", "serverId", "createdAt") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
 | 
						this.insertStmt, err = this.db.Prepare(`INSERT INTO "` + this.itemsTableName + `" ("hash", "key", "headerSize", "bodySize", "metaSize", "expiredAt", "staleAt", "host", "serverId", "createdAt") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -142,7 +149,7 @@ func (this *FileList) Init() error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.purgeStmt, err = this.db.Prepare(`SELECT "hash" FROM "` + this.itemsTableName + `" WHERE expiredAt<=? LIMIT ?`)
 | 
						this.purgeStmt, err = this.db.Prepare(`SELECT "hash" FROM "` + this.itemsTableName + `" WHERE staleAt<=? LIMIT ?`)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -182,7 +189,11 @@ func (this *FileList) Add(hash string, item *Item) error {
 | 
				
			|||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, err := this.insertStmt.Exec(hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.Host, item.ServerId, utils.UnixTime())
 | 
						if item.StaleAt == 0 {
 | 
				
			||||||
 | 
							item.StaleAt = item.ExpiredAt
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := this.insertStmt.Exec(hash, item.Key, item.HeaderSize, item.BodySize, item.MetaSize, item.ExpiredAt, item.StaleAt, item.Host, item.ServerId, utils.UnixTime())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -474,6 +485,8 @@ func (this *FileList) Close() error {
 | 
				
			|||||||
// 初始化
 | 
					// 初始化
 | 
				
			||||||
func (this *FileList) initTables(db *sql.DB, times int) error {
 | 
					func (this *FileList) initTables(db *sql.DB, times int) error {
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							// expiredAt - 过期时间,用来判断有无过期
 | 
				
			||||||
 | 
							// staleAt - 陈旧最大时间,用来清理缓存
 | 
				
			||||||
		_, err := db.Exec(`CREATE TABLE IF NOT EXISTS "` + this.itemsTableName + `" (
 | 
							_, err := db.Exec(`CREATE TABLE IF NOT EXISTS "` + this.itemsTableName + `" (
 | 
				
			||||||
  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
 | 
					  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
  "hash" varchar(32),
 | 
					  "hash" varchar(32),
 | 
				
			||||||
@@ -482,6 +495,7 @@ func (this *FileList) initTables(db *sql.DB, times int) error {
 | 
				
			|||||||
  "bodySize" integer DEFAULT 0,
 | 
					  "bodySize" integer DEFAULT 0,
 | 
				
			||||||
  "metaSize" integer DEFAULT 0,
 | 
					  "metaSize" integer DEFAULT 0,
 | 
				
			||||||
  "expiredAt" integer DEFAULT 0,
 | 
					  "expiredAt" integer DEFAULT 0,
 | 
				
			||||||
 | 
					  "staleAt" integer DEFAULT 0,
 | 
				
			||||||
  "createdAt" integer DEFAULT 0,
 | 
					  "createdAt" integer DEFAULT 0,
 | 
				
			||||||
  "host" varchar(128),
 | 
					  "host" varchar(128),
 | 
				
			||||||
  "serverId" integer
 | 
					  "serverId" integer
 | 
				
			||||||
@@ -497,6 +511,11 @@ ON "` + this.itemsTableName + `" (
 | 
				
			|||||||
  "expiredAt" ASC
 | 
					  "expiredAt" ASC
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE INDEX IF NOT EXISTS "staleAt"
 | 
				
			||||||
 | 
					ON "` + this.itemsTableName + `" (
 | 
				
			||||||
 | 
					  "staleAt" ASC
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE UNIQUE INDEX IF NOT EXISTS "hash"
 | 
					CREATE UNIQUE INDEX IF NOT EXISTS "hash"
 | 
				
			||||||
ON "` + this.itemsTableName + `" (
 | 
					ON "` + this.itemsTableName + `" (
 | 
				
			||||||
  "hash" ASC
 | 
					  "hash" ASC
 | 
				
			||||||
@@ -579,3 +598,26 @@ func (this *FileList) removeOldTables() error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (this *FileList) checkStaleAtField() error {
 | 
				
			||||||
 | 
						rows, err := this.db.Query(`SELECT staleAt FROM "` + this.itemsTableName + `"`)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if strings.Contains(err.Error(), "no such column: staleAt") { // 暂时没有更好的判断方法
 | 
				
			||||||
 | 
								_, err = this.db.Exec(`ALTER TABLE "` + this.itemsTableName + `" ADD COLUMN staleAt integer DEFAULT 0`)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								_, err = this.db.Exec(`UPDATE "` + this.itemsTableName + `" SET staleAt=expiredAt`)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							_ = rows.Close()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -204,14 +204,19 @@ func (this *FileStorage) Init() error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *FileStorage) OpenReader(key string) (Reader, error) {
 | 
					func (this *FileStorage) OpenReader(key string, useStale bool) (Reader, error) {
 | 
				
			||||||
	return this.openReader(key, true)
 | 
						return this.openReader(key, true, useStale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *FileStorage) openReader(key string, allowMemory bool) (Reader, error) {
 | 
					func (this *FileStorage) openReader(key string, allowMemory bool, useStale bool) (Reader, error) {
 | 
				
			||||||
 | 
						// 使用陈旧缓存的时候,我们认为是短暂的,只需要从文件里检查即可
 | 
				
			||||||
 | 
						if useStale {
 | 
				
			||||||
 | 
							allowMemory = false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 先尝试内存缓存
 | 
						// 先尝试内存缓存
 | 
				
			||||||
	if allowMemory && this.memoryStorage != nil {
 | 
						if allowMemory && this.memoryStorage != nil {
 | 
				
			||||||
		reader, err := this.memoryStorage.OpenReader(key)
 | 
							reader, err := this.memoryStorage.OpenReader(key, useStale)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			return reader, err
 | 
								return reader, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -219,6 +224,17 @@ func (this *FileStorage) openReader(key string, allowMemory bool) (Reader, error
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	hash, path := this.keyPath(key)
 | 
						hash, path := this.keyPath(key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 检查文件记录是否已过期
 | 
				
			||||||
 | 
						if !useStale {
 | 
				
			||||||
 | 
							exists, err := this.list.Exist(hash)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !exists {
 | 
				
			||||||
 | 
								return nil, ErrNotFound
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO 尝试使用mmap加快读取速度
 | 
						// TODO 尝试使用mmap加快读取速度
 | 
				
			||||||
	var isOk = false
 | 
						var isOk = false
 | 
				
			||||||
	fp, err := os.OpenFile(path, os.O_RDONLY, 0444)
 | 
						fp, err := os.OpenFile(path, os.O_RDONLY, 0444)
 | 
				
			||||||
@@ -235,15 +251,6 @@ func (this *FileStorage) openReader(key string, allowMemory bool) (Reader, error
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 检查文件记录是否已过期
 | 
					 | 
				
			||||||
	exists, err := this.list.Exist(hash)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if !exists {
 | 
					 | 
				
			||||||
		return nil, ErrNotFound
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	reader := NewFileReader(fp)
 | 
						reader := NewFileReader(fp)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -923,7 +930,7 @@ func (this *FileStorage) hotLoop() {
 | 
				
			|||||||
	this.hotMap = map[string]*HotItem{}
 | 
						this.hotMap = map[string]*HotItem{}
 | 
				
			||||||
	this.hotMapLocker.Unlock()
 | 
						this.hotMapLocker.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 取Top10
 | 
						// 取Top10写入内存
 | 
				
			||||||
	if len(result) > 0 {
 | 
						if len(result) > 0 {
 | 
				
			||||||
		sort.Slice(result, func(i, j int) bool {
 | 
							sort.Slice(result, func(i, j int) bool {
 | 
				
			||||||
			return result[i].Hits > result[j].Hits
 | 
								return result[i].Hits > result[j].Hits
 | 
				
			||||||
@@ -937,7 +944,7 @@ func (this *FileStorage) hotLoop() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		var buf = make([]byte, 32*1024)
 | 
							var buf = make([]byte, 32*1024)
 | 
				
			||||||
		for _, item := range result[:size] {
 | 
							for _, item := range result[:size] {
 | 
				
			||||||
			reader, err := this.openReader(item.Key, false)
 | 
								reader, err := this.openReader(item.Key, false, false)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ type StorageInterface interface {
 | 
				
			|||||||
	Init() error
 | 
						Init() error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// OpenReader 读取缓存
 | 
						// OpenReader 读取缓存
 | 
				
			||||||
	OpenReader(key string) (Reader, error)
 | 
						OpenReader(key string, useStale bool) (reader Reader, err error)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// OpenWriter 打开缓存写入器等待写入
 | 
						// OpenWriter 打开缓存写入器等待写入
 | 
				
			||||||
	OpenWriter(key string, expiredAt int64, status int) (Writer, error)
 | 
						OpenWriter(key string, expiredAt int64, status int) (Writer, error)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -105,7 +105,7 @@ func (this *MemoryStorage) Init() error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// OpenReader 读取缓存
 | 
					// OpenReader 读取缓存
 | 
				
			||||||
func (this *MemoryStorage) OpenReader(key string) (Reader, error) {
 | 
					func (this *MemoryStorage) OpenReader(key string, useStale bool) (Reader, error) {
 | 
				
			||||||
	hash := this.hash(key)
 | 
						hash := this.hash(key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this.locker.RLock()
 | 
						this.locker.RLock()
 | 
				
			||||||
@@ -115,7 +115,7 @@ func (this *MemoryStorage) OpenReader(key string) (Reader, error) {
 | 
				
			|||||||
		return nil, ErrNotFound
 | 
							return nil, ErrNotFound
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if item.ExpiredAt > utils.UnixTime() {
 | 
						if useStale || (item.ExpiredAt > utils.UnixTime()) {
 | 
				
			||||||
		reader := NewMemoryReader(item)
 | 
							reader := NewMemoryReader(item)
 | 
				
			||||||
		err := reader.Init()
 | 
							err := reader.Init()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -236,7 +236,7 @@ func (this *APIStream) handleReadCache(message *pb.NodeStreamMessage) error {
 | 
				
			|||||||
		}()
 | 
							}()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	reader, err := storage.OpenReader(msg.Key)
 | 
						reader, err := storage.OpenReader(msg.Key, false)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if err == caches.ErrNotFound {
 | 
							if err == caches.ErrNotFound {
 | 
				
			||||||
			this.replyFail(message.RequestId, "key not found")
 | 
								this.replyFail(message.RequestId, "key not found")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,11 +69,14 @@ type HTTPRequest struct {
 | 
				
			|||||||
	rewriteRule          *serverconfigs.HTTPRewriteRule    // 匹配到的重写规则
 | 
						rewriteRule          *serverconfigs.HTTPRewriteRule    // 匹配到的重写规则
 | 
				
			||||||
	rewriteReplace       string                            // 重写规则的目标
 | 
						rewriteReplace       string                            // 重写规则的目标
 | 
				
			||||||
	rewriteIsExternalURL bool                              // 重写目标是否为外部URL
 | 
						rewriteIsExternalURL bool                              // 重写目标是否为外部URL
 | 
				
			||||||
	cacheRef             *serverconfigs.HTTPCacheRef       // 缓存设置
 | 
					
 | 
				
			||||||
	cacheKey             string                            // 缓存使用的Key
 | 
						cacheRef         *serverconfigs.HTTPCacheRef // 缓存设置
 | 
				
			||||||
	isCached             bool                              // 是否已经被缓存
 | 
						cacheKey         string                      // 缓存使用的Key
 | 
				
			||||||
	isAttack             bool                              // 是否是攻击请求
 | 
						isCached         bool                        // 是否已经被缓存
 | 
				
			||||||
	requestBodyData      []byte                            // 读取的Body内容
 | 
						cacheCanTryStale bool                        // 是否可以尝试使用Stale缓存
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isAttack        bool   // 是否是攻击请求
 | 
				
			||||||
 | 
						requestBodyData []byte // 读取的Body内容
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// WAF相关
 | 
						// WAF相关
 | 
				
			||||||
	firewallPolicyId    int64
 | 
						firewallPolicyId    int64
 | 
				
			||||||
@@ -137,7 +140,7 @@ func (this *HTTPRequest) Do() {
 | 
				
			|||||||
	// Web配置
 | 
						// Web配置
 | 
				
			||||||
	err := this.configureWeb(this.Server.Web, true, 0)
 | 
						err := this.configureWeb(this.Server.Web, true, 0)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		this.write50x(err, http.StatusInternalServerError)
 | 
							this.write50x(err, http.StatusInternalServerError, false)
 | 
				
			||||||
		this.doEnd()
 | 
							this.doEnd()
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -224,7 +227,7 @@ func (this *HTTPRequest) doBegin() {
 | 
				
			|||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		this.requestBodyData, err = ioutil.ReadAll(io.LimitReader(this.RawReq.Body, AccessLogMaxRequestBodySize))
 | 
							this.requestBodyData, err = ioutil.ReadAll(io.LimitReader(this.RawReq.Body, AccessLogMaxRequestBodySize))
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			this.write50x(err, http.StatusBadGateway)
 | 
								this.write50x(err, http.StatusBadGateway, false)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		this.RawReq.Body = ioutil.NopCloser(io.MultiReader(bytes.NewBuffer(this.requestBodyData), this.RawReq.Body))
 | 
							this.RawReq.Body = ioutil.NopCloser(io.MultiReader(bytes.NewBuffer(this.requestBodyData), this.RawReq.Body))
 | 
				
			||||||
@@ -253,7 +256,7 @@ func (this *HTTPRequest) doBegin() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// 缓存
 | 
						// 缓存
 | 
				
			||||||
	if this.web.Cache != nil && this.web.Cache.IsOn {
 | 
						if this.web.Cache != nil && this.web.Cache.IsOn {
 | 
				
			||||||
		if this.doCacheRead() {
 | 
							if this.doCacheRead(false) {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,7 +33,7 @@ func (this *HTTPRequest) doAuth() (shouldStop bool) {
 | 
				
			|||||||
			return writer.StatusCode(), nil
 | 
								return writer.StatusCode(), nil
 | 
				
			||||||
		}, this.Format)
 | 
							}, this.Format)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			this.write50x(err, http.StatusInternalServerError)
 | 
								this.write50x(err, http.StatusInternalServerError, false)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if b {
 | 
							if b {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 读取缓存
 | 
					// 读取缓存
 | 
				
			||||||
func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
 | 
					func (this *HTTPRequest) doCacheRead(useStale bool) (shouldStop bool) {
 | 
				
			||||||
	cachePolicy := this.Server.HTTPCachePolicy
 | 
						cachePolicy := this.Server.HTTPCachePolicy
 | 
				
			||||||
	if cachePolicy == nil || !cachePolicy.IsOn {
 | 
						if cachePolicy == nil || !cachePolicy.IsOn {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -148,11 +148,6 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
 | 
				
			|||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	buf := bytePool32k.Get()
 | 
					 | 
				
			||||||
	defer func() {
 | 
					 | 
				
			||||||
		bytePool32k.Put(buf)
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var reader caches.Reader
 | 
						var reader caches.Reader
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -161,16 +156,20 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
 | 
				
			|||||||
		this.web.WebP.IsOn &&
 | 
							this.web.WebP.IsOn &&
 | 
				
			||||||
		this.web.WebP.MatchRequest(filepath.Ext(this.requestPath()), this.Format) &&
 | 
							this.web.WebP.MatchRequest(filepath.Ext(this.requestPath()), this.Format) &&
 | 
				
			||||||
		this.web.WebP.MatchAccept(this.requestHeader("Accept")) {
 | 
							this.web.WebP.MatchAccept(this.requestHeader("Accept")) {
 | 
				
			||||||
		reader, _ = storage.OpenReader(key + webpSuffix)
 | 
							reader, _ = storage.OpenReader(key+webpSuffix, useStale)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 检查正常的文件
 | 
						// 检查正常的文件
 | 
				
			||||||
	if reader == nil {
 | 
						if reader == nil {
 | 
				
			||||||
		reader, err = storage.OpenReader(key)
 | 
							reader, err = storage.OpenReader(key, useStale)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if err == caches.ErrNotFound {
 | 
								if err == caches.ErrNotFound {
 | 
				
			||||||
				// cache相关变量
 | 
									// cache相关变量
 | 
				
			||||||
				this.varMapping["cache.status"] = "MISS"
 | 
									this.varMapping["cache.status"] = "MISS"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if this.web.Cache.Stale != nil && this.web.Cache.Stale.IsOn {
 | 
				
			||||||
 | 
										this.cacheCanTryStale = true
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -187,6 +186,12 @@ func (this *HTTPRequest) doCacheRead() (shouldStop bool) {
 | 
				
			|||||||
	this.varMapping["cache.status"] = "HIT"
 | 
						this.varMapping["cache.status"] = "HIT"
 | 
				
			||||||
	this.logAttrs["cache.status"] = "HIT"
 | 
						this.logAttrs["cache.status"] = "HIT"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 准备Buffer
 | 
				
			||||||
 | 
						buf := bytePool32k.Get()
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							bytePool32k.Put(buf)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 读取Header
 | 
						// 读取Header
 | 
				
			||||||
	headerBuf := []byte{}
 | 
						headerBuf := []byte{}
 | 
				
			||||||
	err = reader.ReadHeader(buf, func(n int) (goNext bool, err error) {
 | 
						err = reader.ReadHeader(buf, func(n int) (goNext bool, err error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
package nodes
 | 
					package nodes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/iwind/TeaGo/lists"
 | 
				
			||||||
	"github.com/iwind/TeaGo/types"
 | 
						"github.com/iwind/TeaGo/types"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -25,11 +26,24 @@ func (this *HTTPRequest) writeCode(code int) {
 | 
				
			|||||||
	_, _ = this.writer.Write([]byte(types.String(code) + " " + http.StatusText(code) + ": '" + this.requestFullURL() + "'" + " (Request Id: " + this.requestId + ")"))
 | 
						_, _ = this.writer.Write([]byte(types.String(code) + " " + http.StatusText(code) + ": '" + this.requestFullURL() + "'" + " (Request Id: " + this.requestId + ")"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (this *HTTPRequest) write50x(err error, statusCode int) {
 | 
					func (this *HTTPRequest) write50x(err error, statusCode int, canTryStale bool) {
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		this.addError(err)
 | 
							this.addError(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 尝试从缓存中恢复
 | 
				
			||||||
 | 
						if canTryStale &&
 | 
				
			||||||
 | 
							this.cacheCanTryStale &&
 | 
				
			||||||
 | 
							this.web.Cache.Stale != nil &&
 | 
				
			||||||
 | 
							this.web.Cache.Stale.IsOn &&
 | 
				
			||||||
 | 
							(len(this.web.Cache.Stale.Status) == 0 || lists.ContainsInt(this.web.Cache.Stale.Status, statusCode)) {
 | 
				
			||||||
 | 
							ok := this.doCacheRead(true)
 | 
				
			||||||
 | 
							if ok {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 显示自定义页面
 | 
				
			||||||
	if this.doPage(statusCode) {
 | 
						if this.doPage(statusCode) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,7 +81,7 @@ func (this *HTTPRequest) doFastcgi() (shouldStop bool) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	client, err := fcgi.SharedPool(fastcgi.Network(), fastcgi.RealAddress(), uint(poolSize)).Client()
 | 
						client, err := fcgi.SharedPool(fastcgi.Network(), fastcgi.RealAddress(), uint(poolSize)).Client()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		this.write50x(err, http.StatusInternalServerError)
 | 
							this.write50x(err, http.StatusInternalServerError, false)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -159,13 +159,13 @@ func (this *HTTPRequest) doFastcgi() (shouldStop bool) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	resp, stderr, err := client.Call(fcgiReq)
 | 
						resp, stderr, err := client.Call(fcgiReq)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		this.write50x(err, http.StatusInternalServerError)
 | 
							this.write50x(err, http.StatusInternalServerError, false)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(stderr) > 0 {
 | 
						if len(stderr) > 0 {
 | 
				
			||||||
		err := errors.New("Fastcgi Error: " + strings.TrimSpace(string(stderr)) + " script: " + maps.NewMap(params).GetString("SCRIPT_FILENAME"))
 | 
							err := errors.New("Fastcgi Error: " + strings.TrimSpace(string(stderr)) + " script: " + maps.NewMap(params).GetString("SCRIPT_FILENAME"))
 | 
				
			||||||
		this.write50x(err, http.StatusInternalServerError)
 | 
							this.write50x(err, http.StatusInternalServerError, false)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ func (this *HTTPRequest) doReverseProxy() {
 | 
				
			|||||||
	if origin == nil {
 | 
						if origin == nil {
 | 
				
			||||||
		err := errors.New(this.requestFullURL() + ": no available origin sites for reverse proxy")
 | 
							err := errors.New(this.requestFullURL() + ": no available origin sites for reverse proxy")
 | 
				
			||||||
		remotelogs.ServerError(this.Server.Id, "HTTP_REQUEST_REVERSE_PROXY", err.Error(), "", nil)
 | 
							remotelogs.ServerError(this.Server.Id, "HTTP_REQUEST_REVERSE_PROXY", err.Error(), "", nil)
 | 
				
			||||||
		this.write50x(err, http.StatusBadGateway)
 | 
							this.write50x(err, http.StatusBadGateway, true)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	this.origin = origin // 设置全局变量是为了日志等处理
 | 
						this.origin = origin // 设置全局变量是为了日志等处理
 | 
				
			||||||
@@ -61,7 +61,7 @@ func (this *HTTPRequest) doReverseProxy() {
 | 
				
			|||||||
	if origin.Addr == nil {
 | 
						if origin.Addr == nil {
 | 
				
			||||||
		err := errors.New(this.requestFullURL() + ": origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
 | 
							err := errors.New(this.requestFullURL() + ": origin '" + strconv.FormatInt(origin.Id, 10) + "' does not has a address")
 | 
				
			||||||
		remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
 | 
							remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
 | 
				
			||||||
		this.write50x(err, http.StatusBadGateway)
 | 
							this.write50x(err, http.StatusBadGateway, false)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	this.RawReq.URL.Scheme = origin.Addr.Protocol.Primary().Scheme()
 | 
						this.RawReq.URL.Scheme = origin.Addr.Protocol.Primary().Scheme()
 | 
				
			||||||
@@ -156,7 +156,7 @@ func (this *HTTPRequest) doReverseProxy() {
 | 
				
			|||||||
	client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol)
 | 
						client, err := SharedHTTPClientPool.Client(this, origin, originAddr, this.reverseProxy.ProxyProtocol)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
 | 
							remotelogs.Error("HTTP_REQUEST_REVERSE_PROXY", err.Error())
 | 
				
			||||||
		this.write50x(err, http.StatusBadGateway)
 | 
							this.write50x(err, http.StatusBadGateway, true)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -175,18 +175,18 @@ func (this *HTTPRequest) doReverseProxy() {
 | 
				
			|||||||
			SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
 | 
								SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
 | 
				
			||||||
				this.reverseProxy.ResetScheduling()
 | 
									this.reverseProxy.ResetScheduling()
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
			this.write50x(err, http.StatusBadGateway)
 | 
								this.write50x(err, http.StatusBadGateway, true)
 | 
				
			||||||
			remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+"': "+err.Error())
 | 
								remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+"': "+err.Error())
 | 
				
			||||||
		} else if httpErr.Err != context.Canceled {
 | 
							} else if httpErr.Err != context.Canceled {
 | 
				
			||||||
			SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
 | 
								SharedOriginStateManager.Fail(origin, this.reverseProxy, func() {
 | 
				
			||||||
				this.reverseProxy.ResetScheduling()
 | 
									this.reverseProxy.ResetScheduling()
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
			if httpErr.Timeout() {
 | 
								if httpErr.Timeout() {
 | 
				
			||||||
				this.write50x(err, http.StatusGatewayTimeout)
 | 
									this.write50x(err, http.StatusGatewayTimeout, true)
 | 
				
			||||||
			} else if httpErr.Temporary() {
 | 
								} else if httpErr.Temporary() {
 | 
				
			||||||
				this.write50x(err, http.StatusServiceUnavailable)
 | 
									this.write50x(err, http.StatusServiceUnavailable, true)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				this.write50x(err, http.StatusBadGateway)
 | 
									this.write50x(err, http.StatusBadGateway, true)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+"': "+err.Error())
 | 
								remotelogs.Warn("HTTP_REQUEST_REVERSE_PROXY", this.RawReq.URL.String()+"': "+err.Error())
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
@@ -207,7 +207,7 @@ func (this *HTTPRequest) doReverseProxy() {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if !isClientError {
 | 
								if !isClientError {
 | 
				
			||||||
				this.write50x(err, http.StatusBadGateway)
 | 
									this.write50x(err, http.StatusBadGateway, true)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if resp != nil && resp.Body != nil {
 | 
							if resp != nil && resp.Body != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -110,7 +110,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			this.write50x(err, http.StatusInternalServerError)
 | 
								this.write50x(err, http.StatusInternalServerError, true)
 | 
				
			||||||
			logs.Error(err)
 | 
								logs.Error(err)
 | 
				
			||||||
			return true
 | 
								return true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -139,7 +139,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
 | 
				
			|||||||
					}
 | 
										}
 | 
				
			||||||
					return
 | 
										return
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					this.write50x(err, http.StatusInternalServerError)
 | 
										this.write50x(err, http.StatusInternalServerError, true)
 | 
				
			||||||
					logs.Error(err)
 | 
										logs.Error(err)
 | 
				
			||||||
					return true
 | 
										return true
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -284,7 +284,7 @@ func (this *HTTPRequest) doRoot() (isBreak bool) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	reader, err := os.OpenFile(filePath, os.O_RDONLY, 0444)
 | 
						reader, err := os.OpenFile(filePath, os.O_RDONLY, 0444)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		this.write50x(err, http.StatusInternalServerError)
 | 
							this.write50x(err, http.StatusInternalServerError, true)
 | 
				
			||||||
		return true
 | 
							return true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,7 @@ func (this *HTTPRequest) doURL(method string, url string, host string, statusCod
 | 
				
			|||||||
	resp, err := client.Do(req)
 | 
						resp, err := client.Do(req)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		remotelogs.Error("HTTP_REQUEST_URL", req.URL.String()+": "+err.Error())
 | 
							remotelogs.Error("HTTP_REQUEST_URL", req.URL.String()+": "+err.Error())
 | 
				
			||||||
		this.write50x(err, http.StatusInternalServerError)
 | 
							this.write50x(err, http.StatusInternalServerError, false)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,7 @@ func (this *HTTPRequest) doWebsocket() {
 | 
				
			|||||||
	// TODO 增加N次错误重试,重试的时候需要尝试不同的源站
 | 
						// TODO 增加N次错误重试,重试的时候需要尝试不同的源站
 | 
				
			||||||
	originConn, err := OriginConnect(this.origin, this.RawReq.RemoteAddr)
 | 
						originConn, err := OriginConnect(this.origin, this.RawReq.RemoteAddr)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		this.write50x(err, http.StatusBadGateway)
 | 
							this.write50x(err, http.StatusBadGateway, false)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
@@ -52,13 +52,13 @@ func (this *HTTPRequest) doWebsocket() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	err = this.RawReq.Write(originConn)
 | 
						err = this.RawReq.Write(originConn)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		this.write50x(err, http.StatusBadGateway)
 | 
							this.write50x(err, http.StatusBadGateway, false)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	clientConn, _, err := this.writer.Hijack()
 | 
						clientConn, _, err := this.writer.Hijack()
 | 
				
			||||||
	if err != nil || clientConn == nil {
 | 
						if err != nil || clientConn == nil {
 | 
				
			||||||
		this.write50x(err, http.StatusInternalServerError)
 | 
							this.write50x(err, http.StatusInternalServerError, false)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					// Copyright 2021 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package nodes
 | 
					package nodes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
@@ -413,10 +415,12 @@ func (this *HTTPWriter) Close() {
 | 
				
			|||||||
			if this.isOk {
 | 
								if this.isOk {
 | 
				
			||||||
				err := this.cacheWriter.Close()
 | 
									err := this.cacheWriter.Close()
 | 
				
			||||||
				if err == nil {
 | 
									if err == nil {
 | 
				
			||||||
 | 
										var expiredAt = this.cacheWriter.ExpiredAt()
 | 
				
			||||||
					this.cacheStorage.AddToList(&caches.Item{
 | 
										this.cacheStorage.AddToList(&caches.Item{
 | 
				
			||||||
						Type:       this.cacheWriter.ItemType(),
 | 
											Type:       this.cacheWriter.ItemType(),
 | 
				
			||||||
						Key:        this.cacheWriter.Key(),
 | 
											Key:        this.cacheWriter.Key(),
 | 
				
			||||||
						ExpiredAt:  this.cacheWriter.ExpiredAt(),
 | 
											ExpiredAt:  expiredAt,
 | 
				
			||||||
 | 
											StaleAt:    expiredAt + int64(this.calculateStaleLife()),
 | 
				
			||||||
						HeaderSize: this.cacheWriter.HeaderSize(),
 | 
											HeaderSize: this.cacheWriter.HeaderSize(),
 | 
				
			||||||
						BodySize:   this.cacheWriter.BodySize(),
 | 
											BodySize:   this.cacheWriter.BodySize(),
 | 
				
			||||||
						Host:       this.req.Host,
 | 
											Host:       this.req.Host,
 | 
				
			||||||
@@ -690,3 +694,32 @@ func (this *HTTPWriter) prepareCache(size int64) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 计算stale时长
 | 
				
			||||||
 | 
					func (this *HTTPWriter) calculateStaleLife() int {
 | 
				
			||||||
 | 
						var staleLife = 600 // TODO 可以在缓存策略里设置此时间
 | 
				
			||||||
 | 
						var staleConfig = this.req.web.Cache.Stale
 | 
				
			||||||
 | 
						if staleConfig != nil && staleConfig.IsOn {
 | 
				
			||||||
 | 
							// 从Header中读取stale-if-error
 | 
				
			||||||
 | 
							var isDefinedInHeader = false
 | 
				
			||||||
 | 
							if staleConfig.SupportStaleIfErrorHeader {
 | 
				
			||||||
 | 
								var cacheControl = this.Header().Get("Cache-Control")
 | 
				
			||||||
 | 
								var pieces = strings.Split(cacheControl, ",")
 | 
				
			||||||
 | 
								for _, piece := range pieces {
 | 
				
			||||||
 | 
									var eqIndex = strings.Index(piece, "=")
 | 
				
			||||||
 | 
									if eqIndex > 0 && strings.TrimSpace(piece[:eqIndex]) == "stale-if-error" {
 | 
				
			||||||
 | 
										// 这里预示着如果stale-if-error=0,可以关闭stale功能
 | 
				
			||||||
 | 
										staleLife = types.Int(strings.TrimSpace(piece[eqIndex+1:]))
 | 
				
			||||||
 | 
										isDefinedInHeader = true
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 自定义
 | 
				
			||||||
 | 
							if !isDefinedInHeader && staleConfig.Life != nil {
 | 
				
			||||||
 | 
								staleLife = types.Int(staleConfig.Life.Duration().Seconds())
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return staleLife
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user