用Go語言編寫一個簡單的分布式系統(tǒng)
分布式
- 注冊服務(wù):
RegistryService
- 日志服務(wù):
LogService
- 其他服務(wù):
GradingService
、portal
RegistryService
RegistryService
提供的服務(wù):
- 提供
/services
接口,用于其他服務(wù)在啟動或者停止時告知POST
:告訴RegistryService
,我啟動了一個服務(wù),調(diào)用add
方法DELETE
:告訴RegistryService
,我停止了一個服務(wù),調(diào)用remove
方法
- 通過
add
函數(shù)將服務(wù)添加到registrations
列表中r.registrations = append(r.registrations, reg)
- 通過
remove
函數(shù)將服務(wù)從registrations
列表中移除r.registrations = append(r.registrations[:i], r.registrations[i+1:]...)
- 這里需要注意的是:要保證線程安全,也就是在
append
時,需要使用到鎖
mutex.Lock() append(xxx, xxx) mutex.UnLock()
- 服務(wù)發(fā)現(xiàn):
比如說
GradingService
依賴LogService
,那么GradingService
就需要知道LogService
的地址這個時候
RegistryService
就可以通過registrations
列表來通知GradingService
,LogService
的地址RegistryService
是通過ServiceUpdateURL
來通知的,GradingService
,LogService
的地址 - 服務(wù)發(fā)現(xiàn)需要分兩步進(jìn)行
如果
GradingService
啟動時,如果LogService
已經(jīng)啟動了,那么RegistryService
就可以直接通知GradingService
,LogService
的地址(r.sendRequiredServices(reg)
方法)如果
GradingService
啟動時,如果LogService
還沒有啟動,那么RegistryService
就不會通知GradingService
,LogService
的地址,等到LogService
啟動后,RegistryService
才會通知GradingService
,LogService
的地址(notify
方法)
RegistryService
對外只需要提供 RegisterService
方法,其他服務(wù)調(diào)用這個函數(shù),就能夠獲取 RegistryService
提供的服務(wù)
- 調(diào)用
RegisterService
提供的接口/services
,將服務(wù)注冊到RegistryService
中 - 為注冊的服務(wù)添加路由:
ServiceUpdateURL
- 為注冊的服務(wù)添加
ServeHTTP
方法,用于處理ServiceUpdateURL
的請求,這個請求在方法sendRequiredServices
調(diào)用時相應(yīng),更新providers
中的service
- 為每個注冊的服務(wù)提供健康檢查
最后在提供一個 ShutdownService
用于像 /services
接口發(fā)送 delete
請求,告知 RegistryService
,我停止了一個服務(wù)
LogService
LogService
服務(wù)是對日志進(jìn)行管理,將其他服務(wù)的日志進(jìn)行收集、存儲,提供 /log
接口,用于其他服務(wù)將日志發(fā)送給 LogService
GradingService 和 Portal
這兩個是業(yè)務(wù)服務(wù)
- 在啟動服務(wù)時調(diào)用方法
RegistryService
,將自己注冊到RegistryService
中 - 在停止服務(wù)時調(diào)用方法
ShutdownService
,將自己從RegistryService
中移除
api
os.OpenFile
用于指定模式打開文件,并返回文件的指針
func OpenFile(name string, flag int, perm FileMode) (*File, error)
flag
參數(shù):
os.O_RDONLY
:只讀模式打開文件os.O_WRONLY
:只寫模式打開文件os.O_RDWR
:讀寫模式打開文件os.O_APPEND
:追加模式,寫入內(nèi)容時將數(shù)據(jù)附加到文件尾部os.O_CREATE
:如果文件不存在,則創(chuàng)建一個新文件
perm
參數(shù):
- 0:無權(quán)限
- 1:執(zhí)行權(quán)限
- 2:寫權(quán)限
- 3:寫和執(zhí)行權(quán)限
- 4:讀權(quán)限
- 5:讀和執(zhí)行權(quán)限
- 6:讀和寫權(quán)限
- 7:讀、寫和執(zhí)行權(quán)限
0644
:表示文件的所有者可以讀取和寫入文件,文件所屬組和其他用戶只能讀取文件。這是比較常見的設(shè)置0600
:表示文件的所有者可以讀取和寫入文件,但是文件所屬組和其他用戶不能訪問該文件。這種權(quán)限安全性較高
ioutil.ReadAll
可以將整個文件內(nèi)容讀取到內(nèi)存中,可以將請求體的內(nèi)容讀取到內(nèi)存中
ps:將整個文件的內(nèi)容或者請求體一次性讀取到內(nèi)存中,對于非常大的文件或者請求體,內(nèi)存占用過高
fmt.Scanln
會阻塞程序的執(zhí)行,直到用戶在終端輸入一行內(nèi)容并按下回車鍵,然后它會將用戶輸入的值存儲到傳入的參數(shù)中
它主要用于讀取并解析簡單的基本類型數(shù)據(jù)
func main(){ var name string var age int fmt.Print("Enter your name: ") fmt.Scanln(&name) fmt.Print("Enter your age: ") fmt.Scanln(&age) fmt.Printf("Hello, %s! You are %d years old.\n", name, age) }
http
http.Server
ListenAndServe
:啟動服務(wù),并監(jiān)聽指定的地址和端口,會阻塞Shutdown
:優(yōu)雅地關(guān)閉服務(wù),可以保證正在處理的服務(wù)不會被中斷
var srv htto.Server go func(){ srv.ListenAndServe() }() go func(){ srv.Shutdown() }()
ServeHTTP
當(dāng)一個結(jié)構(gòu)體實(shí)現(xiàn)了 ServeHTTP
方法后,那么這個結(jié)構(gòu)體就實(shí)現(xiàn)了 http.Handler
接口
實(shí)現(xiàn)了 http.Handler
接口的結(jié)構(gòu)體,就可以作為 http.Handle
方法的第二個參數(shù)
然后調(diào)用 http.ListenAndServe
方法就可以啟動一個服務(wù),會自動調(diào)用 ServeHTTP
方法來處理請求
func main() { http.Handle("/ping", &MyHandler{}) http.ListenAndServe(":8080", nil) } type MyHandler struct{} func (mh MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: w.WriteHeader(http.StatusOK) w.Write([]byte("pong")) default: w.WriteHeader(http.StatusMethodNotAllowed) } }
將結(jié)構(gòu)體序列化
buf := new(bytes.Buffer)
創(chuàng)建了一個新的bytes.Buffer
對象,用于存儲編碼后的JSON
數(shù)據(jù)enc := json.NewEncoder(buf)
創(chuàng)建了一個新的JSON
編碼器enc
,并將其關(guān)聯(lián)到buf
對象。這意味著編碼后的JSON
數(shù)據(jù)將被寫入到buf
中err := enc.Encode(r)
使用JSON
編碼器enc
將結(jié)構(gòu)體r
編碼為JSON
數(shù)據(jù),并將結(jié)果寫入到buf
中。Encode
方法返回一個可能的錯誤err
type Registration struct { ServiceName string ServiceURL string } r := Registration{ ServiceName: "LogService", ServiceURL: "http://localhost:3000/services", } buf := new(bytes.Buffer) enc := json.NewEncoder(buf) err := enc.Encode(r) res, err := http.Post(ServicesURL, "application/json", buf)
使用 http 默認(rèn)請求
http.DefaultClient
是標(biāo)準(zhǔn)庫中提供的默認(rèn)HTTP
請求。它已經(jīng)預(yù)先配置好了一些默認(rèn)的設(shè)置,例如超時時間、重試機(jī)制等Do(req)
是http.Client
類型的方法,用于執(zhí)行一個HTTP
請求并返回響應(yīng)- 它接受一個
http.Request
對象作為參數(shù),表示要發(fā)送的請求
- 它接受一個
req, _ := http.NewRequest(http.MethodDelete, "http://localhost:3000/services", bytes.NewBuffer([]byte("http://localhost:4000/log"))) req.Header.Add("Content-Type", "text/plain") res, err := http.DefaultClient.Do(req)
log
log.New
log.New
用于創(chuàng)建一個新的日志記錄器實(shí)例,用于將日志消息寫入指定的輸出地,并可選擇性地添加前綴字符串
- 以文件的形式記錄日志,用
log.New
創(chuàng)建一個新的log
實(shí)例,然后調(diào)用log.Printf
方法將日志寫入文件
它接收 io.Writer
類型的參數(shù),os.OpenFile
返回的文件指針類型 *os.File
實(shí)現(xiàn)了 io.Writer
接口,所以可以將文件指針傳入 log.New
方法中
代碼參考如下:
import ( "fmt" stlog "log" "os" ) func main() { file, err := os.OpenFile("./logs", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) if err != nil { fmt.Println(err) } defer file.Close() log := stlog.New(file, "[go] - ", stlog.LstdFlags) log.Println("hello world") }
- 重寫
log
的Write
方法,也能實(shí)現(xiàn)將日志寫入文件
在重寫 Write
方法時,需要定義一個類型別名,然后在類型別名上實(shí)現(xiàn) Write
方法,那么這個類型別名就能夠傳入 log.New
方法中
代碼參考如下:
import ( stlog "log" "os" ) type filelog string func (fl filelog) Write(data []byte) (int, error) { file, err := os.OpenFile(string(fl), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) if err != nil { return 0, err } defer file.Close() file.Write(data) return len(data), nil } func main() { log := stlog.New(filelog("./logs"), "[go] - ", stlog.LstdFlags) log.Println("hello world") }
以上就是用Go語言編寫一個簡單的分布式系統(tǒng)的詳細(xì)內(nèi)容,更多關(guān)于Go語言分布式系統(tǒng)的資料請關(guān)注腳本之家其它相關(guān)文章!
- Go使用Redis實(shí)現(xiàn)分布式鎖的常見方法
- Golang使用Zookeeper實(shí)現(xiàn)分布式鎖
- Go分布式鏈路追蹤實(shí)戰(zhàn)探索
- 分布式架構(gòu)在Go語言網(wǎng)站的應(yīng)用
- Golang微服務(wù)框架Kratos實(shí)現(xiàn)分布式任務(wù)隊(duì)列Asynq的方法詳解
- 基于Golang實(shí)現(xiàn)Redis分布式鎖解決秒殺問題
- Go語言使用Etcd實(shí)現(xiàn)分布式鎖
- SpringBoot分布式文件存儲數(shù)據(jù)庫mongod
- 在Go語言開發(fā)中實(shí)現(xiàn)高性能的分布式日志收集的方法
相關(guān)文章
golang中實(shí)現(xiàn)graphql請求的方法
這篇文章主要介紹了如何在golang中實(shí)現(xiàn)graphql請求,在本文中,我們介紹了如何使用gqlgen來構(gòu)建GraphQL服務(wù),需要的朋友可以參考下2023-04-04Golang 字符串轉(zhuǎn)time類型實(shí)現(xiàn)
本文主要介紹了Golang 字符串轉(zhuǎn)time類型實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03詳解如何在Golang中實(shí)現(xiàn)CORS(跨域)
很多時候,需要允許Web應(yīng)用程序在不同域之間(跨域)實(shí)現(xiàn)共享資源,本文將簡介跨域、CORS的概念,以及如何在Golang中如何實(shí)現(xiàn)CORS,文中有詳細(xì)的示例代碼,需要的朋友可以參考下2023-10-10Golang 實(shí)現(xiàn)簡單隨機(jī)負(fù)載均衡
均衡算法又分為 隨機(jī),輪詢,加權(quán)輪詢,哈希,而隨機(jī)負(fù)載均衡算法就是本文的重點(diǎn),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06