用Go語言編寫一個(gè)簡單的分布式系統(tǒng)
分布式
- 注冊服務(wù):
RegistryService - 日志服務(wù):
LogService - 其他服務(wù):
GradingService、portal
RegistryService
RegistryService 提供的服務(wù):
- 提供
/services接口,用于其他服務(wù)在啟動或者停止時(shí)告知POST:告訴RegistryService,我啟動了一個(gè)服務(wù),調(diào)用add方法DELETE:告訴RegistryService,我停止了一個(gè)服務(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時(shí),需要使用到鎖
mutex.Lock() append(xxx, xxx) mutex.UnLock()
- 服務(wù)發(fā)現(xiàn):
比如說
GradingService依賴LogService,那么GradingService就需要知道LogService的地址這個(gè)時(shí)候
RegistryService就可以通過registrations列表來通知GradingService,LogService的地址RegistryService是通過ServiceUpdateURL來通知的,GradingService,LogService的地址 - 服務(wù)發(fā)現(xiàn)需要分兩步進(jìn)行
如果
GradingService啟動時(shí),如果LogService已經(jīng)啟動了,那么RegistryService就可以直接通知GradingService,LogService的地址(r.sendRequiredServices(reg)方法)如果
GradingService啟動時(shí),如果LogService還沒有啟動,那么RegistryService就不會通知GradingService,LogService的地址,等到LogService啟動后,RegistryService才會通知GradingService,LogService的地址(notify方法)
RegistryService 對外只需要提供 RegisterService 方法,其他服務(wù)調(diào)用這個(gè)函數(shù),就能夠獲取 RegistryService 提供的服務(wù)
- 調(diào)用
RegisterService提供的接口/services,將服務(wù)注冊到RegistryService中 - 為注冊的服務(wù)添加路由:
ServiceUpdateURL - 為注冊的服務(wù)添加
ServeHTTP方法,用于處理ServiceUpdateURL的請求,這個(gè)請求在方法sendRequiredServices調(diào)用時(shí)相應(yīng),更新providers中的service - 為每個(gè)注冊的服務(wù)提供健康檢查
最后在提供一個(gè) ShutdownService 用于像 /services 接口發(fā)送 delete 請求,告知 RegistryService,我停止了一個(gè)服務(wù)
LogService
LogService 服務(wù)是對日志進(jìn)行管理,將其他服務(wù)的日志進(jìn)行收集、存儲,提供 /log 接口,用于其他服務(wù)將日志發(fā)送給 LogService
GradingService 和 Portal
這兩個(gè)是業(yè)務(wù)服務(wù)
- 在啟動服務(wù)時(shí)調(diào)用方法
RegistryService,將自己注冊到RegistryService中 - 在停止服務(wù)時(shí)調(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í)將數(shù)據(jù)附加到文件尾部os.O_CREATE:如果文件不存在,則創(chuàng)建一個(gè)新文件
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
可以將整個(gè)文件內(nèi)容讀取到內(nèi)存中,可以將請求體的內(nèi)容讀取到內(nèi)存中
ps:將整個(gè)文件的內(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)一個(gè)結(jié)構(gòu)體實(shí)現(xiàn)了 ServeHTTP 方法后,那么這個(gè)結(jié)構(gòu)體就實(shí)現(xiàn)了 http.Handler 接口
實(shí)現(xiàn)了 http.Handler 接口的結(jié)構(gòu)體,就可以作為 http.Handle 方法的第二個(gè)參數(shù)
然后調(diào)用 http.ListenAndServe 方法就可以啟動一個(gè)服務(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)建了一個(gè)新的bytes.Buffer對象,用于存儲編碼后的JSON數(shù)據(jù)enc := json.NewEncoder(buf)創(chuàng)建了一個(gè)新的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方法返回一個(gè)可能的錯(cuò)誤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è)置,例如超時(shí)時(shí)間、重試機(jī)制等Do(req)是http.Client類型的方法,用于執(zhí)行一個(gè)HTTP請求并返回響應(yīng)- 它接受一個(gè)
http.Request對象作為參數(shù),表示要發(fā)送的請求
- 它接受一個(gè)
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)建一個(gè)新的日志記錄器實(shí)例,用于將日志消息寫入指定的輸出地,并可選擇性地添加前綴字符串
- 以文件的形式記錄日志,用
log.New創(chuàng)建一個(gè)新的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í),需要定義一個(gè)類型別名,然后在類型別名上實(shí)現(xiàn) Write 方法,那么這個(gè)類型別名就能夠傳入 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語言編寫一個(gè)簡單的分布式系統(tǒng)的詳細(xì)內(nèi)容,更多關(guān)于Go語言分布式系統(tǒng)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
重學(xué)Go語言之基礎(chǔ)數(shù)據(jù)類型詳解
Go語言有非常強(qiáng)大的數(shù)據(jù)類型系統(tǒng),其支持的數(shù)據(jù)類型大體上可分為四類:基礎(chǔ)數(shù)據(jù)類型、引用數(shù)據(jù)類型、接口類型、復(fù)合類型。本文就來講講它們各自的用法吧2023-02-02
Go?Web開發(fā)之Gin多服務(wù)配置及優(yōu)雅關(guān)閉平滑重啟實(shí)現(xiàn)方法
這篇文章主要為大家介紹了Go?Web開發(fā)之Gin多服務(wù)配置及優(yōu)雅關(guān)閉平滑重啟實(shí)現(xiàn)方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
如何使用工具自動監(jiān)測SSL證書有效期并發(fā)送提醒郵件
本文介紹了如何開發(fā)一個(gè)工具,用于每日檢測SSL證書剩余有效天數(shù)并通過郵件發(fā)送提醒,工具基于命令行,通過SMTP協(xié)議發(fā)送郵件,需配置SMTP連接信息,本文還提供了配置文件樣例及代碼實(shí)現(xiàn),幫助用戶輕松部署和使用該工具2024-10-10
go語言通過odbc操作Access數(shù)據(jù)庫的方法
這篇文章主要介紹了go語言通過odbc操作Access數(shù)據(jù)庫的方法,實(shí)例分析了Go語言通過odbc連接、查詢與關(guān)閉access數(shù)據(jù)庫的技巧,需要的朋友可以參考下2015-03-03
goland2020.2.x永久激活碼破解詳細(xì)教程親測可用(Windows Linux Mac)
這篇文章主要介紹了goland2020.2.x永久激活碼破解詳細(xì)教程親測可用(Windows Linux Mac) ,對goland激活碼注冊碼相關(guān)知識感興趣的朋友跟隨小編一起看看吧2020-11-11
Golang Cron 定時(shí)任務(wù)的實(shí)現(xiàn)示例
這篇文章主要介紹了Golang Cron 定時(shí)任務(wù)的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
golang給函數(shù)參數(shù)設(shè)置默認(rèn)值的幾種方式小結(jié)(函數(shù)參數(shù)默認(rèn)值
在日常開發(fā)中我們有時(shí)候需要使用默認(rèn)設(shè)置,下面這篇文章主要給大家介紹了關(guān)于golang給函數(shù)參數(shù)設(shè)置默認(rèn)值的幾種方式小結(jié)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01

