注册中心
因为服务是单独部署的,因此服务间要想相互调用只能通过RPC的方式:部署在机器A上的用户服务通过RPC的方式调用部署在机器B上的订单服务。 这就引入了微服务架构中一个重要的组件:服务发现与注册中心。你要调用订单服务的接口,首先你得知道哪些机器上部署了订单服务,注册中心就是干这个事的,所有的微服务都把自己注册到注册中心,这样注册中心就知道哪个服务在哪些机器上,当有调用请求的时候,由注册中心按一定的负载均衡策略把这个请求落地到具体的机器上,下面是最能说明服务注册中心的一张经典图:
consul
consul注册中心
1、Consul服务注册中心的整体架构
1.1 Consul Agent
如果要基于Consul作为服务注册中心,那么首先必须在每个服务所在的机器上部署一个Consul Agent,作为一个服务所在机器的代理。
然后还得在多台机器上部署Consul Server,这就是核心的服务注册中心。
这个Consul Agent可以用来收集你的服务信息然后发送给Consul Server,还会对你的服务不停的发送请求检查他是否健康。然后你要发现别的服务的时候,Consul Agent也会帮你转发请求给Consul Server,查询其他服务所在机器。
1.2 Consul Server
Consul Server一般要求部署3~5台机器,以保证高可用以及数据一致性。他们之间会自动实现数据同步,而且Consul Server集群会自动选举出一台机器作为leader,其他的Consul Server就是follower。
2. 服务注册与发现
服务发现协议
consul采用http和dns协议,etcd只支持http
服务注册
consul支持两种方式实现服务注册,
consul官方建议使用第二种方式。
服务发现
consul支持两种方式实现服务发现,一种是通过http API来查询有哪些服务,另外一种是通过consul agent 自带的DNS(8600端口),域名是以NAME.service.consul的形式给出,NAME即在定义的服务配置文件中,服务的名称。DNS方式可以通过check的方式检查服务。
服务间的通信协议
Consul使用gossip协议管理成员关系、广播消息到整个集群,他有两个gossip pool(LAN pool和WAN pool),LAN pool是同一个数据中心内部通信的,WAN pool是多个数据中心通信的,LAN pool有多个,WAN pool只有一个。
3、Consul如何通过Raft协议实现强一致性?
consul server节点之间的数据一致性保证,一致性协议使用的是raft,而zookeeper用的paxos,etcd采用的也是raft。
首先,库存服务注册到Leader Server的时候,会采取Raft协议,要求必须让Leader Server把这条注册数据复制给大部分的Follower Server才算成功。这就保证了,如果你认为自己注册成功了,那么必然是多台Consul Server都有这条注册数据了。
如果你刚发送给Leader Server他自己就宕机了,那么这次注册会认为失败。此时,Consul Server集群会重新选举一个Leader Server出来,你需要再次重新注册。这样就可以保证你注册成功的数据绝对不会丢,然后别人发现服务的时候一定可以从Leader Server上获取到最新的强一致的注册数据。
4、Consul如何通过Agent实现分布式健康检查?
最后说说Consul是如何通过各个服务机器上部署Agent来实现分布式健康检查的。集中式的心跳机制,比如传统的Eureka,是让各个服务都必须每隔一定时间发送心跳到Eureka Server。如果一段时间没收到心跳,那么就认为这个服务宕机了。
但是这种集中式的心跳机制会对Eureka Server造成较大的心跳请求压力,实际上平时Eureka Server接收最多的请求之一就是成千上万服务发送过来的心跳请求。
所以Consul在这块进行了架构优化,引入了Agent概念。每个机器上的Consul Agent会不断的发送请求检查服务是否健康,是否宕机。如果服务宕机了,那么就会通知Consul Server。
怎么样?是不是发现各个服务自己不用再发送心跳请求去Server了?减小了Server这部分的压力吧?没错,这就是Consul基于Agent实现的分布式健康检查机制,可以大幅度的减小Server端的压力。这样一来,哪怕你就部署个三五台机器,可以轻松支持成千上万个服务。
5、consul acl访问控制
type consulServiceRegistry struct {
serviceInstances map[string]map[string]cloud.ServiceInstance
client api.Client
localServiceInstance cloud.ServiceInstance
}
func NewConsulServiceRegistry(host string, port int, token string) (*consulServiceRegistry, error) {
if len(host) < 3 {
return nil, errors.New("check host")
}
if port <= 0 || port > 65535 {
return nil, errors.New("check port, port should between 1 and 65535")
}
config := api.DefaultConfig()
config.Address = host + ":" + strconv.Itoa(port)
config.Token = token
client, err := api.NewClient(config)
if err != nil {
return nil, err
}
return &consulServiceRegistry{client: *client}, nil
}
func TestConsulServiceDiscovery(t *testing.T) {
host := "127.0.0.1"
port := 8500
token := ""
registryDiscoveryClient, err := extension.NewConsulServiceRegistry(host, port, token)
if err != nil {
panic(err)
}
t.Log(registryDiscoveryClient.GetServices())
t.Log(registryDiscoveryClient.GetInstances("go-user-server"))
}
|