mirror of
https://github.com/charlienet/go-mixed.git
synced 2025-07-18 16:42:41 +08:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
fe5c0b54b6 | |||
54fbe8eb0a | |||
2d851d4872 | |||
a83ccf7c00 | |||
bb979f5ccb |
@ -5,7 +5,6 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/charlienet/go-mixed/bytesconv"
|
"github.com/charlienet/go-mixed/bytesconv"
|
||||||
"github.com/charlienet/go-mixed/expr"
|
|
||||||
"github.com/charlienet/go-mixed/hash"
|
"github.com/charlienet/go-mixed/hash"
|
||||||
"github.com/charlienet/go-mixed/redis"
|
"github.com/charlienet/go-mixed/redis"
|
||||||
)
|
)
|
||||||
@ -58,10 +57,7 @@ func New(expectedInsertions uint, fpp float64, opts ...option) *BloomFilter {
|
|||||||
bf := &BloomFilter{
|
bf := &BloomFilter{
|
||||||
bits: bits,
|
bits: bits,
|
||||||
funcs: k,
|
funcs: k,
|
||||||
store: expr.Ternary[bitStore](
|
store: createBitStore(opt, bits),
|
||||||
opt.redisClient == nil,
|
|
||||||
newMemStore(bits),
|
|
||||||
newRedisStore(opt.redisClient, opt.redisKey, bits)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bf
|
return bf
|
||||||
@ -104,6 +100,14 @@ func (bf *BloomFilter) Clear() {
|
|||||||
bf.store.Clear()
|
bf.store.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createBitStore(opt *bloomOptions, bits uint) bitStore {
|
||||||
|
if opt.redisClient != nil {
|
||||||
|
return newRedisStore(opt.redisClient, opt.redisKey, bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newMemStore(bits)
|
||||||
|
}
|
||||||
|
|
||||||
// 计算优化的位图长度,
|
// 计算优化的位图长度,
|
||||||
// n 期望放置元素数量,
|
// n 期望放置元素数量,
|
||||||
// p 预期的误判概率
|
// p 预期的误判概率
|
||||||
|
@ -17,7 +17,7 @@ func newMemStore(size uint) *memStore {
|
|||||||
return &memStore{
|
return &memStore{
|
||||||
size: size,
|
size: size,
|
||||||
set: bitset.New(size),
|
set: bitset.New(size),
|
||||||
lock: locker.NewRWLocker(),
|
lock: locker.RWLocker{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
configure/config.toml
Normal file
7
configure/config.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
Mode = "dev"
|
||||||
|
|
||||||
|
[Nacos]
|
||||||
|
Address = "192.168.2.121"
|
||||||
|
Port = 8848
|
||||||
|
Namespace = "8560b58d-87d0-4b85-8ac5-f2d308c6669e"
|
||||||
|
Group = "dev"
|
46
configure/configure.go
Normal file
46
configure/configure.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package configure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/charlienet/go-mixed/expr"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NotifyFunc func(Configure) error
|
||||||
|
|
||||||
|
type Configure interface {
|
||||||
|
Load(dataId string, v any, onChanged ...NotifyFunc) error
|
||||||
|
GetString(string, string) string
|
||||||
|
GetInt(key string, defaultValue int) int
|
||||||
|
}
|
||||||
|
|
||||||
|
type conf struct {
|
||||||
|
viper *viper.Viper //
|
||||||
|
nacos *nacos //
|
||||||
|
onChangeNotifies map[string][]NotifyFunc // 已经注册的配置变更通知
|
||||||
|
nacosOptions *NacosOptions //
|
||||||
|
useNacos bool //
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) GetString(key string, defaultValue string) string {
|
||||||
|
if c.viper.IsSet(key) {
|
||||||
|
return c.viper.GetString(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) GetInt(key string, defaultValue int) int {
|
||||||
|
return expr.Ternary(c.viper.IsSet(key), c.viper.GetInt(key), defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) Load(dataId string, v any, onChanged ...NotifyFunc) error {
|
||||||
|
if err := c.nacos.Load(dataId, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(onChanged) > 0 {
|
||||||
|
c.onChangeNotifies[dataId] = onChanged
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
100
configure/configure_builder.go
Normal file
100
configure/configure_builder.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package configure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New() *conf { return &conf{viper: viper.New(), onChangeNotifies: make(map[string][]NotifyFunc)} }
|
||||||
|
|
||||||
|
func (c *conf) AddConfigPath(in ...string) *conf {
|
||||||
|
for _, v := range in {
|
||||||
|
c.viper.AddConfigPath(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) SetConfigName(in string) *conf {
|
||||||
|
c.viper.SetConfigName(in)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) SetConfigFile(f string) *conf {
|
||||||
|
c.viper.SetConfigFile(f)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) SetDefault(key string, value any) *conf {
|
||||||
|
c.viper.SetDefault(key, value)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) AutomaticEnv() *conf {
|
||||||
|
c.viper.AutomaticEnv()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) Read() (*conf, error) {
|
||||||
|
// 从本地配置读取
|
||||||
|
if err := c.viper.ReadInConfig(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.viper.WatchConfig()
|
||||||
|
c.viper.OnConfigChange(c.OnViperChanged)
|
||||||
|
|
||||||
|
// 初始化Nacos客户端
|
||||||
|
if err := c.createNacosClient(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) OnViperChanged(in fsnotify.Event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) createNacosClient() error {
|
||||||
|
opt := c.getNacosOptions()
|
||||||
|
if opt == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
nc, err := createNacosClient(opt.Address, opt.Port, opt.Namespace, opt.Group)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.nacos = &nacos{client: nc, group: opt.Group, onChanged: c.onNacosChanged}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) onNacosChanged(dataId, data string) {
|
||||||
|
if fs, ok := c.onChangeNotifies[dataId]; ok {
|
||||||
|
for _, f := range fs {
|
||||||
|
if f != nil {
|
||||||
|
f(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) getNacosOptions() *NacosOptions {
|
||||||
|
if c.nacosOptions != nil {
|
||||||
|
return c.nacosOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.useNacos {
|
||||||
|
return &NacosOptions{
|
||||||
|
Address: c.GetString(AddressKey, "127.0.0.1"),
|
||||||
|
Port: c.GetInt(PortKey, 8848),
|
||||||
|
Namespace: c.GetString(Namespace, ""),
|
||||||
|
Group: c.GetString(Group, ""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
42
configure/configure_test.go
Normal file
42
configure/configure_test.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package configure_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/charlienet/go-mixed/configure"
|
||||||
|
"github.com/charlienet/go-mixed/json"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadSpecifiedFile(t *testing.T) {
|
||||||
|
conf, err := configure.New().SetConfigFile("config.toml").Read()
|
||||||
|
t.Log(err)
|
||||||
|
|
||||||
|
assert.Equal(t, "192.168.2.121", conf.GetString("nacos.address", ""))
|
||||||
|
_ = conf
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewConfigure(t *testing.T) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNacos(t *testing.T) {
|
||||||
|
conf, err := configure.
|
||||||
|
New().
|
||||||
|
AddConfigPath(".").
|
||||||
|
WithNacos().
|
||||||
|
Read()
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
t.Log(conf.GetString("nacos.address", ""))
|
||||||
|
|
||||||
|
type redis struct {
|
||||||
|
Addrs string
|
||||||
|
}
|
||||||
|
|
||||||
|
r := &redis{}
|
||||||
|
|
||||||
|
t.Log(conf.Load("redis", r))
|
||||||
|
t.Log(json.StructToJsonIndent(r))
|
||||||
|
}
|
96
configure/nacos.go
Normal file
96
configure/nacos.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package configure
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/nacos-group/nacos-sdk-go/v2/clients"
|
||||||
|
"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
|
||||||
|
"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
|
||||||
|
"github.com/nacos-group/nacos-sdk-go/v2/vo"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AddressKey = "Nacos.Address"
|
||||||
|
PortKey = "Nacos.Port"
|
||||||
|
Namespace = "Nacos.Namespace"
|
||||||
|
Group = "Nacos.Group"
|
||||||
|
)
|
||||||
|
|
||||||
|
type nacos struct {
|
||||||
|
client config_client.IConfigClient
|
||||||
|
onChanged func(string, string)
|
||||||
|
group string
|
||||||
|
}
|
||||||
|
|
||||||
|
type NacosOptions struct {
|
||||||
|
Address string
|
||||||
|
Port int
|
||||||
|
Namespace string
|
||||||
|
Group string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) WithNacosOptions(options *NacosOptions) *conf {
|
||||||
|
c.nacosOptions = options
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *conf) WithNacos() *conf {
|
||||||
|
c.useNacos = true
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *nacos) Load(dataId string, v any) error {
|
||||||
|
voParam := vo.ConfigParam{
|
||||||
|
DataId: dataId,
|
||||||
|
Group: n.group,
|
||||||
|
OnChange: n.onChange,
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := n.client.GetConfig(voParam)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(content) == 0 {
|
||||||
|
return fmt.Errorf("parameters not configured:%s", dataId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal([]byte(content), v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n.client.ListenConfig(voParam)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *nacos) onChange(namespace, group, dataId, data string) {
|
||||||
|
n.onChanged(dataId, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNacosClient(addr string, port int, namespace, group string) (config_client.IConfigClient, error) {
|
||||||
|
sc := []constant.ServerConfig{{
|
||||||
|
IpAddr: addr,
|
||||||
|
Port: uint64(port),
|
||||||
|
}}
|
||||||
|
|
||||||
|
cc := constant.ClientConfig{
|
||||||
|
NamespaceId: namespace,
|
||||||
|
TimeoutMs: 5000,
|
||||||
|
LogDir: "logs",
|
||||||
|
CacheDir: "cache",
|
||||||
|
LogLevel: "info",
|
||||||
|
NotLoadCacheAtStart: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
configClient, err := clients.CreateConfigClient(map[string]any{
|
||||||
|
"serverConfigs": sc,
|
||||||
|
"clientConfig": cc,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return configClient, nil
|
||||||
|
}
|
@ -3,94 +3,111 @@ package locker
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/charlienet/go-mixed/redis"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WithLocker struct {
|
var empty = &emptyLocker{}
|
||||||
|
|
||||||
|
type Locker struct {
|
||||||
once sync.Once
|
once sync.Once
|
||||||
mu Locker
|
distributedLocker DistributedLocker // 分布式锁
|
||||||
|
mu locker
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithLocker) Synchronize() {
|
func (w *Locker) WithRedis(key string, rdb redis.Client) *Locker {
|
||||||
if w.mu == nil || w.mu == EmptyLocker {
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Locker) WithDistributedLocker(d DistributedLocker) *Locker {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Locker) Synchronize() *Locker {
|
||||||
|
if w.mu == nil || w.mu == empty {
|
||||||
w.mu = NewLocker()
|
w.mu = NewLocker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithLocker) Lock() {
|
func (w *Locker) Lock() {
|
||||||
w.ensureLocker().Lock()
|
w.ensureLocker().mu.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithLocker) Unlock() {
|
func (w *Locker) Unlock() {
|
||||||
w.ensureLocker().Unlock()
|
w.ensureLocker().mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithLocker) TryLock() bool {
|
func (w *Locker) TryLock() bool {
|
||||||
return w.ensureLocker().TryLock()
|
return w.ensureLocker().mu.TryLock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithLocker) ensureLocker() Locker {
|
func (w *Locker) ensureLocker() *Locker {
|
||||||
w.once.Do(func() {
|
w.once.Do(func() {
|
||||||
if w.mu == nil {
|
if w.mu == nil {
|
||||||
w.mu = EmptyLocker
|
w.mu = empty
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return w.mu
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
type WithSpinLocker struct {
|
type SpinLocker struct {
|
||||||
WithLocker
|
Locker
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithSpinLocker) Synchronize() {
|
func (w *SpinLocker) Synchronize() {
|
||||||
if w.mu == nil || w.mu == EmptyLocker {
|
if w.mu == nil || w.mu == empty {
|
||||||
w.mu = NewSpinLocker()
|
w.mu = NewSpinLocker()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type WithRWLocker struct {
|
type RWLocker struct {
|
||||||
once sync.Once
|
once sync.Once
|
||||||
mu RWLocker
|
mu rwLocker
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithRWLocker) Synchronize() {
|
func (w *RWLocker) Synchronize() *RWLocker {
|
||||||
if w.mu == nil || w.mu == EmptyLocker {
|
if w.mu == nil || w.mu == empty {
|
||||||
log.Println("初始化有效锁")
|
|
||||||
w.mu = NewRWLocker()
|
w.mu = NewRWLocker()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithRWLocker) Lock() {
|
func (w *RWLocker) Lock() {
|
||||||
w.ensureLocker().Lock()
|
w.ensureLocker().mu.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithRWLocker) TryLock() bool {
|
func (w *RWLocker) TryLock() bool {
|
||||||
return w.ensureLocker().TryLock()
|
return w.ensureLocker().mu.TryLock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithRWLocker) Unlock() {
|
func (w *RWLocker) Unlock() {
|
||||||
w.ensureLocker().Unlock()
|
w.ensureLocker().mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithRWLocker) RLock() {
|
func (w *RWLocker) RLock() {
|
||||||
w.ensureLocker().RLock()
|
w.ensureLocker().mu.RLock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithRWLocker) TryRLock() bool {
|
func (w *RWLocker) TryRLock() bool {
|
||||||
return w.ensureLocker().TryRLock()
|
return w.ensureLocker().mu.TryRLock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithRWLocker) RUnlock() {
|
func (w *RWLocker) RUnlock() {
|
||||||
w.ensureLocker().RUnlock()
|
w.ensureLocker().mu.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WithRWLocker) ensureLocker() RWLocker {
|
func (w *RWLocker) ensureLocker() *RWLocker {
|
||||||
w.once.Do(func() {
|
w.once.Do(func() {
|
||||||
if w.mu == nil {
|
if w.mu == nil {
|
||||||
log.Println("初始化一个空锁")
|
log.Println("初始化一个空锁")
|
||||||
w.mu = EmptyLocker
|
w.mu = empty
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return w.mu
|
return w
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user