背景
服务通常需要调用其他服务。单体应用中,服务通过语言级别的方法或者过程调用另外的服务。在传统的分布式部署中,服务运行在固定,已知的地址(主机和端口),因此可以请求的通过HTTP/REST或其他RPC机制调用。然而,一个现代的微服务应用通常运行在虚拟或者容器环境,服务实例数和它们的地址都在动态改变。
服务发现因此,你需要实现一种机制,允许服务的客户端向动态变更的一组短暂的服务实例发起请求。
问题
如何让服务的客户端 - API网关或者其他服务 - 发现服务实例的地址
限制
- 每个服务在特定地址(主机和端口)实例暴露一个远程API,比如HTTP/REST,或者Thrift等等
- 服务实例的数量和他们的地址动态改变。
- 虚拟机和容器通常分配动态IP。
- 服务实例数可能动态变化。比如,EC2 Autoscaling Group基于负载确定实例数。
解决方案
当发起对服务的请求时,客户端通过查询一个服务注册中心或者服务实例的地址,服务注册中心知道所有服务实例的地址。
下图介绍了这个模式的架构。
image这很典型的已经被微服务基架处理。
示例
微服务示例应用是使用了客户端服务发现的一个例子。它用Scala编写,使用了Spring Boot和Spring Cloud作为微服务基架。它们提供了许多功能,包括客户端服务发现。
RegistrationServiceProxy
是该应用的一个组件。为了注册用户,它通过Spring框架的RestTemplate
调用其他服务:
@Component
class RegistrationServiceProxy @Autowired()(restTemplate: RestTemplate) extends RegistrationService {
@Value("${user_registration_url}")
var userRegistrationUrl: String = _
override def registerUser(emailAddress: String, password: String): Either[RegistrationError, String] = {
val response = restTemplate.postForEntity(userRegistrationUrl,
RegistrationBackendRequest(emailAddress, password),
classOf[RegistrationBackendResponse])
...
}
它和RestTemplate一起被注入,同时还有user_registration_url
,它指定了REST的端点。
当应用部署时,user_registration_url
被设置为URL http://REGISTRATION-SERVICE/user
- 参见 docker-compose.yml
文件。REGISTRATION-SERVICE
是个逻辑的服务名字,通过客户端服务发现被解析成一个网络地址。服务发现使用了 Netflix OSS 组件。它提供了 Eureka:一个服务注册中心,以及 Ribbon:一个HTTP客户端,查询Eureka以路由HTTP请求到可用的服务实例。
客户端服务发现通过一些Spring Cloud注解来配置
@Configuration
@EnableEurekaClient
@Profile(Array("enableEureka"))
class EurekaClientConfiguration {
@Bean
@LoadBalanced
def restTemplate(scalaObjectMapper : ScalaObjectMapper) : RestTemplate = {
val restTemplate = new RestTemplate()
restTemplate.getMessageConverters foreach {
case mc: MappingJackson2HttpMessageConverter =>
mc.setObjectMapper(scalaObjectMapper)
case _ =>
}
restTemplate
}
@EnableEurekaClient
注解启用了Eureka客户端。@LoadBalanced
注解配置了RestTemplate
使用Ribbon,Ribbon被设置为使用Eureka客户端来实现服务发现。因此,RestTemplate
将通过查询Eureka,找出可用服务实例的网络地址,来处理对 http://REGISTRATION-SERVICE/user
的请求
结果
客户端服务发现有如下优势:
- 相比服务端服务发现,有更少的网络活动和网络跳数
客户端服务发现有如下弊端:
- 这个模式将客户端和服务注册中心耦合
- 你必须为应用使用的每个编程语言 / 框架实现客户端服务发现的逻辑,比如Java / Scala,JavaScript / NodeJS。比如,Netflix Prana 为非JVM的客户端提供了一个以HTTP代理为基础的方式。
网友评论