目 录
- 一. 背景
- 1.1 问题描述
- 1.2 解决思路
- 1.3 什么是注册中心
- 1.4 CAP理论
- 1.5 常见的注册中心
- 二. Eureka 介绍
- 三. 搭建Eureka Server
- 3.1 创建 Eureka-server 子模块
- 3.2 引入 eureka-server 依赖
- 3.3 项目构建插件
- 3.4 完善启动类
- 3.5 编写配置文件
- 3.6 启动服务
- 四. 服务注册
- 4.1 引入 eureka-client 依赖
- 4.3 启动服务
- 五. 服务发现
- 5.1 引入依赖
- 5.2 完善配置文件
- 5.3 远程调用
- 5.4 启动服务
- 六. Eureka 和 Zookeeper 区别
一. 背景
1.1 问题描述
上个文章的例子中可以看到, 远程调用时, 我们的 URL 是写死的
String url = "http://127.0.0.1:9090/product/"+ orderInfo.getProductId();
当更换机器, 或者新增机器时, 这个URL就需要跟着变更, 就需要去通知所有的相关服务去修改. 随之而来的就是各个项目的配置文件反复更新, 各个项目的频繁部署. 这种没有具体意义, 但⼜不得不做的工作, 会让⼈非常痛苦
1.2 解决思路
例如:
某机构电话如果发生变化, 通知114. 用户需要联系机构时, 先打114查询电话, 然后再联系各个机构.114查号台的作用主要有两个:
号码注册: 服务方把电话上报给114
号码查询: 使用方通过114可以查到对应的号码
同样的, 微服务开发时, 也可以采用类似的方案.
- 服务启动/变更时, 向注册中心报道. 注册中心记录应用和 IP 的关系.
- 调用方调用时, 先去注册中心获取服务方的IP, 再去服务方进行调用
1.3 什么是注册中心
在最初的架构体系中, 集群的概念还不那么流行, 且机器数量也比较少, 此时直接使用 DNS+Nginx 就可以满足几乎所有服务的发现. 相关的注册信息直接配置在 Nginx. 但是随着微服务的流行与流量的激增,机器规模逐渐变大, 并且机器会有频繁的上下线行为, 这种时候需要运维手动地去维护这个配置信息是⼀个很麻烦的操作. 所以开发者们开始希望有这么⼀个东西, 它能维护⼀个服务列表, 哪个机器上线了,哪个机器宕机了, 这些信息都会自动更新到服务列表上, 客户端拿到这个列表, 直接进行服务调用即可.这个就是注册中心
注册中心主要有三种角色:
- 服务提供者(Server):⼀次业务中, 被其它微服务调用的服务. 也就是提供接口给其它微服务.
- 服务消费者(Client):⼀次业务中, 调用其它微服务的服务. 也就是调用其它微服务提供的接口.
- 服务注册中心(Registry): 用于保存Server 的注册信息, 当Server 节点发生变更时, Registry 会同步变更. 服务与注册中心使用⼀定机制通信, 如果注册中心与某服务长时间无法通信, 就会注销该实例.
他们之间的关系以及工作内容, 可以通过两个概念来描述:
- 服务注册:服务提供者在启动时, 向 Registry 注册自身服务, 并向 Registry 定期发送心跳汇报存活状态.
- 服务发现: 服务消费者从注册中心查询服务提供者的地址,并通过该地址调用服务提供者的接口. 服务发现的⼀个重要作用就是提供给服务消费者⼀个可用的服务列表
1.4 CAP理论
谈到注册中心, 就避不开CAP理论.
CAP 理论是分布式系统设计中最基础, 也是最为关键的理论.
• ⼀致性(Consistency) CAP理论中的⼀致性, 指的是强⼀致性. 所有节点在同⼀时间具有相同的数据
• 可用性(Availability) 保证每个请求都有响应(响应结果可能不对)
• 分区容错性(Partition Tolerance) 当出现网络分区后,系统仍然能够对外提供服务
CAP 理论告诉我们: ⼀个分布式系统不可能同时满足数据⼀致性, 服务可用性和分区容错性这三个基本需求, 最多只能同时满足其中的两个.
在分布式系统中, 系统间的网络不能 100% 保证健康, 服务又必须对外保证服务. 因此 Partition Tolerance不可避免. 那就只能在C和A中选择⼀个. 也就是 CP 或者 AP 架构
正常情况:
网络异常:
- CP架构: 为了保证分布式系统对外的数据⼀致性, 于是选择不返回任何数据
- AP架构: 为了保证分布式系统的可用性, 节点2返回 V0 版本的数据(即使这个数据不正确)
1.5 常见的注册中心
-
Zookeeper
Zookeeper 的官方并没有说它是⼀个注册中心, 但是国内 Java 体系, 大部分的集群环境都是依赖 Zookeeper 来完成注册中心的功能. -
Eureka
Eureka是Netflix开发的基于REST的服务发现框架, 主要用于服务注册, 管理,负载均衡和服务故障转移.官方声明在 Eureka2.0 版本停止维护, 不建议使用. 但是 Eureka 是 SpringCloud 服务注册/发现的默认实现, 所以目前还是有很多公司在使用. -
Nacos
Nacos是Spring Cloud Alibaba架构中重要的组件, 除了服务注册, 服务发现功能之外, Nacos 还支持配置管理, 流量管理, DNS, 动态DNS等多种特性.
CAP 理论对比
二. Eureka 介绍
Eureka是Netflix OSS套件中关于服务注册和发现的解决方案. Spring Cloud对Eureka进行了集成, 并作为优先推荐⽅案进⾏宣传, 虽然目前Eureka 2.0已经停止维护, 新的微服务架构设计中, 也不再建议使用, 但是目前依然有大量公司的微服务系统使用 Eureka 作为注册中心.
Eureka主要分为两个部分:
- Eureka Server: 作为注册中心 Server 端, 向微服务应用程序提供服务注册, 发现, 健康检查等能力.
- Eureka Client: 服务提供者, 服务启动时, 会向Eureka Server 注册自己的信息(IP,端口,服务信息等),Eureka Server 会存储这些信息
关于Eureka的学习, 主要包含以下三个部分:
- 搭建 Eureka Server
- 将 order-service, product-service 都注册到 Eureka
- order-service 远程调用时, 从 Eureka 中获取 product-service 的服务列表, 然后进行交互
三. 搭建Eureka Server
Eureka-server 是⼀个独立的微服务.
3.1 创建 Eureka-server 子模块
3.2 引入 eureka-server 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
3.3 项目构建插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
3.4 完善启动类
给该项目编写⼀个启动类, 并在启动类上添加 @EnableEurekaServer 注解,开启 eureka 注册中心服务
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3.5 编写配置文件
server:
port: 10010
spring:
application:
name: eureka-server
eureka:
instance:
hostname: localhost
client:
fetch-registry: false # 表⽰是否从Eureka Server获取注册信息,默认为true.因为这是⼀个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,这⾥设置为false
register-with-eureka: false # 表⽰是否将⾃⼰注册到Eureka Server,默认为true.由于当前应⽤就是Eureka Server,故⽽设置为false.
service-url:
# 设置与Eureka Server的地址,查询服务和注册服务都需要依赖这个地址.
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
3.6 启动服务
启动服务, 访问注册中心: http://127.0.0.1:10010/
可以看到 eureka-server 已经启动成功了
四. 服务注册
接下来我们把 product-service 注册到 eureka-server 中
4.1 引入 eureka-client 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
4.2 完善配置文件
添加服务名称和 eureka 地址
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10010/eureka
4.3 启动服务
刷新注册中心: http://127.0.0.1:10010/
可以看到 product-service 已经注册到 eureka 上了
五. 服务发现
接下来我们修改 order-service, 在远程调用时, 从 eureka-server 拉取 product-service 的服务信息, 实现服务发现
5.1 引入依赖
服务注册和服务发现都封装在 eureka-client 依赖中, 所以服务发现时, 也是引入 eureka-client 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
5.2 完善配置文件
服务发现也需要知道 eureka 地址, 因此配置内容依然与服务注册⼀致,都是配置 eureka 信息
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10010/eureka
5.3 远程调用
远程调用时, 我们需要从 eureka-server 中获取 product-service 的列表(可能存在多个服务), 并选择其中⼀个进行调用
import com.bite.order.mapper.OrderMapper;
import com.bite.order.model.OrderInfo;
import com.bite.order.model.ProductInfo;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.eureka.EurekaServiceInstance;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@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;
}
}
5.4 启动服务
刷新注册中心: http://127.0.0.1:10010/
可以看到 order-service 已经注册到 eureka上了
访问接口: http://127.0.0.1:8080/order/1
可以看到, 远程调用也成功了
六. Eureka 和 Zookeeper 区别
Eureka和Zookeeper都是⽤于服务注册和发现的⼯具,区别如下:
- Eureka 是 Netflix 开源的项目, 而 Zookeeper 是 Apache 开源的项目.
- Eureka 基于AP原则, 保证高可用, Zookeeper 基于 CP 原则, 保证数据⼀致性.
- Eureka 每个节点 都是均等的, Zookeeper 的节点区分 Leader 和 Follower 或 Observer, 也正因为这个原因, 如果 Zookeeper 的 Leader 发生故障时, 需要重新选举, 选举过程集群会有短暂时间的不可用.