天天看點

環形緩沖區應用執行個體一、環形緩沖區的介紹二、業務場景三、解決方案

一、環形緩沖區的介紹

如果您對CPU緩沖區或者對環形緩沖區有一定了解,您可以直接跳到第二章閱讀使用場景
環形緩沖區應用執行個體一、環形緩沖區的介紹二、業務場景三、解決方案

(圖來自:來自維基百科)

概念(來自維基百科):圓形緩沖區(circular buffer),也稱作圓形隊列(circular queue),循環緩沖區(cyclic buffer),環形緩沖區(ring buffer),是一種用于表示一個固定尺寸、頭尾相連的緩沖區的資料結構,适合緩存資料流

用法(來自維基百科):圓形緩沖區的一個有用特性是:當一個資料元素被用掉後,其餘資料元素不需要移動其存儲位置。相反,一個非圓形緩沖區(例如一個普通的隊列)在用掉一個資料元素後,其餘資料元素需要向前搬移。換句話說,圓形緩沖區适合實作先進先出緩沖區,而非圓形緩沖區适合後進先出緩沖區。

圓形緩沖區适合于事先明确了緩沖區的最大容量的情形。擴充一個圓形緩沖區的容量,需要搬移其中的資料。是以一個緩沖區如果需要經常調整其容量,用連結清單實作更為合适。

寫操作覆寫圓形緩沖區中未被處理的資料在某些情況下是允許的。特别是在多媒體處理時。例如,音頻的生産者可以覆寫掉聲霸卡尚未來得及處理的音頻資料。

工作過程(來自維基百科):圓形緩沖區(circular buffer),也稱作圓形隊列(circular queue),循環緩沖區(cyclic buffer),環形緩沖區(ring buffer),是一種用于表示一個固定尺寸、頭尾相連的緩沖區的資料結構,适合緩存資料流
環形緩沖區應用執行個體一、環形緩沖區的介紹二、業務場景三、解決方案
圓形緩沖區工作機制(來自維基百科):
環形緩沖區應用執行個體一、環形緩沖區的介紹二、業務場景三、解決方案
環形緩沖區應用執行個體一、環形緩沖區的介紹二、業務場景三、解決方案

二、業務場景

1. 系統功能說明

我們的系統需要操作多台終端并且實時監聽終端的健康狀态

2. 如何實作

我們讓服務端與終端都建立了一個socket通訊,讓服務端和各個終端保持了長連接配接,這讓我們能實作服務端能夠操作終端的同時能夠接收到終端的健康狀态封包

3.生産過程中的問題

1.終端的健康封包資料過大,socket自動的分包,拆包,服務端常常出現異常,無法及時正确擷取終端的健康狀态

三、解決方案

1. 問題分析

服務端與終端之間其實是一個長時間的資料傳輸,這讓我們想到了流,在我們面臨大檔案處理時,我們首先想到的就是緩沖,這和cpu的緩沖層有些相似,cpu的緩沖層會預設一個大小,一邊一直讀取,一邊一直寫入,如果緩沖層到達最大限制,那麼就會進入阻塞。這個思想和我們的業務相似,終端一直發起,服務端一直接受,換個說法就是,終端一直寫入,服務端一直讀取。

2. 如何實作

我們借鑒了環形緩沖區的思想,使用了一個1024大小的數組,一個線程一直從數組中讀取,另外一個線程一直寫入。進而保證無論socket是否把我們的封包拆分,我們都能確定這個封包是完整的可使用的。

環形緩沖區代碼如下(go語言):

import (
	"errors"
	"io"
	"time"
)

type CircleByteBuffer struct {
	io.Reader
	io.Writer
	io.Closer
	datas []byte

	start   int
	end     int
	size    int
	isClose bool
	isEnd   bool
}

func NewCircleByteBuffer(len int) *CircleByteBuffer {
	var e = new(CircleByteBuffer)
	e.datas = make([]byte, len)
	e.start = 0
	e.end = 0
	e.size = len
	e.isClose = false
	e.isEnd = false
	return e
}

func (e *CircleByteBuffer) GetLen() int {
	if e.start == e.end {
		return 0
	} else if e.start < e.end {
		return e.end - e.start
	} else {
		return e.size - (e.start - e.end)
	}
}
func (e *CircleByteBuffer) GetFree() int {
	return e.size - e.GetLen()
}
func (e *CircleByteBuffer) Clear() {
	e.start = 0
	e.end = 0
}
func (e *CircleByteBuffer) PutByte(b byte) error {
	if e.isClose {
		return io.EOF
	}
	e.datas[e.end] = b
	var pos = e.end + 1
	if pos == e.size {
		pos = 0
	}
	for pos == e.start {
		if e.isClose {
			return io.EOF
		}
		time.Sleep(time.Millisecond)
	}
	e.end = pos
	return nil
}

func (e *CircleByteBuffer) GetByte() (byte, error) {
	if e.isClose {
		return 0, io.EOF
	}
	for e.GetLen() <= 0 {
		if e.isClose || e.isEnd {
			return 0, io.EOF
		}
		time.Sleep(time.Millisecond)
	}
	var ret = e.datas[e.start]
	pos := e.start + 1
	if pos == e.size {
		pos = 0
	}
	e.start = pos
	return ret, nil
}
func (e *CircleByteBuffer) Geti(i int) byte {
	if i >= e.GetLen() {
		panic("out buffer")
	}
	var pos = e.start + i
	if pos >= e.size {
		pos -= e.size
	}
	return e.datas[pos]
}

/*func (e*CircleByteBuffer)puts(bts []byte){
	for i:=0;i<len(bts);i++{
		e.put(bts[i])
	}
}
func (e*CircleByteBuffer)gets(bts []byte)int{
	if bts==nil {return 0}
	var ret=0
	for i:=0;i<len(bts);i++{
		if e.GetLen()<=0{break}
		bts[i]=e.get()
		ret++
	}
	return ret
}*/
func (e *CircleByteBuffer) Close() error {
	e.isClose = true
	return nil
}
func (e *CircleByteBuffer) Read(bts []byte) (int, error) {
	if e.isClose {
		return 0, io.EOF
	}
	if bts == nil {
		return 0, errors.New("bts is nil")
	}
	var ret = 0
	for i := 0; i < len(bts); i++ {
		b, err := e.GetByte()
		if err != nil {
			return ret, err
		}
		bts[i] = b
		ret++
	}
	if e.isClose {
		return ret, io.EOF
	}
	return ret, nil
}
func (e *CircleByteBuffer) Write(bts []byte) (int, error) {
	if e.isClose {
		return 0, io.EOF
	}
	if bts == nil {
		e.isEnd = true
		return 0, io.EOF
	}
	var ret = 0
	for i := 0; i < len(bts); i++ {
		err := e.PutByte(bts[i])
		if err != nil {
			return ret, err
		}
		ret++
	}
	if e.isClose {
		return ret, io.EOF
	}
	return ret, nil
} 
           

3.總結

我們在解決業務需求時,可能會受限于當時的技術水準或者其他因素,做出的選擇在現在看來并不是最優,但想想看,在這個輪子橫飛的年代,我相信一個會造輪子的人定會比隻能使用輪子的人更加優秀,他們敲下的每一行代碼對程式的影響他們都了然于心,他們會更加及時的發現問題,更加完善的解決問題。
我們專注于真實案例挖掘并寫下這系列的文章,我希望能讓大家保持思考,保持一顆熱愛編碼的心,在借鑒這些案例時能夠蹦出靈感的火花,那是我最開心的事

文章首發于微信公衆号:java企業級應用(WaXiData)

環形緩沖區應用執行個體一、環形緩沖區的介紹二、業務場景三、解決方案