目錄
Consul
Fabio
服務發現的特點
工作原理
Demo
結合kubernetes擴容
?
Consul
hashicorp團隊開發 就是大名鼎鼎開發 vagrant 的團隊。 Consul?是一個提供服務發現、健康檢測、K/V存儲支持分布式高可用多數據中心的服務軟件。 比較類似ZooKeeper但又比它多了一些功能。 具體可以參考? Consul和ZooKeeper的區別。
?
Fabio
fabio 是 ebay 團隊用 golang 開發的一個快速、簡單零配置,能夠讓 consul 部署的應用快速支持 http(s) 的負載均衡路由器。 因為 consul 支持服務注冊與健康檢查,所以 fabio 能夠零配置提供負載,升級部署從未如此簡單。 根據項目的介紹,fabio 能提供每秒15000次請求。 有了這兩個組件非常容易做服務發現與自動負載均衡, "神器在手、天下我有!" ^ _ ^
?
服務發現的特點
服務與服務之間的調用通常需要在配置文件中填寫好主機和端口,但是這樣不易于維護,且分布式環境中不易于部署與擴容。 那么此時就需要考慮服務啟動的時候自己把主機和端口以及一些其他信息注冊到注冊中心,這樣其他服務可以從中找到它。 甚至更為簡單的,注冊完畢后通過 DNS 的方式來『尋址』。比如 Zookeepr 可以很好的完成這個工作,但是其中還有一個弊端就是服務的健康檢查,服務注冊到注冊中心之后如何保證這個服務一定可用?此時就需要自己來寫邏輯,當服務不可用的時候自動從注冊中心下線。 Consul 可以很輕易的解決這個問題。
?
工作原理
Consul 提供了一套健康檢測機制,簡單的說針對 http 類型的服務(consul 也支持其他類型例如tcp)在注冊的時候可以順便注冊下健康檢測的信息,提供一個健康檢測的地址(url)以及一個頻率超時時間,這樣的話 consul 會定期的發送這個url請求,當狀態碼是200的時候設置此服務是健康的狀態,否則是故障狀態。 既然注冊到consul的服務能夠自己維護健康狀態,此時 fabio 的工作就很簡單了! 就是直接從consul 注冊表里面取出健康的服務,根據服務注冊時候的 tags 配置自動創建自己的路由表,然后當一個 http 請求過來的時候自動去做負載均衡 簡單的流程圖如下:
======????服務注冊?????=========? ? ? ? ?========= ? A服務? ? ? <------>? ? ? ?consul集群??---->??健康的?A/不健康的?A?集群 ======????健康檢查?????=========? ? ? ? ?========= ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ^ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? |?加入/移出路由表 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?======== ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fabio?集群 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?======== ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?|?A服務???如果找到則成功路由,否則返回錯誤 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? V ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?http?請求 ?
?
Demo
這里我們開始寫一個 demo 服務來體驗一波 consul+ fabio, 順便用 docker + k8s 來編排擴容。 因為 consul + fabio 都支持 docker 的方式運行,這里均以 docker 方式為例子。
docker pull magiconair/fabio?docker pull consul
consul 可以用集群的方式開發環境中也可以用 dev 模式 可以 參考這里。 這里可以參考我整理的單機部署 consul 集群的 docker compose 配置:
version:?'2'services:
consul_server_1:
image:?"consul:latest"
container_name:?"consul_server_1"
environment:CONSUL_LOCAL_CONFIG:?'{"leave_on_terminate":?true}'
networks:app_net:ipv4_address:?172.17.0.3
command:?"agent?-server?-bind=172.17.0.3?-client=172.17.0.3?-retry-join=172.17.0.2"consul_server_2:
image:?"consul:latest"
container_name:?"consul_server_2"
ports:-?"8600:8600"-?"8500:8500"
networks:app_net:ipv4_address:?172.17.0.4
command:?"agent?-server?-bind=172.17.0.4?-client=172.17.0.4?-retry-join=172.17.0.3?-ui"consul_server_3:
image:?"consul:latest"
container_name:?"consul_server_3"
environment:CONSUL_LOCAL_CONFIG:?'{"leave_on_terminate":?true}'
networks:app_net:ipv4_address:?172.17.0.5
command:?"agent?-server?-bind=172.17.0.5?-client=172.17.0.5?-retry-join=172.17.0.4?-bootstrap-expect=3"networks:
app_net:
driver:?bridge
ipam:config:-?subnet:?172.17.0.0/24
也可以按照 docker hub 上的文檔自己部署, 啟動成功之后訪問下 consul ui。
然后部署 faibo docker-compose.yml
fabio:
image:?"magiconair/fabio"
ports:
-?"9998:9998"
-?"9999:9999"
volumes:
-?./fabio.properties:/etc/fabio/fabio.properties
fabio 雖然說是零配置但是某些情況下還是需要個性化配置一些東西,此時可以去寫一個簡單的配置 fabio.properties指定了 consul 的地址端口以及自身的一些統計信息等等
registry.consul.register.addr?=?172.16.0.21:9998
registry.consul.addr?=?172.16.0.21:8500
metrics.target?=?stdout
?
fabio 不僅僅可以與 consul 結合,也可以手動寫一些路由規則 語法如下:
route?add?<svc>?<src>?<dst>?weight?<w>?tags?"<t1>,<t2>,..."
-?Add?route?for?service?svc?from?src?to?dst?and?assign?weight?and?tagsroute?add?<svc>?<src>?<dst>?weight?<w>
-?Add?route?for?service?svc?from?src?to?dst?and?assign?weight
詳情請移步。 此時安裝部署完畢之后跑一個 demo 試試。 官方提供了一個簡單的實現:
package?mainimport?(
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"path/filepath"
"strconv"
"strings""github.com/magiconair/fabio-example/_third_party/github.com/hashicorp/consul/api"
)func?main()?{
var?addr,?name,?prefix?string
flag.StringVar(&addr,?"addr",?"127.0.0.1:5000",?"host:port?of?the?service")
flag.StringVar(&name,?"name",?filepath.Base(os.Args[0]),?"name?of?the?service")
flag.StringVar(&prefix,?"prefix",?"",?"comma-sep?list?of?host/path?prefixes?to?register")
flag.Parse()if?prefix?==?""?{flag.Usage()os.Exit(1)
}//?register?prefixes
prefixes?:=?strings.Split(prefix,?",")
for?_,?p?:=?range?prefixes?{http.HandleFunc(p,?func(w?http.ResponseWriter,?r?*http.Request)?{fmt.Fprintf(w,?"Serving?%s?from?%s?on?%s\n",?r.RequestURI,?name,?addr)})
}//?start?http?server
go?func()?{log.Printf("Listening?on?%s?serving?%s",?addr,?prefix)if?err?:=?http.ListenAndServe(addr,?nil);?err?!=?nil?{log.Fatal(err)}
}()//?register?consul?health?check?endpoint
http.HandleFunc("/health",?func(w?http.ResponseWriter,?r?*http.Request)?{fmt.Fprintln(w,?"OK")
})//?build?urlprefix-host/path?tag?list
//?e.g.?urlprefix-/foo,?urlprefix-/bar,?...
var?tags?[]string
for?_,?p?:=?range?prefixes?{tags?=?append(tags,?"urlprefix-"+p)
}//?get?host?and?port?as?string/int
host,?portstr,?err?:=?net.SplitHostPort(addr)
if?err?!=?nil?{log.Fatal(err)
}
port,?err?:=?strconv.Atoi(portstr)
if?err?!=?nil?{log.Fatal(err)
}//?register?service?with?health?check
serviceID?:=?name?+?"-"?+?addr
service?:=?&api.AgentServiceRegistration{ID:??????serviceID,Name:????name,Port:????port,Address:?host,Tags:????tags,Check:?&api.AgentServiceCheck{HTTP:?????"http://"?+?addr?+?"/health",Interval:?"1s",Timeout:??"1s",},
}client,?err?:=?api.NewClient(api.DefaultConfig())
if?err?!=?nil?{log.Fatal(err)
}if?err?:=?client.Agent().ServiceRegister(service);?err?!=?nil?{log.Fatal(err)
}
log.Printf("Registered?service?%q?in?consul?with?tags?%q",?name,?strings.Join(tags,?","))//?run?until?we?get?a?signal
quit?:=?make(chan?os.Signal,?1)
signal.Notify(quit,?os.Interrupt,?os.Kill)
<-quit//?deregister?service
if?err?:=?client.Agent().ServiceDeregister(serviceID);?err?!=?nil?{log.Fatal(err)
}
log.Printf("Deregistered?service?%q?in?consul",?name)
}??
程序在啟動的時候去注冊,并且注冊了健康檢測的地址/health,在退出的時候取消注冊,往 consul注冊服務的時候有個 tags ,可以允許給這個服務傳一個標簽 ,fabio 根據這個參數來自動關聯路由映射。 先跑下試試
CONSUL_HTTP_ADDR=172.16.0.21:8500?./fabio-example?-addr=172.16.0.17:9876?-prefix=a.com/
此時 consul 已經收到注冊 ,fabio 路由已經添加。
#????Service?Host????Path????Dest????Weight
1???fabio-example???a.com???/???http://172.16.0.17:9876/????100%
?
???~?curl?-iv?-H?'Host:?a.com'?172.16.0.21:9999/
*???Trying?172.16.0.21...
*?Connected?to?172.16.0.21?(172.16.0.21)?port?9999?(#0)
>?GET?/?HTTP/1.1
>?Host:?a.com
>?User-Agent:?curl/7.43.0
>?Accept:?*/*
>
<?HTTP/1.1?200?OK
HTTP/1.1?200?OK
<?Content-Length:?49
Content-Length:?49
<?Content-Type:?text/plain;?charset=utf-8
Content-Type:?text/plain;?charset=utf-8
<?Date:?Fri,?22?Jul?2016?01:01:28?GMT
Date:?Fri,?22?Jul?2016?01:01:28?GMT<
Serving?/?from?fabio-example?on?172.16.0.17:9876
*?Connection?#0?to?host?172.16.0.21?left?intact
?
結合kubernetes擴容
如果基于 k8s 做編排的話需要做一些修改,例如: ip端口 要動態的從容器里面獲取,服務啟動的時候那個 tags prefix 需要配置,可以參考 fabio的文檔。 這里我就用 java 快速搭建了一個, Spring cloud框架提供了服務發現一條龍服務,針對 consul 的就是? spring-cloud-consul?,只要一行代碼 幾行配置就能快速使用 :) Application類上添加 @EnableDiscoveryClient 注解。 application.yml
spring:
cloud:
consul:discovery:healthCheckPath:?${management.contextPath}/health??#健康檢測的路徑?healthCheckInterval:?15s?#健康檢測的頻率tags:?urlprefix-api.xxxx.com/??#fabio?的路由規則
ps: spring 健康檢測可以用 spring-boot-starter-actuator快速實現。 然后 k8s 里面需要擴容就可以任性擴容了,因為如果不采用服務發現的方式, k8s 擴容時候可能 http 請求會轉發到啟動完畢的容器,但是服務并不可用(例如 java 進程雖然啟動了,但是初始化可能需要幾分鐘...)。 感受一下 擴容10個
$?kubectl?scale?--replicas=10?rc?api$?kubectl?get?pods[root@172-16-0-17?fabio-example]#?kubectl?get?pods
NAME?????????????????????????????????????????????READY?????STATUS????RESTARTS???AGE
api-6xytx?????????????????????????????????????????1/1???????Running???0??????????11s
api-9e5838075aae036e2dc971984855e379-ac30s????????1/1???????Running???0??????????14h
api-dfmtv?????????????????????????????????????????1/1???????Running???0??????????11s
api-eo01h?????????????????????????????????????????1/1???????Running???0??????????11s
api-hn1kv?????????????????????????????????????????1/1???????Running???0??????????11s
api-iyqmg?????????????????????????????????????????1/1???????Running???0??????????11s
api-k32ud?????????????????????????????????????????1/1???????Running???0??????????11s
api-q10a7?????????????????????????????????????????1/1???????Running???0??????????11s
api-re7e1?????????????????????????????????????????1/1???????Running???0??????????11s
api-tm2pk?????????????????????????????????????????1/1???????Running???0??????????11s
10秒鐘瞬間擴容至10個, 然后看下 consul 與 fabio
fabio
#????Service?Host????Path????Dest????????????????Weight
1???api?api.com?????/???http://172.31.9.3:8080/?????10%
2???api?api.com?????/???http://172.31.9.2:8080/?????10%
3???api?api.com?????/???http://172.31.82.6:8080/????10%
4???api?api.com?????/???http://172.31.82.4:8080/????10%
5???api?api.com?????/???http://172.31.28.3:8080/????10%
6???api?api.com?????/???http://172.31.28.2:8080/????10%
7???api?api.com?????/???http://172.31.23.7:8080/????10%
8???api?api.com?????/???http://172.31.23.2:8080/????10%
9???api?api.com?????/???http://172.31.12.6:8080/????10%
10??api?api.com?????/???http://172.31.12.4:8080/????10%
總結
以上是生活随笔 為你收集整理的Consul + fabio 实现自动服务发现、负载均衡 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。