在开始之前,我们先来回顾下业内对于微服务架构的定义。简单来说,微服务就是用一组小服务的方式来构建一个应用,服务独立运行在不同的进程中,服务之间通过轻量的通讯机制(如 RESTful 接口)来交互,并且服务可以通过自动化部署方式独立部署。
1.什么是服务发现,在微服务架构中,服务发现的作用是什么?
之前做单体式应用开发时很少提及服务发现,因为传统单体应用动态性不强,不会频繁的更新和重新发布,也较少进行自动伸缩。传统单体应用的网络位置很少发生变化,在发生变化时,由运维人员手工更新一下它们的配置文件,也不是什么太大的问题。
而微服务架构则完全不同,微服务会被频繁的更新和重新发布,频繁的根据负载情况进行动态伸缩,微服务实例还可能受资源调度影响而从一台服务器迁移到另一台服务器。
总而言之,在微服务架构中,微服务实例的网络位置发生变化是一种常态,所以必须提供一种机制,使得服务消费者在服务提供者的网络位置发生变化时,能够及时获得最新的位置信息,一般是提供一个网络位置稳定的服务注册中心,服务提供者的网络位置被注册到注册中心,并在网络位置发生变化的时候及时更新,而服务消费者定期向注册中心获取服务提供者的最新位置信息,这就是最基本的服务发现机制。较为复杂的服务发现实现除了服务提供者的位置信息外,还可以向服务消费者提供服务提供者的描述信息、状态信息和资源使用信息,以供服务消费者实现更为复杂的服务选择逻辑。
2.服务发现的具体流程是怎样的?
服务发现的流程比较简单,去年我翻译了 Chris Richardson 的一些微服务文章,对服务发现的流程做了些基本的描述,比较完备的说,服务发现流程应该分为两种模式:
客户端发现:
- 服务提供者的实例在启动时或者位置信息发生变化时会向服务注册表注册自身,在停止时会向服务注册表注销自身,如果服务提供者的实例发生故障,在一段时间内不发送心跳之后,也会被服务注册表注销。
- 服务消费者的实例会向服务注册表查询服务提供者的位置信息,然后通过这些位置信息直接向服务提供者发起请求。
服务端发现:
- 第一步与客户端发现相同。
- 服务消费者不直接向服务注册表查询,也不直接向服务提供者发起请求,而是将对服务提供者的请求发往一个中央路由器或者负载均衡器,中央路由器或者负载均衡器查询服务注册表获取服务提供者的位置信息,并将请求转发给服务提供者。
这两种模式各有利弊,客户端发现模式的优势是,服务消费者向服务提供者发起请求时比服务端发现模式少了一次网络跳转,劣势是服务消费者需要内置特定的服务发现客户端和服务发现逻辑;服务端发现模式的优势是服务消费者无需内置特定的服务发现客户端和服务发现逻辑,劣势是多了一次网络跳转,并且需要基础设施环境提供中央路由机制或者负载均衡机制。目前客户端发现模式应用的多一些,因为这种模式的对基础设施环境没有特殊的要求,和基础设施环境也没有过多的耦合性。
3.目前都有哪些服务发现的解决方案?各有什么优缺点?
其实可选方案并不多,所以选择起来也并不纠结。DNS 可以算是最为原始的服务发现系统,但是在服务变更较为频繁,即服务的动态性很强的时候,DNS 记录的传播速度可能会跟不上服务的变更速度,这将导致在一定的时间窗口内无法提供正确的服务位置信息,所以这种方案只适合在比较静态的环境中使用,不适用于微服务。
基于 ZooKeeper、Etcd 等分布式键值对存储服务来建立服务发现系统在现在看起来也不是一种很好的方案,一方面是因为它们只能提供基本的数据存储功能,还需要在外围做大量的开发才能形成完整的服务发现方案。另一方面是因为它们都是强一致性系统,在集群发生分区时会优先保证一致性、放弃可用性,而服务发现方案更注重可用性,为了保证可用性可以选择最终一致性,这两方面原因共同导致了 ZooKeeper、Etcd 这类系统越来越远离服务发现方案的备选清单,像 SmartStack 这种依赖 ZooKeeper 的服务发现方案也逐渐发觉 ZooKeeper 成了它的薄弱环节。
Netflix 的 Eureka 是现在最流行的服务发现方案,服务端和客户端都是 Java 编写的,针对微服务场景,并且和 Netflix 的其他开源项目以及 Spring Cloud 都有着非常好的整合,具备良好的生态,如果你使用 Java 语言开发,Eureka 几乎是你的最佳选择。与 ZooKeeper、Etcd 或者依赖它们的方案不同,Eureka 是个专门为服务发现从零开始开发的项目,Eureka 以可用性为先,可以在多种故障期间保持服务发现和服务注册功能可用,虽然此时会存在一些数据错误,但是 Eureka 的设计原则是“存在少量的错误数据,总比完全不可用要好”,并且可以在故障恢复之后按最终一致性进行状态合并,清理掉错误数据。
前面为什么说 Eureka“几乎是”最佳选择,因为它还有个强大的对手 Consul。Consul 是 HashiCorp 公司的商业产品,它有一个开源的基础版本,这个版本在基本的服务发现功能之外,还提供了多数据中心部署能力,包括内存、存储使用情况在内的细粒度服务状态检测能力,和用于服务配置的键值对存储能力(这是一把双刃剑,使用它可以带来便捷,但是也意味着和 Consul 的较强耦合性),这几个能力 Eureka 目前都没有。而 Consul 的商业版本功能更为强大,如果你不介意依赖单一公司提供的商业产品,也可以从 Consul 的开源版本开始用起。
最后还有一个比较有趣的方案是 SkyDNS,这是一个结合古老的 DNS 技术和时髦的 Go 语言、Raft 算法的有趣项目,主要在 Kubernetes 里使用,因为 Kubernetes 有一层较为稳定的 Service 抽象,有点类似于问题 2 里描述的服务端服务发现方式,所以不存在 DNS 时间窗口的问题。
这里我就不对上述的各个方案做具体功能特性上的对比了,我在做方案选型时不太喜欢做这种微观对比,因为具体的功能特性是易变的,今天 Consul 出一个新功能,明天 Eureka 出一个新特性,如果依赖这个做选择,会摇摆不定,我更注重这些方案背后的一些根深蒂固的必然性,比如 ZooKeeper 永远都不会为了服务发现放弃它的强一致性,所以即使它有再多适合服务发现的功能特性,它也不会成为服务发现的优选方案,再比如 Consul 由一家商业软件公司提供,那么必然或多或少的存在商业软件的某些弊端,如果你非常在意这些弊端,Consul 再强大,你也不会选择它。
4.落地服务发现的难点是什么?
落地服务发现的难点主要来自于分布式系统本身的复杂性和对业务系统的侵入性。因为几乎所有服务提供者和服务消费者都对服务发现服务存在依赖,如果服务发现服务出现问题,将会造成大范围的影响,所以服务发现服务自身的可用性至关重要。
为了保证服务发现服务本身的可用性,除了对服务发现服务进行本地的多节点部署之外,往往还需要跨越多个可用区甚至多个数据中心部署,以确保服务发现服务可以在多个层次的软硬件故障中存活。在服务提供者和服务消费者数量众多时,服务发现服务的性能也可能会成为问题。上述这些问题和分布式系统普遍存在的问题一致,只要在技术上做出充分准备,都是可以解决的,在此不做赘述。
更大的难点在某种程度上是非技术问题,现有的服务发现方案都或多或少的对业务系统存在侵入性,会改变业务系统的开发模式,例如在开发阶段需要引入特殊的客户端和服务的注册、查询过程。给开发带来新的复杂度倒不是什么特别大的问题,因为这些复杂度可以通过技术工具降低,比如普元现在在做的微服务开发平台就可以使这些开发过程中的额外工作自动化,更重要的是服务发现方案一旦确定,之后再做更换的成本会非常高,在对方案进行推广的时候,很可能会引起业务部门的担忧,并因此遇到阻力。
原文地址:https://www.cnblogs.com/weiBlog/p/10205053.html