隊(duì)列的概念在 順序隊(duì)列 中,而使用循環(huán)隊(duì)列的目的主要是規(guī)避假溢出造成的空間浪費(fèi),在使用循環(huán)隊(duì)列處理假溢出時(shí),主要有三種解決方案
創(chuàng)新互聯(lián)專注于企業(yè)營銷型網(wǎng)站、網(wǎng)站重做改版、新華網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5技術(shù)、商城系統(tǒng)網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為新華等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
本文提供后兩種解決方案。
順序隊(duì)和循環(huán)隊(duì)列是一種特殊的線性表,與順序棧類似,都是使用一組地址連續(xù)的存儲(chǔ)單元依次存放自隊(duì)頭到隊(duì)尾的數(shù)據(jù)元素,同時(shí)附設(shè)隊(duì)頭(front)和隊(duì)尾(rear)兩個(gè)指針,但我們要明白一點(diǎn),這個(gè)指針并不是指針變量,而是用來表示數(shù)組當(dāng)中元素下標(biāo)的位置。
本文使用切片來完成的循環(huán)隊(duì)列,由于一開始使用三個(gè)參數(shù)的make關(guān)鍵字創(chuàng)建切片,在輸出的結(jié)果中不包含nil值(看起來很舒服),而且在驗(yàn)證的過程中發(fā)現(xiàn)使用append()函數(shù)時(shí)切片內(nèi)置的cap會(huì)發(fā)生變化,在消除了種種障礙后得到了一個(gè)四不像的循環(huán)隊(duì)列,即設(shè)置的指針是順序隊(duì)列的指針,但實(shí)際上進(jìn)行的操作是順序隊(duì)列的操作。最后是對(duì)make()函數(shù)和append()函數(shù)的一些使用體驗(yàn)和小結(jié),隊(duì)列的應(yīng)用放在鏈隊(duì)好了。
官方描述(片段)
即切片是一個(gè)抽象層,底層是對(duì)數(shù)組的引用。
當(dāng)我們使用
構(gòu)建出來的切片的每個(gè)位置的值都被賦為interface類型的初始值nil,但是nil值也是有大小的。
而使用
來進(jìn)行初始化時(shí),雖然生成的切片中不包含nil值,但是無法通過設(shè)置的指針變量來完成入隊(duì)和出隊(duì)的操作,只能使用append()函數(shù)來進(jìn)行操作
在go語言中,切片是一片連續(xù)的內(nèi)存空間加上長度與容量的標(biāo)識(shí),比數(shù)組更為常用。使用 append 關(guān)鍵字向切片中追加元素也是常見的切片操作
正是基于此,在使用go語言完成循環(huán)隊(duì)列時(shí),首先想到的就是使用make(type, len, cap)關(guān)鍵字方式完成切片初始化,然后使用append()函數(shù)來操作該切片,但這一方式出現(xiàn)了很多問題。在使用append()函數(shù)時(shí),切片的cap可能會(huì)發(fā)生變化,用不好就會(huì)發(fā)生擴(kuò)容或收縮。最終造成的結(jié)果是一個(gè)四不像的結(jié)果,入隊(duì)和出隊(duì)操作變得與指針變量無關(guān),失去了作為循環(huán)隊(duì)列的意義,用在順序隊(duì)列還算合適。
參考博客:
Go語言中的Nil
Golang之nil
Go 語言設(shè)計(jì)與實(shí)現(xiàn)
網(wǎng)關(guān)=反向代理+負(fù)載均衡+各種策略,技術(shù)實(shí)現(xiàn)也有多種多樣,有基于 nginx 使用 lua 的實(shí)現(xiàn),比如 openresty、kong;也有基于 zuul 的通用網(wǎng)關(guān);還有就是 golang 的網(wǎng)關(guān),比如 tyk。
這篇文章主要是講如何基于 golang 實(shí)現(xiàn)一個(gè)簡單的網(wǎng)關(guān)。
轉(zhuǎn)自: troy.wang/docs/golang/posts/golang-gateway/
整理:go語言鐘文文檔:
啟動(dòng)兩個(gè)后端 web 服務(wù)(代碼)
這里使用命令行工具進(jìn)行測試
具體代碼
直接使用基礎(chǔ)庫 httputil 提供的NewSingleHostReverseProxy即可,返回的reverseProxy對(duì)象實(shí)現(xiàn)了serveHttp方法,因此可以直接作為 handler。
具體代碼
director中定義回調(diào)函數(shù),入?yún)?http.Request,決定如何構(gòu)造向后端的請(qǐng)求,比如 host 是否向后傳遞,是否進(jìn)行 url 重寫,對(duì)于 header 的處理,后端 target 的選擇等,都可以在這里完成。
director在這里具體做了:
modifyResponse中定義回調(diào)函數(shù),入?yún)?http.Response,用于修改響應(yīng)的信息,比如響應(yīng)的 Body,響應(yīng)的 Header 等信息。
最終依舊是返回一個(gè)ReverseProxy,然后將這個(gè)對(duì)象作為 handler 傳入即可。
參考 2.2 中的NewSingleHostReverseProxy,只需要實(shí)現(xiàn)一個(gè)類似的、支持多 targets 的方法即可,具體實(shí)現(xiàn)見后面。
作為一個(gè)網(wǎng)關(guān)服務(wù),在上面 2.3 的基礎(chǔ)上,需要支持必要的負(fù)載均衡策略,比如:
隨便 random 一個(gè)整數(shù)作為索引,然后取對(duì)應(yīng)的地址即可,實(shí)現(xiàn)比較簡單。
具體代碼
使用curIndex進(jìn)行累加計(jì)數(shù),一旦超過 rss 數(shù)組的長度,則重置。
具體代碼
輪詢帶權(quán)重,如果使用計(jì)數(shù)遞減的方式,如果權(quán)重是5,1,1那么后端 rs 依次為a,a,a,a,a,b,c,a,a,a,a…,其中 a 后端會(huì)瞬間壓力過大;參考 nginx 內(nèi)部的加權(quán)輪詢,或者應(yīng)該稱之為平滑加權(quán)輪詢,思路是:
后端真實(shí)節(jié)點(diǎn)包含三個(gè)權(quán)重:
操作步驟:
具體代碼
一致性 hash 算法,主要是用于分布式 cache 熱點(diǎn)/命中問題;這里用于基于某 key 的 hash 值,路由到固定后端,但是只能是基本滿足流量綁定,一旦后端目標(biāo)節(jié)點(diǎn)故障,會(huì)自動(dòng)平移到環(huán)上最近的那么個(gè)節(jié)點(diǎn)。
實(shí)現(xiàn):
具體代碼
每一種不同的負(fù)載均衡算法,只需要實(shí)現(xiàn)添加以及獲取的接口即可。
然后使用工廠方法,根據(jù)傳入的參數(shù),決定使用哪種負(fù)載均衡策略。
具體代碼
作為網(wǎng)關(guān),中間件必不可少,這類包括請(qǐng)求響應(yīng)的模式,一般稱作洋蔥模式,每一層都是中間件,一層層進(jìn)去,然后一層層出來。
中間件的實(shí)現(xiàn)一般有兩種,一種是使用數(shù)組,然后配合 index 計(jì)數(shù);一種是鏈?zhǔn)秸{(diào)用。
具體代碼
Golang的interface,和別的語言是不同的。它不需要顯式的implements,只要某個(gè)struct實(shí)現(xiàn)了interface里的所有函數(shù),編譯器會(huì)自動(dòng)認(rèn)為它實(shí)現(xiàn)了這個(gè)interface。
SICP里詳細(xì)解釋了為什么同一個(gè)接口,需要根據(jù)不同的數(shù)據(jù)類型,有不同的實(shí)現(xiàn);以及如何做到這一點(diǎn)。在這里沒有OO的概念,先把OO放到一邊,從原理上看一下這是怎么做到的。
先把大概原理放在這里,然后再舉例子。為了實(shí)現(xiàn)多態(tài),需要維護(hù)一張全局的查找表,它的功能是根據(jù)類型名和方法名,返回對(duì)應(yīng)的函數(shù)入口。當(dāng)我增加了一種類型,需要把新類型的名字、相應(yīng)的方法名和實(shí)際函數(shù)入口添加到表里。這基本上就是所謂的動(dòng)態(tài)綁定了,類似于C++里的vtable。對(duì)于SICP中使用的lisp語言來說,這些工作需要手動(dòng)完成。而對(duì)于java,則通過implements完成了這項(xiàng)工作。而golang則用了更加激進(jìn)的方式,連implements都省了,編譯器自動(dòng)發(fā)現(xiàn)自動(dòng)綁定。
給你個(gè)fmt.Printf的例子:
echo 函數(shù)不定參數(shù),其調(diào)用fmt.Printf進(jìn)行輸出,因?yàn)関是一個(gè)slice,所以傳遞給fmt.Printf的時(shí)候需要 v...,就類似append(slice1,slice2...)
package main
import (
"fmt"
)
func main() {
echo("Hello %s, I am %s\n", "Bob", "John")
}
func echo(format string, v ... interface{}) {
fmt.Printf(format, v...)
}