爱上开源之boot4go-util几个常用的结构

前言

golang是进行底层服务架构的强而有力的语言,高效,高性能,天生的高并发适用性优点。作为底层架构的开发,经常要使用底层的数据结构来实现高性能的编程,虽然在golang原生就封装了切片,map等常用的数据结构,虽然他们有着非常不错的适用场景,但是对待一些对性能有比较要求的场景下,他们的实现还是略显单薄, 作为底层开发的需要,boot4go对针对一些特殊高性能的数据结构有封装了自己的数据结构。今天就一起来分享一下这些常用的数据结构

LOOPQUEUE

看名字就可以知道这个是个环形队列, 大名鼎鼎的disruptor,就是这个数据结构的原型, Disruptor是英国外汇交易公司LMAX开发的一个高性能队列,研发的初衷是解决内存队列的延迟问题,说起高性能队列,大家比较容易想到的就是Kafka,RocketMQ这些消息队列,Disruptor和他们本质的区别,Kafka都是服务间级别的队列, Disruptor是线程间的队列,极而言之就是应用内部级别的队列,使用内存实现的队列,比如Java里的Queue接口的相关实现, Disruptor使用一个环形的链表数据结构来维持队列的数据,

以下是主要对外接口的实现

func NewLoopQueue[T any](size int) *loopQueue[T] {
   return &loopQueue[T]{
      items: make([]*CacheUnit[T], size),
      size:  size,
   }
}


func (lq *loopQueue[T]) Get() *T {
	if lq.IsEmpty() {
		return nil
	}

	w := lq.items[lq.head]

	return w.obj
}


func (lq *loopQueue[T]) Detach() *T {
	if lq.IsEmpty() {
		return nil
	}

	w := lq.items[lq.head]
	lq.items[lq.head] = nil
	lq.head++
	if lq.head == lq.size {
		lq.head = 0
	}
	lq.isFull = false

	return w.obj
}


func (lq *loopQueue[T]) Insert(t *T) error {
	if lq.size == 0 {
		return errQueueIsReleased
	}

	if lq.isFull {
		return errQueueIsFull
	}

	if lq.items[lq.tail] == nil {
		lq.items[lq.tail] = &CacheUnit[T]{obj: t}
	} else {
		lq.items[lq.tail].obj = t
	}

	lq.items[lq.tail].lastUsedDuration = time.Now()

	lq.tail++

	if lq.tail == lq.size {
		lq.tail = 0
	}
	if lq.tail == lq.head {
		lq.isFull = true
	}

	return nil
}

注:

LoopQueue类似Disruptor一样,以无锁队列的方式实现, 所以是线程非安全的,如果需要保证线程安全,操作上要在调用上进行原子操作。

ByteBufferPool

基于GOlang的原生ByteBuffer上的封装, ByteBuffer在基于网络编程中是非常常用的数据结构,提供了一个内存Buffer的操作,一般用来保证Net.Conn的Reader和Writer操作,用来接收[]byte和操作[]byte.  原生的ByteBuffer功能也比较实用, 稍微欠缺的就是没有池的概念,这样在使用过程中,申请内存空间和师傅内存空间都会有一定的开销,也会影响到go的gc,所以就在原有基础上进行了池化的功能,同时对ByteBuffer进行了实现,比ByteBuffer更加高效。

以下是主要对外接口的实现


// The function appends all the data read from r to b.
func (b *ByteBuffer) ReadFrom(r io.Reader) (int64, error) 


// WriteTo implements io.WriterTo.
func (b *ByteBuffer) WriteTo(w io.Writer) (int64, error) 


func (b *ByteBuffer) Flip(n int) (rtn []byte) 


func (b *ByteBuffer) Compact(w io.Writer, nwrite int) (int64, error)


// Write implements io.Writer - it appends p to ByteBuffer.B
func (b *ByteBuffer) Write(p []byte) (int, error) 

// The purpose of this function is bytes.Buffer compatibility.
func (b *ByteBuffer) Bytes() []byte 


// The function always returns nil.
func (b *ByteBuffer) WriteByte(c byte) error 

// Get returns an empty byte buffer from the pool.
//
// Got byte buffer may be returned to the pool via Put call.
// This reduces the number of memory allocations required for byte buffer
// management.
func Get() *ByteBuffer { return defaultPool.Get() 

// Put returns byte buffer to the pool.
//
// ByteBuffer.B mustn't be touched after returning it to the pool.
// Otherwise, data races will occur.
func Put(b *ByteBuffer) 

Bitmap

以下是主要对外接口的实现

//Bitmap Bitmap结构体
type Bitmap struct {
	vals []byte
	size uint16
}


//Set 将offset位置的值设置为value(0/1)
func (b *Bitmap) Set(offset uint16, value uint8) bool {
	if b.size < offset {
		return false
	}

	index, pos := offset>>3, offset&0x07

	if value == 0 {
		b.vals[index] &^= 0x01 << pos
	} else {
		b.vals[index] |= 0x01 << pos
	}

	return true
}


//Get 获取offset位置处的value值
func (b *Bitmap) Get(offset uint16) uint8 {
	if b.size < offset {
		return 0
	}

	index, pos := offset>>3, offset&0x07

	return (b.vals[index] >> pos) & 0x01
}

STACK

以下是主要对外接口的实现


type stack[T any] struct {
	items  []*CacheUnit[T]
	expiry []*CacheUnit[T]
	size   int
}


func (s *stack[T]) Get() *T {
	l := s.Len()
	if l == 0 {
		return nil
	}

	w := s.items[l-1]

	return w.obj
}


func (s *stack[T]) Detach() *T {
	l := s.Len()
	if l == 0 {
		return nil
	}

	w := s.items[l-1]
	s.items[l-1] = nil // avoid memory leaks
	s.items = s.items[:l-1]

	return w.obj
}


func (s *stack[T]) Insert(t *T) error {
	var o []*CacheUnit[T] = s.items
	var v *CacheUnit[T] = &CacheUnit[T]{obj: t, lastUsedDuration: time.Now()}
	s.items = append(o, v)
	return nil
}

TRIE前缀树

Trie前缀树是针对特殊场景的一种数据结构,是Key Value数据结构的一种加强场景的数据结构,根据Key的前缀的层次进行一个树形结构的构造,可以根据 匹配符 # + 来进行树形结构的搜索

比如Key

a/b/c1

a/b/c2

a/b2/c1

a/b2/c2

a1/b3 

就会构造出

a---

    b---

         c1

         c2

   b2---

         c1

         c2

a1----

    b3

这样的树结构

如果使用a/#进行key的搜索, 就可以搜索出a树下所有的key

如果使用a/+进行key的搜索,就可以搜索出a树下下一层的所有的key,但不包含下下层的key

如果对MQTT的Topic和TopicFilter比较了解的话,应该就比较熟悉这样的数据结构。

以下是主要对外接口的实现

// TrieNode  trie Node
type TrieNode[T any] struct {
	children map[string]any
	Data     *T
	parent   any // pointer of parent node
	Key      string
}


// NewTrie create a new trie tree
func NewTrie[T any]() *TrieNode[T] {
	o := newNode[T]()
	return o
}


// AddData add data
func (t *TrieNode[T]) AddData(key string, data *T) error {

	keySlice := strings.Split(key, "/")
	var pNode = t
	for _, lv := range keySlice {
		if _, ok := pNode.children[lv]; !ok {
			pNode.children[lv] = pNode.NewChild()
		}
		pNode = pNode.children[lv].(*TrieNode[T])
	}
	pNode.Data = data
	pNode.Key = key

	return nil
}


// Find walk through the tire and return the node that represent the key
// return nil if not found
func (t *TrieNode[T]) Find(key string) *TrieNode[T] 


func (t *TrieNode[T]) LoopKey(key string, fn OnMatch[T]) 

结束语

以上的数据结构都是boot4go里进行底层架构编程常用的包的封装,Github项目地址,可以搜索boot4go-util。 

欢迎大家关注

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

inthirties

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值