diff --git a/FUTURE.md b/FUTURE.md index 0b8cf2e..39dcdb2 100644 --- a/FUTURE.md +++ b/FUTURE.md @@ -5,8 +5,6 @@ * [x] 全新 vex 通信协议 * [x] 优化性能,全双工通信 * [x] 支持客户端连接池 -* [ ] 支持拒绝策略,比如 ip 拒绝 -* [ ] 支持 tls 安全传输 * [x] 单元测试覆盖率提高到 90% ### v0.4.x diff --git a/HISTORY.md b/HISTORY.md index b44a48d..b208e28 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,11 @@ ## 🎡 历史版本的特性介绍 +### v0.5.2 + +> 此版本发布于 2026-03-01 + +* 增加 Pool 代码和单元测试 + ### v0.5.1-alpha > 此版本发布于 2026-02-27 diff --git a/README.en.md b/README.en.md index b3e64a2..ec4dbfc 100644 --- a/README.en.md +++ b/README.en.md @@ -126,8 +126,8 @@ goos: linux goarch: amd64 cpu: Intel(R) Xeon(R) CPU E5-26xx v4 -BenchmarkPacket-2 48885 25712 ns/op 4600 B/op 9 allocs/op -BenchmarkPacketPool-2 58665 21461 ns/op 4601 B/op 9 allocs/op +BenchmarkPacket-2 43992 26818 ns/op 4664 B/op 15 allocs/op +BenchmarkPacketPool-2 56511 20681 ns/op 4690 B/op 16 allocs/op ``` > Benchmark: [_examples/packet_test.go](./_examples/packet_test.go). diff --git a/README.md b/README.md index 4f3c078..4a207b4 100644 --- a/README.md +++ b/README.md @@ -121,8 +121,8 @@ goos: linux goarch: amd64 cpu: Intel(R) Xeon(R) CPU E5-26xx v4 -BenchmarkPacket-2 48885 25712 ns/op 4600 B/op 9 allocs/op -BenchmarkPacketPool-2 58665 21461 ns/op 4601 B/op 9 allocs/op +BenchmarkPacket-2 43992 26818 ns/op 4664 B/op 15 allocs/op +BenchmarkPacketPool-2 56511 20681 ns/op 4690 B/op 16 allocs/op ``` > 测试文件:[_examples/packet_test.go](./_examples/packet_test.go)。 diff --git a/_icons/coverage.svg b/_icons/coverage.svg index 1d8d43d..89927ff 100644 --- a/_icons/coverage.svg +++ b/_icons/coverage.svg @@ -10,7 +10,7 @@ coverage coverage - 92% - 92% + 95% + 95% \ No newline at end of file diff --git a/pool.go b/pool.go index 87638aa..1063c19 100644 --- a/pool.go +++ b/pool.go @@ -6,10 +6,18 @@ package vex import ( "context" + "errors" "github.com/FishGoddess/rego" ) +var ( + errPoolClosed = errors.New("vex: pool is closed") +) + +// Status is the status information of pool. +type Status rego.Status + type poolClient struct { pool *Pool @@ -61,6 +69,10 @@ func NewPool(limit uint64, dial DialFunc, opts ...Option) *Pool { } pool.clients = rego.New(limit, acquire, release) + pool.clients.WithPoolClosedErrFunc(func(ctx context.Context) error { + return errPoolClosed + }) + return pool } @@ -69,6 +81,12 @@ func (p *Pool) Get(ctx context.Context) (Client, error) { return p.clients.Acquire(ctx) } +// Status returns the status of pool. +func (p *Pool) Status() Status { + status := p.clients.Status() + return Status(status) +} + // Close closes the pool and releases all clients in it. func (p *Pool) Close() error { ctx := context.Background() diff --git a/pool_test.go b/pool_test.go new file mode 100644 index 0000000..2d80080 --- /dev/null +++ b/pool_test.go @@ -0,0 +1,86 @@ +// Copyright 2025 FishGoddess. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package vex + +import ( + "context" + "fmt" + "sync/atomic" + "testing" +) + +// go test -v -cover -run=^TestNewPool$ +func TestPool(t *testing.T) { + ctx := context.Background() + + address, done, err := runTestServer() + if err != nil { + t.Fatal(err) + } + + defer done() + + var clientPtr atomic.Pointer[Client] + dial := func(ctx context.Context) (Client, error) { + client, err := NewClient(address) + if err != nil { + return nil, err + } + + clientPtr.Store(&client) + return client, nil + } + + pool := NewPool(4, dial) + defer pool.Close() + + for range 100 { + func() { + client, err := pool.Get(ctx) + if err != nil { + t.Fatal(err) + } + + defer client.Close() + + poolClient, ok := client.(poolClient) + if !ok { + t.Fatalf("got %T is wrong", client) + } + + loadClient := clientPtr.Load() + if loadClient == nil { + t.Fatalf("load client is nil") + } + + got := fmt.Sprintf("%p", poolClient.client) + want := fmt.Sprintf("%p", *loadClient) + if got != want { + t.Fatalf("got %s != want %s", got, want) + } + + status := pool.Status() + wantStatus := Status{Limit: 4, Using: 1, Idle: 0, Waiting: 0} + if status != wantStatus { + t.Fatalf("got %+v != want %+v", status, wantStatus) + } + }() + } + + status := pool.Status() + wantStatus := Status{Limit: 4, Using: 0, Idle: 1, Waiting: 0} + if status != wantStatus { + t.Fatalf("got %+v != want %+v", status, wantStatus) + } + + if err = pool.Close(); err != nil { + t.Fatal(err) + } + + _, err = pool.Get(ctx) + if err != errPoolClosed { + t.Fatalf("got %+v != want %+v", err, errPoolClosed) + } +}