在开始之前,我们先来回顾下业内对于微服务架构的定义。简单来说,微服务就是用一组小服务的方式来构建一个应用,服务独立运行在不同的进程中,服务之间通过轻量的通讯机制(如 RESTful 接口)来交互,并且服务可以通过自动化部署方式独立部署。
从定义中不难理解,微服务架构其实也就意味着更多的独立服务,并且这些服务之间需要频繁交互和通信。通讯可以使用 RESTful 的方式,但通讯之前服务和服务之间是如何知道彼此的地址的(类比打电话)?这个时候就需要引入服务发现的概念。简单来说,服务发现就是服务或者应用之间互相定位的过程,它也并不是什么新鲜的概念。那在微服务的架构体系中,我们应该如何落地服务发现?又有哪些可用的开源方案?带着这些问题,InfoQ 记者采访了微服务专家宋潇男。
InfoQ:谈谈什么是服务发现?你是如何理解服务发现的?在整个微服务架构中,服务发现起到什么作用?
宋潇男:服务发现这个事情可以说是自古有之,当我们使用网络打印机的时候,首先要通过 WS-Discovery 或者 Bonjour 协议发现并连接网络中存在的打印服务。当我们使用蓝牙耳机或者音箱的时候,首先要通过 SDP 协议发现并连接附近的蓝牙音频服务,这些服务发现方式可以让用户不必关心服务提供者的具体网络位置(IP 地址、端口等)和配置方步骤,只需选择和连接即可使用这些服务。
之前做单体式应用开发时很少提及服务发现,因为传统单体应用动态性不强,不会频繁的更新和重新发布,也较少进行自动伸缩。传统单体应用的网络位置很少发生变化,在发生变化时,由运维人员手工更新一下它们的配置文件,也不是什么太大的问题。
而微服务架构则完全不同,微服务会被频繁的更新和重新发布,频繁的根据负载情况进行动态伸缩,微服务实例还可能受资源调度影响而从一台服务器迁移到另一台服务器。
总而言之,在微服务架构中,微服务实例的网络位置发生变化是一种常态,所以必须提供一种机制,使得服务消费者在服务提供者的网络位置发生变化时,能够及时获得最新的位置信息,一般是提供一个网络位置稳定的服务注册中心,服务提供者的网络位置被注册到注册中心,并在网络位置发生变化的时候及时更新,而服务消费者定期向注册中心获取服务提供者的最新位置信息,这就是最基本的服务发现机制。较为复杂的服务发现实现除了服务提供者的位置信息外,还可以向服务消费者提供服务提供者的描述信息、状态信息和资源使用信息,以供服务消费者实现更为复杂的服务选择逻辑。
之前做单体式应用的时候,有个和服务发现类似的概念叫服务目录,其实这两个概念并不冲突,也不存在明显的替代关系。服务目录更像是一个市场,是一个把服务当作产品上架展示,供用户了解和购买的渠道,使用者是人;而服务发现是供服务注册自身和查询其他服务相关信息的一种机制,使用者是程序。在微服务架构中,服务发现作为一种基本机制,必须得到实现,而服务目录则是一个可选项,如果有的话,用户体验会更好一些。
InfoQ:能否以一个请求为例,介绍下服务发现的流程?
宋潇男:服务发现的流程比较简单,去年我翻译了 Chris Richardson 的一些微服务文章,对服务发现的流程做了些基本的描述,比较完备的说,服务发现流程应该分为两种模式:
客户端发现:
- 服务提供者的实例在启动时或者位置信息发生变化时会向服务注册表注册自身,在停止时会向服务注册表注销自身,如果服务提供者的实例发生故障,在一段时间内不发送心跳之后,也会被服务注册表注销。
- 服务消费者的实例会向服务注册表查询服务提供者的位置信息,然后通过这些位置信息直接向服务提供者发起请求。
服务端发现:
- 第一步与客户端发现相同。
- 服务消费者不直接向服务注册表查询,也不直接向服务提供者发起请求,而是将对服务提供者的请求发往一个中央路由器或者负载均衡器,中央路由器或者负载均衡器查询服务注册表获取服务提供者的位置信息,并将请求转发给服务提供者。
这两种模式各有利弊,客户端发现模式的优势是,服务消费者向服务提供者发起请求时比服务端发现模式少了一次网络跳转,劣势是服务消费者需要内置特定的服务发现客户端和服务发现逻辑;服务端发现模式的优势是服务消费者无需内置特定的服务发现客户端和服务发现逻辑,劣势是多了一次网络跳转,并且需要基础设施环境提供中央路由机制或者负载均衡机制。目前客户端发现模式应用的多一些,因为这种模式的对基础设施环境没有特殊的要求,和基础设施环境也没有过多的耦合性。
InfoQ:目前都有哪些服务发现的解决方案?能否详细介绍下各个解决方案的优缺点?
宋潇男:其实可选方案并不多,所以选择起来也并不纠结。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 再强大,你也不会选择它。
InfoQ:你推荐使用哪种解决方案?它如何与整个的服务架构结合?
宋潇男:其实在上一个问题的回答中,我的倾向性已经非常明显了,我推荐 Eureka,如果你想使用商业产品,我也不会推荐 Consul,所以商业产品我当然会推荐普元的(笑)。当然,普元并不提供单独的服务注册产品,普元在微服务开发和运行平台中基于 Eureka 提供服务发现能力。
至于第二个问题,因为 Eureka 和 Spring Boot、Spring Cloud 都整合的非常好,所以使用起来非常简单,只需在 pom 中加入对 Spring Cloud Eureka Server 的依赖并在代码中加入 @EnableEurekaServer,即可创建一个 Eureka Server,在服务提供者和消费者这边,只需在 pom 中加入对 Spring Cloud Eureka 的依赖并在代码中加入 @EnableDiscoveryClient,代码运行时即可自动将自身注册到 Eureka Server 中,然后使用 getInstances 方法即可查询服务实例的位置信息,这个时候还可以使用客户端负载均衡方案 Netflix Ribbon 对这些实例做负载均衡。如果你在服务架构的其他部分也使用 Netflix 和 Spring Cloud 提供的模块,那么集成起来也非常容易。
InfoQ:落地服务发现的难点是什么?
宋潇男:落地服务发现的难点主要来自于分布式系统本身的复杂性和对业务系统的侵入性。因为几乎所有服务提供者和服务消费者都对服务发现服务存在依赖,如果服务发现服务出现问题,将会造成大范围的影响,所以服务发现服务自身的可用性至关重要。
为了保证服务发现服务本身的可用性,除了对服务发现服务进行本地的多节点部署之外,往往还需要跨越多个可用区甚至多个数据中心部署,以确保服务发现服务可以在多个层次的软硬件故障中存活。在服务提供者和服务消费者数量众多时,服务发现服务的性能也可能会成为问题。上述这些问题和分布式系统普遍存在的问题一致,只要在技术上做出充分准备,都是可以解决的,在此不做赘述。
更大的难点在某种程度上是非技术问题,现有的服务发现方案都或多或少的对业务系统存在侵入性,会改变业务系统的开发模式,例如在开发阶段需要引入特殊的客户端和服务的注册、查询过程。给开发带来新的复杂度倒不是什么特别大的问题,因为这些复杂度可以通过技术工具降低,比如普元现在在做的微服务开发平台就可以使这些开发过程中的额外工作自动化,更重要的是服务发现方案一旦确定,之后再做更换的成本会非常高,在对方案进行推广的时候,很可能会引起业务部门的担忧,并因此遇到阻力。
欢迎扫描以下个人微信二维码,加入由本文专家宋潇男主持的技术讨论微信群,与专家一同交流、探讨微服务相关话题,加群暗号:611。
感谢郭蕾对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ , @丁晓昀),微信(微信号: InfoQChina )关注我们。
评论