如果說類是對數(shù)據(jù)和方法的抽象和封裝,那么接口就是對類的抽象。
10年的常寧網(wǎng)站建設(shè)經(jīng)驗(yàn),針對設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。全網(wǎng)整合營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整常寧建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。成都創(chuàng)新互聯(lián)從事“常寧網(wǎng)站設(shè)計(jì)”,“常寧網(wǎng)站推廣”以來,每個客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
這里使用Java來說明,Java中的接口與`Go`中的接口是類似的,只不過Java中要顯式的聲明實(shí)現(xiàn)某個接口,而Go中不需要顯式的聲明實(shí)現(xiàn)了某個接口,只要實(shí)現(xiàn)了其中的所有方法,就默認(rèn)為實(shí)現(xiàn)了該接口。
比如說有一個Person接口:
public interface Person{ void walk();
}
有一個Student類實(shí)現(xiàn)了Person接口
public class Student implements Person { //實(shí)現(xiàn)walk方法
@Override
public void walk(){
System.out.print("Student.walk()");
} //...其他方法}
有一個Teacher類也實(shí)現(xiàn)了Person接口
public class Teacher implements Person { //實(shí)現(xiàn)walk方法
@Override
public void walk(){
System.out.print("Teacher.walk()");
} //...其他方法}
在某個類中可以這樣使用
public class SomeClass{ //這里接收一個Person類型
public static void usePerson(Person p){
p.walk()
}
public static void main(String[] args){ //可以接收任何實(shí)現(xiàn)了Person接口的類的對象
usePerson(new Student());
usePerson(new Teacher());
}
}
使用接口的好處是非常靈活,這樣就與具體的實(shí)現(xiàn)做了解耦,如果以后有其他的實(shí)現(xiàn)類,只需要實(shí)現(xiàn)Person接口就可以了,而不用去改使用的時候的代碼。
基本設(shè)計(jì)思路:
類型轉(zhuǎn)換、類型斷言、動態(tài)派發(fā)。iface,eface。
反射對象具有的方法:
編譯優(yōu)化:
內(nèi)部實(shí)現(xiàn):
實(shí)現(xiàn) Context 接口有以下幾個類型(空實(shí)現(xiàn)就忽略了):
互斥鎖的控制邏輯:
設(shè)計(jì)思路:
(以上為寫被讀阻塞,下面是讀被寫阻塞)
總結(jié),讀寫鎖的設(shè)計(jì)還是非常巧妙的:
設(shè)計(jì)思路:
WaitGroup 有三個暴露的函數(shù):
部件:
設(shè)計(jì)思路:
結(jié)構(gòu):
Once 只暴露了一個方法:
實(shí)現(xiàn):
三個關(guān)鍵點(diǎn):
細(xì)節(jié):
讓多協(xié)程任務(wù)的開始執(zhí)行時間可控(按順序或歸一)。(Context 是控制結(jié)束時間)
設(shè)計(jì)思路: 通過一個鎖和內(nèi)置的 notifyList 隊(duì)列實(shí)現(xiàn),Wait() 會生成票據(jù),并將等待協(xié)程信息加入鏈表中,等待控制協(xié)程中發(fā)送信號通知一個(Signal())或所有(Boardcast())等待者(內(nèi)部實(shí)現(xiàn)是通過票據(jù)通知的)來控制協(xié)程解除阻塞。
暴露四個函數(shù):
實(shí)現(xiàn)細(xì)節(jié):
部件:
包: golang.org/x/sync/errgroup
作用:開啟 func() error 函數(shù)簽名的協(xié)程,在同 Group 下協(xié)程并發(fā)執(zhí)行過程并收集首次 err 錯誤。通過 Context 的傳入,還可以控制在首次 err 出現(xiàn)時就終止組內(nèi)各協(xié)程。
設(shè)計(jì)思路:
結(jié)構(gòu):
暴露的方法:
實(shí)現(xiàn)細(xì)節(jié):
注意問題:
包: "golang.org/x/sync/semaphore"
作用:排隊(duì)借資源(如錢,有借有還)的一種場景。此包相當(dāng)于對底層信號量的一種暴露。
設(shè)計(jì)思路:有一定數(shù)量的資源 Weight,每一個 waiter 攜帶一個 channel 和要借的數(shù)量 n。通過隊(duì)列排隊(duì)執(zhí)行借貸。
結(jié)構(gòu):
暴露方法:
細(xì)節(jié):
部件:
細(xì)節(jié):
包: "golang.org/x/sync/singleflight"
作用:防擊穿。瞬時的相同請求只調(diào)用一次,response 被所有相同請求共享。
設(shè)計(jì)思路:按請求的 key 分組(一個 *call 是一個組,用 map 映射存儲組),每個組只進(jìn)行一次訪問,組內(nèi)每個協(xié)程會獲得對應(yīng)結(jié)果的一個拷貝。
結(jié)構(gòu):
邏輯:
細(xì)節(jié):
部件:
如有錯誤,請批評指正。
1.先安裝Go對應(yīng)的開源Swagger相關(guān)的庫
go get?github.com/swaggo/swag/cmd/swag
go get github.com/swaggo/gin-swagger
go get?github.com/swaggo/files
go get?github.com/alecthomas/template
2.驗(yàn)證是否安裝成功:swag -v
3.針對接口寫入注解
// @Summary 獲取多個標(biāo)簽
// @Tags 標(biāo)簽
// @Produce? json
// @Param name query string false "標(biāo)簽名稱" maxlength(100)
// @Param state query int false "狀態(tài)" Enums(0, 1) default(1)
// @Param page query int false "頁碼"
// @Param page_size query int false "每頁數(shù)量"
// @Success 200 {object} model.TagSwagger "成功"
// @Failure 400 {object} errcode.Error "請求錯誤"
// @Failure 500 {object} errcode.Error "內(nèi)部錯誤"
// @Router /api/v1/tags [get]
func (t Tag) List(c *gin.Context) {
}
// @Summary 新增標(biāo)簽
// @Tags 標(biāo)簽
// @Produce? json
// @Param name body string true "標(biāo)簽名稱" minlength(3) maxlength(100)
// @Param state body int false "狀態(tài)" Enums(0, 1) default(1)
// @Param created_by body string false "創(chuàng)建者" minlength(3) maxlength(100)
// @Success 200 {object} model.Tag "成功"
// @Failure 400 {object} errcode.Error "請求錯誤"
// @Failure 500 {object} errcode.Error "內(nèi)部錯誤"
// @Router /api/v1/tags [post]
func (t Tag) Create(c *gin.Context) {
}
// @Summary 更新標(biāo)簽
// @Tags 標(biāo)簽
// @Produce? json
// @Param id path int true "標(biāo)簽ID"
// @Param name body string false "標(biāo)簽名稱" minlength(3) maxlength(100)
// @Param state body int false "狀態(tài) (0為未刪除、1為已刪除)" Enums(0, 1) default(1)
// @Param modified_by body string true "修改者" minlength(3) maxlength(100)
// @Success 200 {array} model.Tag "成功"
// @Failure 400 {object} errcode.Error "請求錯誤"
// @Failure 500 {object} errcode.Error "內(nèi)部錯誤"
// @Router /api/v1/tags/{id} [put]
func (t Tag) Update(c *gin.Context) {
}
4.針對整個項(xiàng)目進(jìn)行注解,直接在main方法寫入如下注解
//@title 項(xiàng)目名稱
//@version 1.0
//@description 這里是描述
func main() {
5.生成執(zhí)行 swag init
這時會在我項(xiàng)目的docs文件夾下面生成docs.go、swagger.json、swagger.yaml三個文件
6.要在routers中進(jìn)行默認(rèn)初始化和注冊對應(yīng)的路由:
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
同時要引用 _"blog-service/docs" ,不然會報(bào)錯
7.查看接口文檔 :
8.ok,完成
1.為什么golang的開發(fā)效率高?golang是一編譯型的強(qiáng)類型語言,它在開發(fā)上的高效率主要來自于后發(fā)優(yōu)勢,不用考慮舊有惡心的歷史,又有一個較高的工程視角。良好的避免了程序員因?yàn)椤皗需不需要獨(dú)占一行”這種革命問題打架,也解決了一部分趁編譯時間找產(chǎn)品妹妹搭訕的階級敵人。它有自己的包管理機(jī)制,工具鏈成熟,從開發(fā)、調(diào)試到發(fā)布都很簡單方便;有反向接口、defer、coroutine等大量的syntacticsugar;編譯速度快,因?yàn)槭菑?qiáng)類型語言又有g(shù)c,只要通過編譯,非業(yè)務(wù)毛病就很少了;它在語法級別上支持了goroutine,這是大家說到最多的內(nèi)容,這里重點(diǎn)提一下。首先,coroutine并不稀罕,語言并不能超越硬件、操作系統(tǒng)實(shí)現(xiàn)神乎其神的功能。golang可以做到事情,其他語言也可以做到,譬如c++,在boost庫里面自己就有的coroutine實(shí)現(xiàn)(當(dāng)然用起來跟其他boost庫一樣惡心)。golang做的事情,是把這一套東西的使用過程簡化了,并且提供了一套channel的通信模式,使得程序員可以忽略諸如死鎖等問題。goroutine的目的是描述并發(fā)編程模型。并發(fā)與并行不同,它并不需要多核的硬件支持,它不是一種物理運(yùn)行狀態(tài),而是一種程序邏輯流程。它的主要目的不是利用多核提高運(yùn)行效率,而是提供一種更容易理解、不容易出錯的語言來描述問題。實(shí)際上golang默認(rèn)就是運(yùn)行在單OS進(jìn)程上面的,通過指定環(huán)境變量GOMAXPROCS才能轉(zhuǎn)身跑在多OS進(jìn)程上面。有人提到了網(wǎng)易的pomelo,開源本來是一件很不錯的事情,但是基于自己對callbackhell的偏見,我一直持有這種態(tài)度:敢用nodejs寫大規(guī)模游戲服務(wù)器的人,都是真正的勇士:)。2、Erlang與Golang的coroutine有啥區(qū)別,coroutine是啥?coroutine本質(zhì)上是語言開發(fā)者自己實(shí)現(xiàn)的、處于userspace內(nèi)的線程,無論是erlang、還是golang都是這樣。需要解決沒有時鐘中斷;碰著阻塞式i\o,整個進(jìn)程都會被操作系統(tǒng)主動掛起;需要自己擁有調(diào)度控制能力(放在并行環(huán)境下面還是挺麻煩的一件事)等等問題。那為啥要廢老大的勁自己做一套線程放userspace里面呢?并發(fā)是服務(wù)器語言必須要解決的問題;systemspace的進(jìn)程還有線程調(diào)度都太慢了、占用的空間也太大了。把線程放到userspace的可以避免了陷入systemcall進(jìn)行上下文切換以及高速緩沖更新,線程本身以及切換等操作可以做得非常的輕量。這也就是golang這類語言反復(fù)提及的超高并發(fā)能力,分分鐘給你開上幾千個線程不費(fèi)力。不同的是,golang的并發(fā)調(diào)度在i/o等易發(fā)阻塞的時候才會發(fā)生,一般是內(nèi)封在庫函數(shù)內(nèi);erlang則更夸張,對每個coroutine維持一個計(jì)數(shù)器,常用語句都會導(dǎo)致這個計(jì)數(shù)器進(jìn)行reduction,一旦到點(diǎn),立即切換調(diào)度函數(shù)。中斷介入程度的不同,導(dǎo)致erlang看上去擁有了preemptivescheduling的能力,而golang則是cooperativeshceduling的。golang一旦寫出純計(jì)算死循環(huán),進(jìn)程內(nèi)所有會話必死無疑;要有大計(jì)算量少i\o的函數(shù)還得自己主動叫runtime.Sched()來進(jìn)行調(diào)度切換。3、golang的運(yùn)行效率怎么樣?我是相當(dāng)反感所謂的ping\pong式benchmark,運(yùn)行效率需要放到具體的工作環(huán)境下面考慮。首先,它再快也是快不過c的,畢竟底下做了那么多工作,又有調(diào)度,又有g(shù)c什么的。那為什么在那些benchmark里面,golang、nodejs、erlang的響應(yīng)效率看上去那么優(yōu)秀呢,響應(yīng)快,并發(fā)強(qiáng)?并發(fā)能力強(qiáng)的原因上面已經(jīng)提到了,響應(yīng)快是因?yàn)榇罅糠亲枞絠\o操作出現(xiàn)的原因。這一點(diǎn)c也可以做到,并且能力更強(qiáng),但是得多寫不少優(yōu)質(zhì)代碼。然后,針對游戲服務(wù)器這種高實(shí)時性的運(yùn)行環(huán)境,GC所造成的跳幀問題確實(shí)比較麻煩,前面的大神@達(dá)達(dá)有比較詳細(xì)的論述和緩解方案,就不累述了。隨著golang的持續(xù)開發(fā),相信應(yīng)該會有非常大的改進(jìn)。一是屏蔽內(nèi)存操作是現(xiàn)代語言的大勢所趨,它肯定是需要被實(shí)現(xiàn)的;二是GC算法已經(jīng)相當(dāng)?shù)某墒?,效率勉勉?qiáng)強(qiáng)過得去;三是可以通過incremental的操作來均攤cpu消耗。用這一點(diǎn)點(diǎn)效率損失換取一個更高的生產(chǎn)能力是不是值得呢?我覺得是值得的,硬件已經(jīng)很便宜了,人生苦短,讓自己的生活更輕松一點(diǎn)吧:)。4、基于以上的論述,我認(rèn)為采用go進(jìn)行小范圍的MMORPG開發(fā)是可行的。