Eureka-Client
Golang實現eureka-client
原理
根據Java版本的源碼,可以看出client主要是通過REST請求來與server進行通信。
Java版本的核心實現:com.netflix.discovery.DiscoveryClient
。
其中主要邏輯如下:
- client啟動時注冊信息到server
- 定時心跳、刷新服務列表,主要是兩個線程池:
heartbeatExecutor
、cacheRefreshExecutor
- client關閉時刪除注冊信息
實現
這里不限制語言,主要是發送REST請求到server。
注冊信息
通過POST請求,將服務信息注冊到server。
請求地址:POST /eureka/apps/{APP_NAME}
信息如下:(不是完整的信息)
{
"instance":{
"instanceId":"192.168.1.107:golang-example:10000",
"hostName":"192.168.1.107",
"ipAddr":"192.168.1.107",
"app":"golang-example",
"port":{
"@enabled":"true",
"$":10000
},
"securePort":{
"@enabled":"true",
"$":443
},
"status":"UP",
"overriddenStatus":"UNKNOWN",
"dataCenterInfo":{
"name":"MyOwn",
"@class":"com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo"
}
}
}
定時心跳、刷新服務列表
服務啟動后,接下來就是維持client與server之間的心跳等。
定時心跳
默認情況下是30秒發送心跳信息到server。
請求地址:PUT /eureka/apps/{APP_NAME}/{INSTANCE_ID}?status=UP&lastDirtyTimestamp={TIMESTAMP}
定時刷新服務列表
默認情況下是30秒刷新服務列表。
刷新服務列表有全量和增量兩種方式:
- 全量:
GET /eureka/apps
- 增量(delta):
GET /eureka/apps/delta
其中,全量就是每次都拉取到所有服務信息;而增量拉取變化的服務信息,然后本地去做更新。
為了方便我只實現了全量拉取,沒有實現delta。
刪除注冊信息
在服務停止時,刪除注冊的信息即可。
請求地址:DELETE /eureka/apps/{APP_NAME}/{INSTANCE_ID}
Golang核心實現
有2個定時器:
refreshTicker
來刷新服務列表heartbeatTicker
進行心跳
func (c *Client) Start() {
c.mutex.Lock()
c.Running = true
c.mutex.Unlock()
refreshTicker := time.NewTicker(c.EurekaClientConfig.RefreshIntervalSeconds)
heartbeatTicker := time.NewTicker(c.EurekaClientConfig.HeartbeatIntervalSeconds)
go func() {
for range refreshTicker.C {
if c.Running {
if err := c.doRefresh(); err != nil {
fmt.Println(err)
}
} else {
break
}
}
}()
go func() {
if err := c.doRegister(); err != nil {
fmt.Println(err)
}
for range heartbeatTicker.C {
if c.Running {
if err := c.doHeartbeat(); err != nil {
fmt.Println(err)
}
} else {
break
}
}
}()
}
例子
下面是使用的例子,為了在client停止時刪除注冊信息,這里用到了signal
。
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
client "github.com/xuanbo/eureka-client"
)
func main() {
// 1.創建客戶端
c := client.NewClient(&client.EurekaClientConfig{
DefaultZone: "http://127.0.0.1:8080/eureka/",
App: "golang-example",
Port: 10000,
})
// 2.啟動client,注冊到server。並定心跳、刷新服務列表
c.Start()
sigs := make(chan os.Signal)
exit := make(chan bool, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
// 隨便弄一個請求
http.HandleFunc("/services", func(writer http.ResponseWriter, request *http.Request) {
// 3.獲取所有服務(status均為UP)
services := c.Services
b, _ := json.Marshal(services)
_, _ = writer.Write(b)
})
server := &http.Server{
Addr: ":10000",
Handler: http.DefaultServeMux,
}
// 啟動http服務
go func() {
if err := server.ListenAndServe(); err != nil {
fmt.Println(err)
}
}()
// 關閉
go func() {
fmt.Println(<-sigs)
// 停止http服務
if err := server.Close(); err != nil {
panic(err)
}
// 4.停止客戶端,並刪除注冊信息
c.Shutdown()
exit <- true
}()
<-exit
}
主要是4步,用起來比較簡單。
Github地址
說明
未在生產中使用,只是想把Golang的服務與Java的Spring Cloud結合起來玩耍。
Just for fun!