Skip to content

Commit

Permalink
refactor(internal/locale): 采用 TTL 缓存常用的 Printer
Browse files Browse the repository at this point in the history
  • Loading branch information
caixw committed Mar 13, 2024
1 parent 4826cdb commit 27b1d05
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 23 deletions.
2 changes: 2 additions & 0 deletions cmd/web/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@ require (
github.com/issue9/mux/v7 v7.4.1 // indirect
github.com/issue9/scheduled v0.19.3 // indirect
github.com/issue9/sliceutil v0.15.1 // indirect
github.com/jellydator/ttlcache/v3 v3.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
)
4 changes: 4 additions & 0 deletions cmd/web/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ github.com/issue9/unique/v2 v2.0.1 h1:Tdbq7hWZd7rvnf3ckUqzvEftOBWl1Z3S60Jk/zbPvy
github.com/issue9/unique/v2 v2.0.1/go.mod h1:oYIXt0BXX4tekc9+77oBu/ROsm7tr5kmD78XEqFoWwk=
github.com/issue9/version v1.0.8 h1:IsNdDYdV8UGDGwwgp8H4RszJE0Ko26HjWg9pZzyOivs=
github.com/issue9/version v1.0.8/go.mod h1:w8bQwODBOG5+iaS3qIJbElxxpp3Uo4x5F39qKBqwpdc=
github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE=
github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
Expand All @@ -72,6 +74,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
Expand Down
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/issue9/web

go 1.21

require (
github.com/andybalholm/brotli v1.1.0
github.com/issue9/assert/v4 v4.1.1
Expand All @@ -16,6 +18,7 @@ require (
github.com/issue9/source v0.8.2
github.com/issue9/term/v3 v3.2.7
github.com/issue9/unique/v2 v2.0.1
github.com/jellydator/ttlcache/v3 v3.2.0
github.com/klauspost/compress v1.17.7
golang.org/x/crypto v0.21.0
golang.org/x/text v0.14.0
Expand All @@ -31,7 +34,6 @@ require (
github.com/redis/go-redis/v9 v9.5.1 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.18.0 // indirect
)

go 1.21
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/issue9/assert/v4 v4.1.1 h1:OhPE8SB8n/qZCNGLQa+6MQtr/B3oON0JAVj68k8jJlc=
Expand Down Expand Up @@ -40,16 +42,26 @@ github.com/issue9/term/v3 v3.2.7 h1:esfhoinbQ65P3oFscXhticrDFOgZJQqUwL/IC70HiWc=
github.com/issue9/term/v3 v3.2.7/go.mod h1:DvA/fPiKzX11P/ZoVWJG5QMVpI0ia+uqiU31iZV2jHE=
github.com/issue9/unique/v2 v2.0.1 h1:Tdbq7hWZd7rvnf3ckUqzvEftOBWl1Z3S60Jk/zbPvyM=
github.com/issue9/unique/v2 v2.0.1/go.mod h1:oYIXt0BXX4tekc9+77oBu/ROsm7tr5kmD78XEqFoWwk=
github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE=
github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
Expand Down
19 changes: 17 additions & 2 deletions internal/locale/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,32 @@ import (
func BenchmarkLocale_NewPrinter(b *testing.B) {
l := New(language.SimplifiedChinese, nil, nil)

b.Run("equal id", func(b *testing.B) {
b.Run("equal Locale.id", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
l.NewPrinter(language.SimplifiedChinese)
}
})

b.Run("not equal id", func(b *testing.B) {
b.Run("not equal Locale.id", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
l.NewPrinter(language.TraditionalChinese)
}
})

langs := []language.Tag{
language.Chinese,
language.SimplifiedChinese,
language.TraditionalChinese,
language.MustParse("zh-CN"),
language.MustParse("cmn-Hans"),
}
b.Run("rand id", func(b *testing.B) {
b.ResetTimer()
size := len(langs)
for i := 0; i < b.N; i++ {
l.NewPrinter(langs[i%size])
}
})
}
22 changes: 16 additions & 6 deletions internal/locale/locale.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/issue9/config"
"github.com/issue9/localeutil/message/serialize"
"github.com/jellydator/ttlcache/v3"
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/message/catalog"
Expand All @@ -21,6 +22,7 @@ type Locale struct {
id language.Tag
config *config.Config
printer *message.Printer
ttl *ttlcache.Cache[language.Tag, *message.Printer]
}

func New(id language.Tag, conf *config.Config, b *catalog.Builder) *Locale {
Expand All @@ -34,11 +36,13 @@ func New(id language.Tag, conf *config.Config, b *catalog.Builder) *Locale {
panic(err)
}

p, _ := NewPrinter(id, b)
return &Locale{
Builder: b,
id: id,
config: conf,
printer: NewPrinter(id, b),
printer: p,
ttl: ttlcache.New(ttlcache.WithCapacity[language.Tag, *message.Printer](10)),
}
}

Expand All @@ -59,14 +63,20 @@ func (l *Locale) NewPrinter(id language.Tag) *message.Printer {
return l.Printer()
}

// TODO 以使用频次或是 TTL 的方式缓存常用的 Printer,可以在一定程序上提升 NewPrinter 的性能。
return NewPrinter(id, l)
if item := l.ttl.Get(id); item != nil {
return item.Value()
}
p, exact := NewPrinter(id, l)
if exact {
l.ttl.Set(id, p, ttlcache.DefaultTTL)
}
return p
}

// NewPrinter 从 cat 是查找最符合 tag 的语言 ID 并返回对应的 [message.Printer] 对象
func NewPrinter(tag language.Tag, cat catalog.Catalog) *message.Printer {
tag, _, _ = cat.Matcher().Match(tag) // 从 cat 中查找最合适的 tag
return message.NewPrinter(tag, message.Catalog(cat))
func NewPrinter(tag language.Tag, cat catalog.Catalog) (*message.Printer, bool) {
tag, _, confidence := cat.Matcher().Match(tag) // 从 cat 中查找最合适的 tag
return message.NewPrinter(tag, message.Catalog(cat)), confidence == language.Exact
}

func Load(s config.Serializer, b *catalog.Builder, glob string, fsys ...fs.FS) error {
Expand Down
24 changes: 12 additions & 12 deletions internal/locale/locale_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@ func TestLocale_Printer(t *testing.T) {
a := assert.New(t, false)

b := catalog.NewBuilder()
a.NotError(b.SetString(language.SimplifiedChinese, "lang", "hans"))
l := New(language.SimplifiedChinese, nil, b)
a.NotNil(l).Equal(l.Sprintf("lang"), "hans")
a.NotError(l.SetString(language.SimplifiedChinese, "lang", "hans-2"))
a.Equal(l.Sprintf("lang"), "hans-2")
a.NotError(l.SetString(language.SimplifiedChinese, "lang", "hans")).
NotNil(l).Equal(l.Sprintf("lang"), "hans").
NotError(l.SetString(language.SimplifiedChinese, "lang", "hans-2")).
Equal(l.Sprintf("lang"), "hans-2")

// ID 不存在于 catalog

b = catalog.NewBuilder()
a.NotError(b.SetString(language.SimplifiedChinese, "lang", "hans"))
l = New(language.Afrikaans, nil, b)
a.NotNil(l).Equal(l.Sprintf("lang"), "lang") // 找不到对应的翻译项,返回原值
a.NotError(l.SetString(language.Afrikaans, "lang", "afrik"))
a.Equal(l.Sprintf("lang"), "afrik")
a.NotNil(l).Equal(l.Sprintf("lang"), "lang"). // 找不到对应的翻译项,返回原值
NotError(l.SetString(language.Afrikaans, "lang", "afrik")).
Equal(l.Sprintf("lang"), "afrik")
}

func TestLocale_NewPrinter(t *testing.T) {
Expand All @@ -61,12 +61,12 @@ func TestLocale_NewPrinter(t *testing.T) {
func TestNewPrinter(t *testing.T) {
a := assert.New(t, false)

c := catalog.NewBuilder()
a.NotError(c.SetString(language.MustParse("zh-CN"), "k1", "zh-cn"))
a.NotError(c.SetString(language.MustParse("zh-TW"), "k1", "zh-tw"))
c := catalog.NewBuilder(catalog.Fallback(language.MustParse("zh-TW")))
a.NotError(c.SetString(language.MustParse("zh-CN"), "k1", "zh-cn")).
NotError(c.SetString(language.MustParse("zh-TW"), "k1", "zh-tw"))

p := NewPrinter(language.MustParse("cmn-hans"), c)
a.Equal(p.Sprintf("k1"), "zh-cn")
p, ok := NewPrinter(language.MustParse("und"), c)
a.Equal(p.Sprintf("k1"), "zh-tw").False(ok)
}

func Test_Load(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion server/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,6 @@ func NewPrinter(glob string, fsys ...fs.FS) (*localeutil.Printer, error) {
return nil, err
}

return locale.NewPrinter(tag, b), nil
p, _ := locale.NewPrinter(tag, b)
return p, nil
}

0 comments on commit 27b1d05

Please sign in to comment.