高校思政专题网站建设,音乐外链网站,ui设计的就业前景,h5制作开发价目表需求
最近在弄一个游戏的gate网关转发服务器#xff0c;服务器之间使用的是nats通讯#xff0c;gate的作用是接收客户端发来的消息转发到对应的服务器上#xff0c;并从nats上获取游戏服务器发送给客户端的消息并转发给客户端。前面接收还好处理#xff0c;因为都是发布订…需求最近在弄一个游戏的gate网关转发服务器服务器之间使用的是nats通讯gate的作用是接收客户端发来的消息转发到对应的服务器上并从nats上获取游戏服务器发送给客户端的消息并转发给客户端。前面接收还好处理因为都是发布订阅模式的消息收到消息直接向nats上扔就行了。但转发服务器来的消息就不一样了从nats上取的速度远大于gate转发给客户端的速度会有数据囤积在nats中。为了解决这个问题可以一个协和去nats中取数据用多个协程并行转发给客户端因为现在cpu都是n核的多协程转发肯定会快的不止一点点这里要注意一点的是同个玩家的消息转发的顺序不能变就是按一定的规则把同一个玩家的所有消息在同一个协程上转发就可以了。为了现实这个转发需要一个无限缓存的channel先装nats中的数据读出来分别放到各转发协程的channal中转发协程只从自己的channel取数据一条一条慢慢转发给客户端就行了。设计先说一下设计无限缓存channel的大至思路然后直接上完整代码。分析要达到前面的需求我们需要设计的无限缓存channel应该满足几个要求缓存无限。因为不知道nats那边会接收到多少消息而转发可能会因网络波动阻塞。不能阻塞写。要保证接收nats消息的协程能及时处理所有的消息并写入转发协程的channel中。无数据时阻塞读此特性保持和普通channle一样。没数据时转发协程处理阻塞等待。读写都应通过channle操作和普通channel的操作一样。channle被关闭后未读取的数据应该仍然可读此特性和普通channle保持一致。针对上面的要求设计如下因为go暂不支持操作符重载所以封装一个结构体包含二个channel来分离读(Out Channel)和写(In Channel)。因为channel的缓存大小是有限的需要一个可无限扩容缓存多于的数据这里可以使用ringbuffer来实现。无限缓存内部数据FIFO实现当Out Channel还没有满时并且buf中没有数据读取In中数据将其放入Out直到Out满当Buf中有数据时无论Out是否满都将将In中读到的数据直接写入到Buf中目的就是为了保证数据的FIFO原则实现代码完整工程参见https://github.com/zngw/zchan1、双向环形链表package ringbuffer import ( errors fmt sync/atomic ) type T interface{} var ErrIsEmpty errors.New(ringbuffer is empty) type cell struct { Data []T // 数据部分 fullFlag bool // cell满的标志 next *cell // 指向后一个cellBuffer pre *cell // 指向前一个cellBuffer r int // 下一个要读的指针 w int // 下一个要下的指针 } type RingBuffer struct { cellSize int // cell大小 cellCount int // cell数量 count int32 // 有效元素个数 readCell *cell // 下一个要读的cell writeCell *cell // 下一个要写的cell } // NewRingBuffer 新建一个RingBuffer包含两个cell func NewRingBuffer(cellSize int) (buf *RingBuffer, err error) { if cellSize 0 || cellSize(cellSize-1) ! 0 { err fmt.Errorf(初始大小必须是 2 的幂) return } rootCell : cell{ Data: make([]T, cellSize), } lastCell : cell{ Data: make([]T, cellSize), } rootCell.pre lastCell lastCell.pre rootCell rootCell.next lastCell lastCell.next rootCell buf RingBuffer{ cellSize: cellSize, cellCount: 2, count: 0, readCell: rootCell, writeCell: rootCell, } return } // Read 读取数据 func (r *RingBuffer) Read() (data T, err error) { // 无数据 if r.IsEmpty() { err ErrIsEmpty return } // 读取数据并将读指针向右移动一位 data r.readCell.Data[r.readCell.r] r.readCell.r atomic.AddInt32(r.count, -1) // 此cell已经读完 if r.readCell.r r.cellSize { // 读指针归零并将该cell状态置为非满 r.readCell.r 0 r.readCell.fullFlag false // 将readCell指向下一个cell r.readCell r.readCell.next } return } // Pop 读一个元素读完后移动指针 func (r *RingBuffer) Pop() (data T) { data, err : r.Read() if errors.Is(err, ErrIsEmpty) { panic(ErrIsEmpty.Error()) } return } // Peek 窥视 读一个元素仅读但不移动指针 func (r *RingBuffer) Peek() (data T) { if r.IsEmpty() { panic(ErrIsEmpty.Error()) } // 仅读 data r.readCell.Data[r.readCell.r] return } // Write 写入数据 func (r *RingBuffer) Write(value T) { // 在 r.writeCell.w 位置写入数据指针向右移动一位 r.writeCell.Data[r.writeCell.w] value r.writeCell.w atomic.AddInt32(r.count, 1) // 当前cell写满了 if r.writeCell.w r.cellSize { // 指针置0将该cell标记为已满并指向下一个cell r.writeCell.w 0 r.writeCell.fullFlag true r.writeCell r.writeCell.next } // 下一个cell也已满扩容 if r.writeCell.fullFlag true { r.grow() } } // grow 扩容 func (r *RingBuffer) grow() { // 新建一个cell newCell : cell{ Data: make([]T, r.cellSize), } // 总共三个cellwriteCellpreCellnewCell // 本来关系 preCell writeCell // 现在将newcell插入preCell newCell writeCell pre : r.writeCell.pre pre.next newCell newCell.pre pre newCell.next r.writeCell r.writeCell.pre newCell // 将writeCell指向新建的cell r.writeCell r.writeCell.pre // cell 数量加一 r.cellCount } // IsEmpty 判断RingBuffer是否为空 func (r *RingBuffer) IsEmpty() bool { return r.Len() 0 } // Capacity RingBuffer容量 func (r *RingBuffer) Capacity() int { return r.cellCount * r.cellSize } // Len RingBuffer数据长度 func (r *RingBuffer) Len() (count int) { count int(r.count) return } // Reset 重置为仅指向两个cell的ring func (r *RingBuffer) Reset() { // 没有数据切cellCount只有两个时无需重置 if r.count 0 r.cellCount 2 { return } lastCell : r.readCell.next lastCell.w 0 lastCell.r 0 r.readCell.r 0 r.readCell.w 0 r.cellCount 2 r.count 0 lastCell.next r.readCell }2. 无限缓存channelpackage zchan import ( github.com/zngw/ringbuffer ) type T interface{} type ZChan struct { In chan- T // 写入channel Out -chan T // 读取channel buffer *ringbuffer.RingBuffer // 双向环形链表 } // Len uc中总共的元素数量 func (uc *ZChan) Len() int { return len(uc.In) uc.BufLen() len(uc.Out) } // BufLen uc的buf中的元素数量 func (uc *ZChan) BufLen() int { return uc.buffer.Len() } // New 新建一个无限缓存的Channel并指定In和Out大小(In和Out设置得一样大) func New(initCapacity int) (ch *ZChan, err error) { rb, err : ringbuffer.NewRingBuffer(512) if err ! nil { return } in : make(chan T, initCapacity) out : make(chan T, initCapacity) ch ZChan{In: in, Out: out, buffer: rb} go process(in, out, ch) return } // 内部Worker Goroutine实现 func process(in, out chan T, ch *ZChan) { defer close(out) // in 关闭数据读取后也把out关闭 // 不断从in中读取数据放入到out或者buf中 loop: for { // 第一步从in中读取数据 value, ok : -in if !ok { // in 关闭了退出loop break loop } // 第二步将数据存储到out或者buf中 if ch.buffer.Len() 0 { // 当buf中有数据时新数据优先存放到buf中确保数据FIFO原则 ch.buffer.Write(value) } else { // out 没有满,数据放入out中 select { case out - value: continue default: } // out 满了数据放入buf中 ch.buffer.Write(value) } // 第三步处理buf一直尝试把buf中的数据放入到out中直到buf中没有数据 for ch.buffer.Len() 0 { select { // 为了避免阻塞in还要尝试从in中读取数据 case val, ok : -in: if !ok { // in 关闭了退出loop break loop } // 因为这个时候out是满的新数据直接放入buf中 ch.buffer.Write(val) // 将buf中数据放入out case out - ch.buffer.Peek(): ch.buffer.Pop() if ch.buffer.IsEmpty() { ch.buffer.Reset() } } } } // in被关闭退出loop后buf中还有可能有未处理的数据将他们塞入out中并重置buf for ch.buffer.Len() 0 { out - ch.buffer.Pop() } }3. 使用测试package main import ( fmt github.com/zngw/zchan time ) func main() { zc, err : zchan.New(4) if err ! nil { panic(err.Error()) } go func() { // 写入channel数据 // 10毫秒写入1次 for i : 0; i 55; i { zc.In - i fmt.Printf(写入数据%v, chan长度%v Buf长度 %v \n, i, zc.Len(), zc.BufLen()) time.Sleep(10 * time.Millisecond) } close(zc.In) }() for v : range zc.Out { // 20 毫毛读取一次数据 fmt.Printf(读取入数据%v, chan长度%v Buf长度%v \n, v, zc.Len(), zc.BufLen()) time.Sleep(20 * time.Millisecond) } }AI大模型学习福利作为一名热心肠的互联网老兵我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。一、全套AGI大模型学习路线AI大模型时代的学习之旅从基础到前沿掌握人工智能的核心技能因篇幅有限仅展示部分资料需要点击文章最下方名片即可前往获取二、640套AI大模型报告合集这套包含640份报告的合集涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师还是对AI大模型感兴趣的爱好者这套报告合集都将为您提供宝贵的信息和启示。因篇幅有限仅展示部分资料需要点击文章最下方名片即可前往获三、AI大模型经典PDF籍随着人工智能技术的飞速发展AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型如GPT-3、BERT、XLNet等以其强大的语言理解和生成能力正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。因篇幅有限仅展示部分资料需要点击文章最下方名片即可前往获四、AI大模型商业化落地方案因篇幅有限仅展示部分资料需要点击文章最下方名片即可前往获作为普通人入局大模型时代需要持续学习和实践不断提高自己的技能和认知水平同时也需要有责任感和伦理意识为人工智能的健康发展贡献力量