相关参考
中文文档
localhost:9988
调用访问,但是一个微服务要动态的增加或者删减服务节点,重启之后ip也会发生变化,我们怎么合理地把请求均衡的分配给所有节点?ip变化之后怎么访问?这就需要用到服务动态注册与发现。我们刚才说过,需要有一个服务去存放我们的ip和端口,这个时候可能会有这样一个想法,我们根据key-value的存储方式,去存储我们服务的信息不行吗?
答案是否定的,因为作为一个服务发现服务,不仅要保存服务的访问方式(ip+port),而且还需要有服务监听的功能,隔一段时间去监听你的服务是否正常,即健康检查。
第二,可能有多个节点提供一个服务,特别是高并发的时候,我们可能会增加服务,那么我们来服务的实现负载均衡呢。
第三,如果几百个服务我们不可能一个一个去改服务的配置,而且本身作为微服务,每个服务之间应该都是独立的,一个服务的改动不应该影响到其他服务。
所以说一个服务发现的系统至少应该具备服务发现、健康检查、负载均衡和全局分布的键值存储的功能
consul是分布式的、高可用、横向扩展的。
service discovery:consul通过DNS或者HTTP接口使服务注册和服务发现变的很容易,一些外部服务,例如saas提供的也可以一样注册。
health checking:健康检测使consul可以快速的告警在集群中的操作。和服务发现的集成,可以防止服务转发到故障的服务上面。
key/value storage:一个用来存储动态配置的系统。提供简单的HTTP接口,可以在任何地方操作。
multi-datacenter:无需复杂的配置,即可支持任意数量的区域。
安装 相关安装地址
为了方便,我这里使用docker启动,相关命令如下 相关参考
zhangguofu@localhost ~ $ docker run -d -p 8500:8500 -p 8300:8309 -p 8301:8301 -p8302:8302 -p 8600:8600/udp consul consul agent -dev -client=0.0.0.0
Unable to find image 'consul:latest' locally
latest: Pulling from library/consul
895e193edb51: Pull complete
74b324295dd0: Pull complete
9ba21ab1eb9f: Pull complete
3c08861b7dc5: Pull complete
e2c643221233: Pull complete
fe56612bde98: Pull complete
Digest: sha256:28b50d5bfc7f95d515d19590d44f1cf566e550545e653452c2046c92b6a07e7e
Status: Downloaded newer image for consul:latest
530e4f9e489933e2145311fd97ad38ccf9b5ae778d17e3fd7feeeeed46cc5672
zhangguofu@bogon ~ $ curl -X PUT -H "Content-Type: application/json" -d '{"Name": "my-service", "Address": "localhost", "Port": 5000, "Tags": ["tag1", "tag2"]}' http://localhost:8500/v1/agent/service/register
zhangguofu@bogon ~ $ curl http://localhost:8500/v1/catalog/services
{
"consul": [],
"my-service": [
"tag1",
"tag2"
]
}
zhangguofu@bogon ~ $ curl http://localhost:8500/v1/catalog/service/my-service
[
{
"ID": "06788a51-1192-fed7-11cd-e088eea65330",
"Node": "530e4f9e4899",
"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": "my-service",
# 服务名称
"ServiceName": "my-service",
# 服务标签
"ServiceTags": [
"tag1",
"tag2"
],
# ip地址
"ServiceAddress": "localhost",
"ServiceWeights": {
"Passing": 1,
"Warning": 1
},
"ServiceMeta": {},
# 服务的端口号
"ServicePort": 5000,
"ServiceSocketPath": "",
"ServiceEnableTagOverride": false,
"ServiceProxy": {
"Mode": "",
"MeshGateway": {},
"Expose": {}
},
"ServiceConnect": {},
"CreateIndex": 4318,
"ModifyIndex": 4318
}
]
curl -X PUT http://localhost:8500/v1/agent/service/deregister/my-service
注意,在Consul中,如果要使用HTTP API进行服务注册,必须使用v1/agent API端点。这是因为v1/agent是Consul API中与代理进行交互的端点,可以让我们通过API与代理进行通信,从而注册、注销、查询服务等操作。使用v1/agent API端点可以帮助我们更好地管理服务实例,提高服务可用性和可靠性。
package main
import "fmt"
import consulapi "github.com/hashicorp/consul/api"
//定义服务注册的信息
type ServiceConfig struct {
ID string
Name string
Tags []string
Port int
Address string
}
//consul的服务地址
var consulIp="127.0.0.1:8500"
//注册
func RegisterSevice(s ServiceConfig) error{
config:=consulapi.DefaultConfig()
config.Address = consulIp
//获取到客户端
client, err := consulapi.NewClient(config)
if err != nil {
fmt.Printf("create consul client : %v\n", err.Error())
return err
}
registration := &consulapi.AgentServiceRegistration{
ID: s.ID,
Name: s.Name,
Port: s.Port,
Tags: s.Tags,
Address: s.Address,
}
if err := client.Agent().ServiceRegister(registration); err != nil {
fmt.Printf("register to consul error: %v\n", err.Error())
return err
}
return nil
}
func main() {
//先注册一下
service := ServiceConfig{
ID: "9527",
Name: "demo_service",
Tags: []string{"a", "b"},
Port: 10111,
Address: "192.168.0.125",
}
err := RegisterSevice(service)
if err != nil {
fmt.Printf("register to consul error: %v\n", err.Error())
}
}
//注册
func RegisterSevice(s ServiceConfig) error {
config := consulapi.DefaultConfig()
config.Address = consulIp
//获取到客户端
client, err := consulapi.NewClient(config)
if err != nil {
fmt.Printf("create consul client : %v\n", err.Error())
return err
}
registration := &consulapi.AgentServiceRegistration{
ID: s.ID,
Name: s.Name,
Port: s.Port,
Tags: s.Tags,
Address: s.Address,
}
//开启健康检测
check := &consulapi.AgentServiceCheck{}
//检测服务地址
check.TCP = fmt.Sprintf("%s:%d",service.Address,service.Port )
//设置过期时间
check.Timeout="5s"
//每5s执行一次
check.Interval="5s"
//检查失败超过20s将会被注销
check.DeregisterCriticalServiceAfter = "20s"
//将check属性添加上去
registration.Check=check
if err := client.Agent().ServiceRegister(registration); err != nil {
fmt.Printf("register to consul error: %v\n", err.Error())
return err
}
return nil
}
func tcpService(){
network:="tcp"
address:=fmt.Sprintf("%s:%d",service.Address,service.Port )
//绑定和监听tpc和端口
listen, err := net.Listen(network, address)
if err != nil {
fmt.Println("listen err")
}
//关闭监听
defer listen.Close()
for{
//等待连接
_,err:=listen.Accept()
if err != nil {
fmt.Println("accept error")
}
}
}
func httpService() {
url:=fmt.Sprintf("%s:%d",service.Address, service.Port)
http.HandleFunc("/", func(w http.ResponseWriter,r *http.Request){
fmt.Printf("run http service in %s",url)
})
//监听http请求
err := http.ListenAndServe(url, nil)
if err != nil {
fmt.Printf("http service error")
}
}
//注册
func RegisterSevice(s ServiceConfig) error {
config := consulapi.DefaultConfig()
config.Address = consulIp
//获取到客户端
client, err := consulapi.NewClient(config)
if err != nil {
fmt.Printf("create consul client : %v\n", err.Error())
return err
}
// 定义要注册的服务实例
services := []*consulapi.AgentServiceRegistration{
{
ID: "service-1",
Name: "test-service",
Address: "192.168.1.10",
Port: 8080,
Tags: []string{"v1"},
Check: &consulapi.AgentServiceCheck{
HTTP: "http://192.168.1.10:8080/health",
Interval: "10s",
},
},
{
ID: "service-2",
Name: "test-service",
Address: "192.168.1.11",
Port: 8080,
Tags: []string{"v2"},
Check: &consulapi.AgentServiceCheck{
HTTP: "http://192.168.1.11:8080/health",
Interval: "10s",
},
},
}
for _, service := range services {
err = client.Agent().ServiceRegister(service)
if err != nil {
fmt.Println("注册服务失败:", err)
return nil
}
fmt.Println("注册服务成功:", service.ID)
}
return nil
}
package main
import (
"encoding/json"
"fmt"
consulapi "github.com/hashicorp/consul/api"
)
//consul的服务地址
var consulIp = "127.0.0.1:8500"
var serviceName = "demo_service"
func main() {
//请求consul
config := consulapi.DefaultConfig()
config.Address = consulIp
client, err := consulapi.NewClient(config)
if err != nil {
fmt.Printf("consul client error: %v", err)
}
service, _, err := client.Health().Service(serviceName, "", false, nil)
if err != nil {
fmt.Printf("get service error: %v", err)
}
addr := ""
for _, v := range service {
j, err := json.Marshal(v)
if err != nil {
return
}
fmt.Printf("%s \n\n", j)
addr = fmt.Sprintf("http://%s:%d", v.Service.Address, v.Service.Port)
}
fmt.Printf("the service is %s",addr)
}