一、背景
1.1、问题描述
在微服务架构中,远程调用时 URL 通常是写死的。例如:
介绍微服务架构下的服务注册与发现机制,基于 CAP 理论分析注册中心选型,详细演示了 Eureka Server 搭建、服务提供者注册及服务消费者通过 DiscoveryClient 实现远程调用的完整流程,并对比了 Eureka 与 Zookeeper 的差异。

在微服务架构中,远程调用时 URL 通常是写死的。例如:
String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();
当更换机器或新增机器时,这个 URL 就需要跟着变更,需要通知所有的相关服务去修改。随之而来的就是各个项目的配置文件反复更新,各个项目的频繁部署。这种没有具体意义但又不得不做的工作,会让人非常痛苦。
生活中,避免不了和各个机构(医院、学校、政府部门等)打交道,就需要保存各个机构的电话号码。如果机构换了电话号码,就需要通知各个使用方,但是这些机构的使用方群体是巨大的,没办法做到一一通知,怎么处理呢?
机构电话如果发生变化,通知 114。用户需要联系机构时,先打 114 查询电话,然后再联系各个机构。114 查号台的作用主要有两个:
同样的,微服务开发时,也可以采用类似的方案。
在最初的架构体系中,集群的概念还不那么流行,且机器数量也比较少,此时直接使用 DNS+Nginx 就可以满足几乎所有服务的发现。相关的注册信息直接配置在 Nginx。但是随着微服务的流行与流量的激增,机器规模逐渐变大,并且机器会有频繁的上下线行为,这种时候需要运维手动地去维护这个配置信息是一个很难的操作。所以开发者们开始希望有这么一个东西,它能维护一个服务列表,哪个机器上线了,哪个机器宕机了,这些信息都会自动更新到服务列表上,客户端拿到这个列表,直接进行服务调用即可。这就是注册中心。
注册中心主要有三种角色:
- 服务提供者 (Server):一次业务中,被其它微服务调用的服务。也就是提供接口给其它微服务。
- 服务消费者 (Client):一次业务中,调用其它微服务的服务。也就是调用其它微服务提供的接口。
- 服务注册中心 (Registry):用于保存 Server 的注册信息,当 Server 节点发生变更时,Registry 会同步变更。服务与注册中心使用一定机制通信,如果注册中心与某服务长时间无法通信,就会注销该实例。
他们之间的关系以及工作内容,可以通过两个概念来描述:
谈到注册中心,就避不开 CAP 理论。 CAP 理论是分布式系统设计中最基础,也是最为关键的理论。
- 一致性 (Consistency):CAP 理论中的一致性,指的是强一致性。所有节点在同一时间具有相同的数据。
- 可用性 (Availability):保证每个请求都有响应(响应结果可能不对)。
- 分区容错性 (Partition Tolerance):当出现网络分区后,系统仍然能够对外提供服务。
举例说明:
一个部门全国各地都有岗位,这时候,总部下发了一个通知,由于通知需要开会周知全员,当有客户咨询时:
- 所有成员对客户的回应结果都是一致的(一致性)
- 客户咨询时,一定有回应(可用性)
- 当其中一个成员休假时,这个部门的其他成员也可以对客户提供咨询服务(分区容错性)
CAP 理论告诉我们:一个分布式系统不可能同时满足数据一致性、服务可用性和分区容错性这三个基本需求,最多只能同时满足其中的两个。
在分布式系统中,系统间的网络不能 100% 保证健康,服务又必须对外保证服务。因此 Partition Tolerance 不可避免。那就只能在 C 和 A 中选择一个。也就是 CP 或者 AP 架构。
正常情况:

网络异常:

CP 架构:为了保证分布式系统对外的数据一致性,于是选择不返回任何数据。 AP 架构:为了保证分布式系统的可用性,节点 2 返回 V0 版本的数据(即使这个数据不正确)。
CAP 理论对比:
| Zookeeper | Eureka | Nacos | |
|---|---|---|---|
| CAP 理论 | CP | AP | CP 或 AP、默认 AP |
在分布式环境中,即使拿到一个错误的数据,也胜过无法提供实例信息而造成请求失败要好(比如淘宝 11.11,京东 618 都是谨遵 AP 原则),本系列文章会给大家介绍 Eureka 和 Nacos 的使用。
Eureka 是 Netflix OSS 套件中关于服务注册和发现的解决方案。Spring Cloud 对 Eureka 进行了集成,并作为优先推荐方案进行宣传,虽然目前 Eureka 2.0 已经停止维护,新的微服务架构设计中,也不再建议使用,但是目前依然有大量公司的微服务系统使用 Eureka 作为注册中心。
Eureka 主要分为两个部分:
关于 Eureka 的学习,主要包含以下三个部分:
Eureka-server 是一个独立的微服务。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
给该项目编写一个启动类,并在启动类上添加 @EnableEurekaServer 注解,开启 eureka 注册中心服务。
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
server:
port: 10010
spring:
application:
name: eureka-server
eureka:
instance:
hostname: localhost
client:
fetch-registry: false # 表示是否从 EurekaServer 获取注册信息,默认为 true。因为这是一个单点的 EurekaServer,不需要同步其他的 EurekaServer 节点的数据,这里设置为 false
register-with-eureka: false # 表示是否将自己注册到 EurekaServer,默认为 true。由于当前应用就是 EurekaServer,故而设置为 false.
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动服务后,注册中心就可以实现了。
接下来我们把 product-service 注册到 eureka-server 中。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
添加服务名称和 eureka 地址。
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10010/eureka
启动后服务注册成功。
接下来我们修改 order-service,在远程调用时,从 eureka-server 拉取 product-service 的服务信息,实现服务发现。
服务注册和服务发现都封装在 eureka-client 依赖中,所以服务发现时,也是引入 eureka-client 依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
spring:
application:
name: order-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10010/eureka
@Slf4j
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Resource
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
// String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId();
// 根据应用名称获取服务列表
List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
// 服务可能有多个,获取第一个
EurekaServiceInstance instance = (EurekaServiceInstance) instances.get(0);
log.info(instance.getInstanceId());
// 拼接 url
String url = instance.getUri() + "/product/" + orderInfo.getProductId();
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
}
Eureka 和 Zookeeper 都是用于服务注册和发现的工具,区别如下:
- Eureka 是 Netflix 开源的项目,而 Zookeeper 是 Apache 开源的项目。
- Eureka 基于 AP 原则,保证高可用,Zookeeper 基于 CP 原则,保证数据一致性。
- Eureka 每个节点都是均等的,Zookeeper 的节点区分 Leader 和 Follower 或 Observer,也正因为这个原因,如果 Zookeeper 的 Leader 发生故障时,需要重新选举,选举过程集群会有短暂时间的不可用。
本文介绍了 Spring Cloud 中 Eureka 的基本配置与使用流程,涵盖了服务注册中心的原理、CAP 理论分析以及具体的代码实现。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online