使用Eureka做服务发现
Zookeeper做注册中心的缺陷

Peter Kelley(个性化教育初创公司Knewton的一名软件工程师)发表了一篇文章说明为什么ZooKeeper用于服务发现是一个错误的做法,他主要提出了三个缺点[1]:

ZooKeeper无法很好的处理网络分区问题,当网络分区中的客户端节点无法到达Quorum时,会与ZooKeeper失去联系,从而也就无法使用其服务发现机制。
服务发现系统应该是一个AP系统,设计上针对可用性;而ZooKeeper是一个CP系统。
ZooKeeper的设置和维护非常困难,实际操作的时候也容易出错,比如在客户端重建Watcher,处理Session和异常的时候。
当然,Peter Kelley提出的这几个问题并不是不能克服的,并不能说明基于ZooKeeper就不能做好一个服务发现系统,但是我们可能有更简洁的方案来实现。
Eureka介绍

什么是Eureka

官方的介绍在这里Eureka wiki。Eureka是Netflix开源的一个RESTful服务,主要用于服务的注册发现。Eureka由两个组件组成:Eureka服务器和Eureka客户端。Eureka服务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。Netflix在其生产环境中使用的是另外的客户端,它提供基于流量、资源利用率以及出错状态的加权负载均衡。
在我看来,Eureka的吸引力来源于以下几点:
开源:大家可以对实现一探究竟,甚至修改源码。
可靠:经过Netflix多年的生产环境考验,使用应该比较靠谱省心
功能齐全:不但提供了完整的注册发现服务,还有Ribbon等可以配合使用的服务。
基于Java:对于Java程序员来说,使用起来,心里比较有底。
spring cloud可以使用Spring Cloud, 与Eureka进行了很好的集成,使用起来非常方便。
Eureka架构

Netflix主要是在AWS中使用Eureka的,虽然同时也支持本地环境,但是了解AWS的一些基础概念对于理解Eureka的设计非常有帮助。

区域与可用区

首先,我们先熟悉两个概念:

区域(Region): AWS云服务在全球不同的地方都有数据中心,比如北美、南美、欧洲和亚洲等。与此对应,根据地理位置我们把某个地区的基础设施服务集合称为一个区域。通过AWS的区域,一方面可以使得AWS云服务在地理位置上更加靠近我们的用户,另一方面使得用户可以选择不同的区域存储他们的数据以满足法规遵循方面的要求。美东(北佛吉尼亚)、美西(俄勒冈)、美西(北加利佛尼亚)、欧洲(爱尔兰)、亚太(新加坡)、亚太(东京)等。每个区域都有自己对应的编码,如:编码对应
这里写图片描述
可用区(Zone): AWS的每个区域一般由多个可用区(AZ)组成,而一个可用区一般是由多个数据中心组成。AWS引入可用区设计主要是为了提升用户应用程序的高可用性。因为可用区与可用区之间在设计上是相互独立的,也就是说它们会有独立的供电、独立的网络等,这样假如一个可用区出现问题时也不会影响另外的可用区。在一个区域内,可用区与可用区之间是通过高速网络连接,从而保证有很低的延时。AWS的区域与可用区的关系示意如下图所示:

这里写图片描述

每次当用户需要使用EC2相关资源的时候,他需要首先选择目标区域,如美东(北佛杰尼亚)us-east-1。然后在创建EC2实例的时候,用户可以选择实例所在的可用区,比如可以是us-east-1a或us-east-1b等。可用区的编码就是区域后面顺序添加不同的英文字母。

Eureka架构说明

下图是Eureka wiki中提供的架构图:

这里写图片描述

从上面的架构图可以看出,主要有三种角色:

  • Eureka Server

    • 通过Register, Get,Renew等接口提供注册和发现
  • Application Service(Service Provider):

    • 服务提供方
    • 把自身服务实例注册到Eureka Server
  • Application Client(Service Consumer):
    • 服务调用方
    • 通过Eureka Server获取服务实例,并调用Application Service

他们主要进行的活动如下:

  1. 每个Region有一个Eureka Cluster, Region中的每个Zone都至少有一个Eureka Server。
  2. Service作为一个Eureka Client,通过register注册到Eureka Server,并且通过发送心跳的方式更新租约(renew leases)。如果Eureka Client到期没有更新租约,那么过一段时间后,Eureka Server就会移除该Service实例。
  3. 当一个Eureka Server的数据改变以后,会把自己的数据同步到其他Eureka Server。
  4. Application Client也作为一个Eureka Client通过Get接口从Eureka Server中获取Service实例信息,然后直接调用Service实例。
  5. Application Client调用Service实例时,可以跨可用区调用。

Eureka Demo

实际工作中,我们很少会直接使用Eureka,因为Spring cloud已经把Eureka与Springboot进行了集成,使用起来更为简单,所以我们使用spring cloud作为示例。
网上有很多示例,这里直接选择一个借花献佛了。spring-cloud-eureka-example

启动Eureka server
Eureka Server非常简单,只需要三个步骤:

1、在pom.xml中添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>

2、实现Application, 添加annotation @EnableEurekaServer @EnableDiscoveryClient 执行main方法启动Eureka Server。

@SpringBootApplication
@EnableEurekaServer
@EnableDiscoveryClient
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

3、在application.yml或者application.properties中添加配置

security:
  basic:
    enabled: true
  user:
    name: admin
    password: admin123
server: 
  port: 8761
eureka:
  client:
    registerWithEureka: false
    fetchRegistry: false
    service-url:
      defaultZone: http://admin:admin123@localhost:${server.port}/eureka/

4、运行Application即可启动Server,启动Server后打开 http://localhost:8761/ ,可以看到如下页面:
这里写图片描述

此时还没有服务注册。

注册服务

把一个服务注册在server中需要以下几个步骤:

1、添加eureka依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

2、添加@EnableEurekaClient注解

@EnableEurekaClient
public class Application

3、在application.yml或者application.properties中添加配置

spring:
  application:
    name: microsoft-consumer-user
server:
  context-path: /consumeruser
  port: 8901   
eureka:
  client:
    serviceUrl:
      defaultZone: http://admin:admin123@192.168.3.130:8761/springbooteureka/eureka/
    healthcheck:
      enabled: true  
  instance:    
    prefer-ip-address: true     
    statusPageUrlPath: ${management.context-path}/info
    healthCheckUrlPath: ${management.context-path}/health 

配置中有两项需要额外注意:

eureka.client.serviceUrl.defaultZone: 指定Eureka服务端的地址,当客户端没有专门进行配置时,就会使用这个默认地址。
spring.application.name: 服务注册所使用的名称,同时其他服务查找该服务时也使用该名称。
我们启动该服务后,可以在管理页面中查看到该服务已经在注册中心中注册成功了。
这里写图片描述

服务发现与负载均衡(Ribbon+RestTemplate)

直接使用Eureka Client还是比较麻烦的,幸运的是,RestTemplate整合了EurekaClient,Ribbon为我们提供了多样的负载均衡的功能,为我们提供了很多便利,我们所需要做的就是在Spring中注册一个RestTemplate,并且添加@LoadBalanced 注解

@Configuration
@RibbonClient(name="microsoft-consumer-user",configuration = TestConfiguration.class)
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

接下来,可以直接使用RestTemplate调用服务。服务的URL中包含了服务名称,例如:http://customer-service/customer/ ,其中,customer-service是服务名,而customer是该服务下的一个接口。

@Autowired
private RestTemplate restTemplate;

public MessageWrapper<Customer> getCustomer(int id) {
    Customer customer = restTemplate.exchange( "http://customer-service/customer/{id}", HttpMethod.GET, null, new ParameterizedTypeReference<Customer>() { }, id).getBody();
    return new MessageWrapper<>(customer, "server called using eureka with rest template");
}

Eureka Api

如果使用的是非Java的语言客户端,可以通过API的方式进行集成。文档

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐