Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案,Consul的方案更“一站式”,内置了服务注册与发现框 架、具有以下性质:
分布一致性协议实现、
健康检查、
Key/Value存储、
多数据中心方案,
不再需要依赖其他工具(比如ZooKeeper等)。
使用起来也较 为简单。Consul使用Go语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与Docker等轻量级容器可无缝配合 。
基于 Mozilla Public License 2.0 的协议进行开源. Consul 支持健康检查,并允许 HTTP 和 DNS 协议调用 API 存储键值对.
一致性协议采用 Raft 算法,用来保证服务的高可用. 使用 GOSSIP 协议管理成员和广播消息, 并且支持 ACL 访问控制.
这里使用consul,当然用etcd等也ok
详细内容参见官网文档:https://www.consul.io/api-docs/agent/service
首先搭建一个注册中心,可以直接docker起,这里部署一个单节点
sudo docker run --name=consul -itd -p 8500:8500 consul agent -server -bootstrap -ui -client 0.0.0.0
说明:
测试访问虚拟机地址加8500端口即可访问consul界面,此时还没有任何数据
这里基于apizza手动注册一个服务
consul service 是一个web服务,所有相关操作通过请求完成,请求内容和url参考文档如下截图:
其余略,参考官方文档:https://www.consul.io/api-docs/agent/service
首先下载cansul包,cansul也是go开发的项目,我大golang在分布式服务领域天下无双
go get github.com/hashicorp/consul/api
代码注册的本质还是http请求,但是consul自带的api包帮我们实现很多功能,不bb,上代码
首先声明一个控制类,并实现连接consul服务的方法
type ConsulContrl struct {
Client *api.Client
Config * api.AgentServiceRegistration
}
func ConnConsul(addr string)(*ConsulContrl,error){
csc := new(ConsulContrl)
config := api.DefaultConfig()
config.Address = addr
var err error
csc.Client,err = api.NewClient(config)
return csc,err
}
这里我自己写了一个默认的配置
func (c * ConsulContrl)Regist(config *api.AgentServiceRegistration)error{
if config !=nil{
c.Config = config
}else {
c.Config = &api.AgentServiceRegistration{
Kind: "HTTP",
ID: "mytestserver",
Name: "myserver",
Tags: []string{"haha","hehe"},
Port: 666,
Address: "192.168.10.106",
EnableTagOverride: false,
Meta: map[string]string{"haha":"hehe"},
Weights: &api.AgentWeights{
Passing: 10,
Warning: 1,
},
Check: &api.AgentServiceCheck{
Interval: "10s",
Timeout: "5s",
HTTP: "http://192.168.10.106:666/health",
Method: "GET",
},
}
}
var err error
if err = c.Client.Agent().ServiceRegister(c.Config);err!=nil{
return err
}
return nil
}
func (c * ConsulContrl)Deregister()error{
return c.Client.Agent().ServiceDeregister(c.Config.ID)
}
func (c*ConsulContrl)ServiceList()(map[string]*api.AgentService,error){
return c.Client.Agent().Services()
}
//consul方法
func (c *ConsulContrl)GetServiceConfiguration(serverid string)(*api.AgentService,error){
server,_,err:= c.Client.Agent().Service(serverid,nil)
return server,err
}
//结合gokit的服务发现
//返回一个发现实例,引用:github.com/go-kit/kit/sd/consul
func (c * ConsulContrl)GetInstancer(service string,tage []string)*consul.Instancer{
return consul.NewInstancer(consul.NewClient(c.Client) ,log.NewLogfmtLogger(os.Stdout),service,tage,true)
}
go func(){
<- time.After(time.Second*3)
s,err := cscontrol.ConnConsul("192.168.10.10:8500")
if err!=nil{
fmt.Println(err)
return
}
if s.Regist(nil)!=nil{
fmt.Println(err)
return
}
list,err :=s.ServiceList()
if err!=nil{
fmt.Println(err)
return
}
fmt.Printf("%v\n",*list["mytestserver"])
self ,err := s.GetServiceConfiguration("mytestserver")
if err!=nil{
fmt.Println(err)
return
}
fmt.Printf("%v\n",self)
s.Deregister()
}()
输出打印
结合服务发现,写一个创建负载均衡的方法
//添加引用 kithttp "github.com/go-kit/kit/transport/http"
func GetUserEndpointer(service string,tag []string,methond string,e kithttp.EncodeRequestFunc,d kithttp.DecodeResponseFunc,logf log.Logger) lb.Balancer {
//func(ctx context.Context, request interface{}) (response interface{}, err error)
f := func(instance string) (endpoint.Endpoint, io.Closer, error){
//这里的instance 是从consul中读取到的service的address,就是ip+端口
u,err:= url.Parse("http://"+instance)
if err!=nil{
return nil,nil,err
}
//NewClient创建一个http的客户端,先编码函数,再解码函数,和server相反
client:= kithttp.NewClient(methond,u,e,d)
return client.Endpoint(),nil,nil
}
//结合服务发现创建负载均衡
//第一个参数中的util.DefaultConsul是ConsulContrl
per:= sd.NewEndpointer(util.DefaultConsul.GetInstancer(service,tag),f,logf)
//创建轮询负载均衡
return lb.NewRoundRobin(per)
//创建随机负载均衡
//return lb.NewRandom(per,time.Now().UnixNano())
}
生成一个子服务的负载均衡
//连接consul后,创建负载均衡
bal = GetUserEndpointer("usermanage",[]string{"user"},"GET",EncodeUser,DecodeUser,log.NewLogfmtLogger(os.Stdout))
func EncodeUser(_ context.Context,r *http.Request,requ interface{}) error{
//TODO
//编码方法,执行完此方法后访问子服务
return nil
}
func DecodeUser(_ context.Context,r *http.Response) (response interface{}, err error){
//TODO
//解码方法
return string(data),nil
}
使用这个负载均衡
//具体的业务调用
func (u *UserService) GetName(userid int) string {
//从负载均衡中取出一个服务方法
end,err := bal.Endpoint()
if err!=nil{
return err.Error()
}
//执行这个方法
resp,err:= end(context.Background(),nil)
if err!=nil{
return err.Error()
}
return resp.(string)
}
未完待续