微服务注册发现de小实验
本文是个人在学习书籍《Go语言高并发与微服务实战》第6章内容过程中,而动手做的小实验,涉及consul和go-kid相关知识,仅供参考。
安装consul
到consul官网,找到适合你系统的包并下载,解压后拷贝consul到你的PATH路径中即可。
https://www.consul.io/downloads
在Unix系统中
~/bin
和/usr/local/bin
是通常的安装目录。
在Windows系统中可以安装到%PATH%
的路径中。
consul的简单使用
单机启动
consul agent -dev
启动后可打开浏览器查看:http://127.0.0.1:8500/
集群启动
主机器1:consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -node=hdp2 -bind=10.0.0.52
从机器2:consul agent -data-dir /tmp/consul -node=hdp3 -bind=10.0.0.53
加入集群:consul join 10.0.0.52
查询服务
集群成员:consul members
DNS API: dig @127.0.0.1 -p 8600 web.service.consul
HTTP API: curl http://localhost:8500/v1/catalog/service/web
编写go-kit服务
层级划分
go-kit的架构分为三层结构:Transport层,Endpoint层,Service层。
- Transport层主要负责与传输协议HTTP,GRPC,THRIFT等相关的逻辑
- Endpoint层主要负责 request/response 格式的转换,以及公用拦截器相关的逻辑;
- Service层则专注于业务逻辑。
服务端实现
大致逻辑如下:
- 服务发现的客户端连接consul
- 声明service层对象
- 通过service层对象,创建各个endpoint层对象
- transport层创建http路由处理,跟endpoint层映射关系
- 向consul进行服务注册
- 开启http端口的监听服务
- 程序退出时向consul取消注册
main.go
package main
import (
"context"
"flag"
"fmt"
"github.com/longjoy/micro-go-book/ch6-discovery/config"
"github.com/longjoy/micro-go-book/ch6-discovery/discover"
"github.com/longjoy/micro-go-book/ch6-discovery/endpoint"
"github.com/longjoy/micro-go-book/ch6-discovery/service"
"github.com/longjoy/micro-go-book/ch6-discovery/transport"
uuid "github.com/satori/go.uuid"
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
)
func main() {
// 从命令行中读取相关参数,没有时使用默认值
var (
// 服务地址和服务名
servicePort = flag.Int("service.port", 10086, "service port")
serviceHost = flag.String("service.host", "127.0.0.1", "service host")
serviceName = flag.String("service.name", "SayHello", "service name")
// consul 地址
consulPort = flag.Int("consul.port", 8500, "consul port")
consulHost = flag.String("consul.host", "127.0.0.1", "consul host")
)
flag.Parse()
ctx := context.Background()
errChan := make(chan error)
// 声明服务发现客户端
var discoveryClient discover.DiscoveryClient
discoveryClient, err := discover.NewKitDiscoverClient(*consulHost, *consulPort)
// 获取服务发现客户端失败,直接关闭服务
if err != nil{
config.Logger.Println("Get Consul Client failed")
os.Exit(-1)
}
// 声明并初始化 Service
var svc = service.NewDiscoveryServiceImpl(discoveryClient)
// 创建打招呼的Endpoint
sayHelloEndpoint := endpoint.MakeSayHelloEndpoint(svc)
// 创建服务发现的Endpoint
discoveryEndpoint := endpoint.MakeDiscoveryEndpoint(svc)
//创建健康检查的Endpoint
healthEndpoint := endpoint.MakeHealthCheckEndpoint(svc)
endpts := endpoint.DiscoveryEndpoints{
SayHelloEndpoint: sayHelloEndpoint,
DiscoveryEndpoint: discoveryEndpoint,
HealthCheckEndpoint: healthEndpoint,
}
//创建http.Handler
r := transport.MakeHttpHandler(ctx, endpts, config.KitLogger)
// 定义服务实例ID
instanceId := *serviceName + "-" + uuid.NewV4().String()
// 启动 http server
go func() {
config.Logger.Println("Http Server start at port:" + strconv.Itoa(*servicePort))
//启动前执行注册
if !discoveryClient.Register(*serviceName, instanceId, "/health", *serviceHost, *servicePort, nil, config.Logger){
config.Logger.Printf("string-service for service %s failed.", serviceName)
// 注册失败,服务启动失败
os.Exit(-1)
}
handler := r
errChan <- http.ListenAndServe(":" + strconv.Itoa(*servicePort), handler)
}()
go func() {
// 监控系统信号,等待 ctrl + c 系统信号通知服务关闭
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
errChan <- fmt.Errorf("%s", <-c)
}()
error := <-errChan
//服务退出取消注册
discoveryClient.DeRegister(instanceId, config.Logger)
config.Logger.Println(error)
}
完整代码详见附录的配套代码 ch6-discovery ,这里就不完整展示了。
运行效果
# go run main.go
2021/07/16 16:45:46 Http Server start at port:10086
2021/07/16 16:45:46 Register Service Success!
consul后台会看到SayHello服务已注册,使用查询指令也能正常查到结果。
# dig @127.0.0.1 -p 8600 SayHello.service.consul SRV
; <<>> DiG 9.10.6 <<>> @127.0.0.1 -p 8600 SayHello.service.consul SRV
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 881
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;SayHello.service.consul. IN SRV
;; ANSWER SECTION:
SayHello.service.consul. 0 IN SRV 1 1 10086 7f000001.addr.dc1.consul.
;; ADDITIONAL SECTION:
7f000001.addr.dc1.consul. 0 IN A 127.0.0.1
yisonlideMacBook-Pro.local.node.dc1.consul. 0 IN TXT "consul-network-segment="
;; Query time: 1 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Fri Jul 16 16:51:51 CST 2021
;; MSG SIZE rcvd: 183
# curl http://localhost:8500/v1/catalog/service/SayHello
[
{
"ID": "7e80199a-e09b-018f-c0c9-f35d36445717",
"Node": "yisonlideMacBook-Pro.local",
"Address": "127.0.0.1",
"Datacenter": "dc1",
"TaggedAddresses": {
"lan": "127.0.0.1",
"lan_ipv4": "127.0.0.1",
"wan": "127.0.0.1",
"wan_ipv4": "127.0.0.1"
},
"NodeMeta": {
"consul-network-segment": ""
},
"ServiceKind": "",
"ServiceID": "SayHello-5f93a8c1-f3c5-44cb-a996-96ef6de69358",
"ServiceName": "SayHello",
"ServiceTags": [],
"ServiceAddress": "127.0.0.1",
"ServiceTaggedAddresses": {
"lan_ipv4": {
"Address": "127.0.0.1",
"Port": 10086
},
"wan_ipv4": {
"Address": "127.0.0.1",
"Port": 10086
}
},
"ServiceWeights": {
"Passing": 1,
"Warning": 1
},
"ServiceMeta": {},
"ServicePort": 10086,
"ServiceEnableTagOverride": false,
"ServiceProxy": {
"MeshGateway": {},
"Expose": {}
},
"ServiceConnect": {},
"CreateIndex": 22,
"ModifyIndex": 22
}
]
检查服务功能
# curl http://127.0.0.1:10086/say-hello
{"message":"Hello World!"}
# curl http://127.0.0.1:10086/discovery
{"error":"invalid request parameter"}
# curl http://127.0.0.1:10086/discovery?serviceName=SayHello
{"instances":[{"ID":"SayHello-5f93a8c1-f3c5-44cb-a996-96ef6de69358","Service":"SayHello","Tags":[],"Meta":null,"Port":10086,"Address":"127.0.0.1","TaggedAddresses":{"lan_ipv4":{"Address":"127.0.0.1","Port":10086},"wan_ipv4":{"Address":"127.0.0.1","Port":10086}},"Weights":{"Passing":1,"Warning":1},"EnableTagOverride":false,"CreateIndex":22,"ModifyIndex":22,"Proxy":{"MeshGateway":{},"Expose":{}},"Connect":{}}],"error":""}
总结
服务注册与发现,原理看起来没有太复杂,也是使用 服务端+客户端 进行通信,使用一致性算法Raft或ZAB保证服务;
如果单单只是看书、只是看原理,也许能理解,但或许不太深刻;只有动手实践后,你才明白其中的意义。
虽然只试验了其中一种组件,但这时再回过头来看各个服务注册发现的组件对比,或者做架构选型时,就清晰多了。
附录
- 《Go语言高并发与微服务实战》配套代码
- 《consul官方中文文档》电子版
- 若有需要,请关注文末我的公众号,找我领取吧
关于我
name: yison.li
blog: http://yyeer.com
github: https://github.com/yisonli