mirror of
				https://github.com/TeaOSLab/EdgeNode.git
				synced 2025-11-04 07:40:56 +08:00 
			
		
		
		
	[cache]优化缓存文件写入加锁方法
This commit is contained in:
		@@ -20,6 +20,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -31,7 +32,8 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	ErrNotFound = errors.New("cache not found")
 | 
			
		||||
	ErrNotFound      = errors.New("cache not found")
 | 
			
		||||
	ErrFileIsWriting = errors.New("the file is writing")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type FileStorage struct {
 | 
			
		||||
@@ -251,19 +253,17 @@ func (this *FileStorage) Open(key string, expiredAt int64) (Writer, error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	this.locker.Lock()
 | 
			
		||||
 | 
			
		||||
	// 先删除
 | 
			
		||||
	this.list.Remove(hash)
 | 
			
		||||
 | 
			
		||||
	path := dir + "/" + hash + ".cache"
 | 
			
		||||
	writer, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_SYNC|os.O_WRONLY, 0777)
 | 
			
		||||
	writer, err := os.OpenFile(path, os.O_CREATE|os.O_SYNC|os.O_WRONLY, 0666)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		this.locker.Unlock()
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isOk := false
 | 
			
		||||
	removeOnFailure := true
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			isOk = false
 | 
			
		||||
@@ -272,11 +272,24 @@ func (this *FileStorage) Open(key string, expiredAt int64) (Writer, error) {
 | 
			
		||||
		// 如果出错了,就删除文件,避免写一半
 | 
			
		||||
		if !isOk {
 | 
			
		||||
			_ = writer.Close()
 | 
			
		||||
			_ = os.Remove(path)
 | 
			
		||||
			this.locker.Unlock()
 | 
			
		||||
			if removeOnFailure {
 | 
			
		||||
				_ = os.Remove(path)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// 尝试锁定,如果锁定失败,则直接返回
 | 
			
		||||
	err = syscall.Flock(int(writer.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		removeOnFailure = false
 | 
			
		||||
		return nil, ErrFileIsWriting
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = writer.Truncate(0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 写入过期时间
 | 
			
		||||
	_, err = writer.WriteString(fmt.Sprintf("%d", expiredAt))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -299,7 +312,7 @@ func (this *FileStorage) Open(key string, expiredAt int64) (Writer, error) {
 | 
			
		||||
 | 
			
		||||
	isOk = true
 | 
			
		||||
 | 
			
		||||
	return NewFileWriter(writer, key, expiredAt, &this.locker), nil
 | 
			
		||||
	return NewFileWriter(writer, key, expiredAt), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 写入缓存数据
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,8 @@ import (
 | 
			
		||||
	_ "github.com/iwind/TeaGo/bootstrap"
 | 
			
		||||
	"github.com/iwind/TeaGo/logs"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
@@ -72,6 +74,111 @@ func TestFileStorage_Open(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFileStorage_Concurrent_Open_DifferentFile(t *testing.T) {
 | 
			
		||||
	storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
 | 
			
		||||
		Id:   1,
 | 
			
		||||
		IsOn: true,
 | 
			
		||||
		Options: map[string]interface{}{
 | 
			
		||||
			"dir": Tea.Root + "/caches",
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	err := storage.Init()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	defer func() {
 | 
			
		||||
		t.Log(time.Since(now).Seconds()*1000, "ms")
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	wg := sync.WaitGroup{}
 | 
			
		||||
	count := 100
 | 
			
		||||
	wg.Add(count)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < count; i++ {
 | 
			
		||||
		go func(i int) {
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
 | 
			
		||||
			writer, err := storage.Open("abc"+strconv.Itoa(i), time.Now().Unix()+3600)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if err != ErrFileIsWriting {
 | 
			
		||||
					t.Fatal(err)
 | 
			
		||||
				}
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			//t.Log(writer)
 | 
			
		||||
 | 
			
		||||
			_, err = writer.Write([]byte("Hello,World"))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 故意造成慢速写入
 | 
			
		||||
			time.Sleep(1 * time.Second)
 | 
			
		||||
 | 
			
		||||
			err = writer.Close()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
		}(i)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFileStorage_Concurrent_Open_SameFile(t *testing.T) {
 | 
			
		||||
	storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
 | 
			
		||||
		Id:   1,
 | 
			
		||||
		IsOn: true,
 | 
			
		||||
		Options: map[string]interface{}{
 | 
			
		||||
			"dir": Tea.Root + "/caches",
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	err := storage.Init()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	defer func() {
 | 
			
		||||
		t.Log(time.Since(now).Seconds()*1000, "ms")
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	wg := sync.WaitGroup{}
 | 
			
		||||
	count := 100
 | 
			
		||||
	wg.Add(count)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < count; i++ {
 | 
			
		||||
		go func(i int) {
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
 | 
			
		||||
			writer, err := storage.Open("abc"+strconv.Itoa(0), time.Now().Unix()+3600)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if err != ErrFileIsWriting {
 | 
			
		||||
					t.Fatal(err)
 | 
			
		||||
				}
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			//t.Log(writer)
 | 
			
		||||
 | 
			
		||||
			t.Log("writing")
 | 
			
		||||
			_, err = writer.Write([]byte("Hello,World"))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// 故意造成慢速写入
 | 
			
		||||
			time.Sleep(time.Duration(1) * time.Second)
 | 
			
		||||
 | 
			
		||||
			err = writer.Close()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				t.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
		}(i)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFileStorage_Write(t *testing.T) {
 | 
			
		||||
	storage := NewFileStorage(&serverconfigs.HTTPCachePolicy{
 | 
			
		||||
		Id:   1,
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ package caches
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"sync"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type FileWriter struct {
 | 
			
		||||
@@ -10,16 +9,14 @@ type FileWriter struct {
 | 
			
		||||
	key        string
 | 
			
		||||
	size       int64
 | 
			
		||||
	expiredAt  int64
 | 
			
		||||
	locker     *sync.RWMutex
 | 
			
		||||
	isReleased bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewFileWriter(rawWriter *os.File, key string, expiredAt int64, locker *sync.RWMutex) *FileWriter {
 | 
			
		||||
func NewFileWriter(rawWriter *os.File, key string, expiredAt int64) *FileWriter {
 | 
			
		||||
	return &FileWriter{
 | 
			
		||||
		key:       key,
 | 
			
		||||
		rawWriter: rawWriter,
 | 
			
		||||
		expiredAt: expiredAt,
 | 
			
		||||
		locker:    locker,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -42,6 +39,7 @@ func (this *FileWriter) Close() error {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		_ = os.Remove(this.rawWriter.Name())
 | 
			
		||||
	}
 | 
			
		||||
	_ = this.rawWriter.Close()
 | 
			
		||||
 | 
			
		||||
	this.Release()
 | 
			
		||||
 | 
			
		||||
@@ -73,5 +71,4 @@ func (this *FileWriter) Release() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	this.isReleased = true
 | 
			
		||||
	this.locker.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -366,7 +366,9 @@ func (this *HTTPWriter) prepareCache(size int64) {
 | 
			
		||||
	expiredAt := utils.UnixTime() + life
 | 
			
		||||
	cacheWriter, err := storage.Open(this.req.cacheKey, expiredAt)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		remotelogs.Error("REQUEST_WRITER", "write cache failed: "+err.Error())
 | 
			
		||||
		if err != caches.ErrFileIsWriting {
 | 
			
		||||
			remotelogs.Error("REQUEST_WRITER", "write cache failed: "+err.Error())
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	this.cacheWriter = cacheWriter
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user