在远程调用的时候,我们写的url是写死的。
String url = "<http://127.0.0.1:9090/product/>"+ orderInfo.getProductId();
当换个机器,或者新增个机器,导致ip变换,从而使得 url 发生了变化,接着就需要去通知所有的相关业务也跟着去改变,这就导致后期非常麻烦。
解决思路:
使用注册中心
什么是注册中心
注册中心是分布式系统/微服务架构中的核心基础设施,它扮演着服务治理的"电话簿"角色,他能维护一个服务列表,哪个机器上线了,哪个机器宕机了,这些信息都会自动更新到服务列表上,客户端拿到这个列表,直接进行服务调用即可。这个就是注册中心。 注册中心主要有三个角色:
- **服务提供者(Server):**一次业务中,被其他微服务调用的服务,也就是提供接口给其他微服务。
- **服务消费者(Client):**一次业务中,调用其他微服务的服务,也就是调用其他微服务提供的接口。
- **服务注册中心(Register):**用于保存Server的注册信息,当Server节点发生变更时,Register会同步变更。服务和注册中心使用一定的通信机制,如果注册中心与某服务长时间无法通信,就会注销该实例。
注:服务提供者和服务消费者是相对的。
他们之间的关系及工作内容,可以通过两个概念来描述:
- **服务注册:**服务提供者在启动时,向Register注册自身服务,并向Register定期发送心跳汇报存活状态。
- **服务发现:**服务消费者从注册中心查询服务提供者的地址,并通过该地址调用服务提供者的接口,服务发现的一个重要作用就是提供给消费者一个可用的服务列表。
CAP理论:分布式系统的基石
CAP 理论是分布式系统设计的核心定理,由计算机科学家 Eric Brewer 于 2000 年提出,揭示了分布式系统必然面临的三元悖论。其核心内容可概括为:
任何分布式系统最多只能同时满足以下三项中的两项:
Consistency(一致性)
Availability(可用性)
Partition tolerance(分区容错性)
注:一致性分为强一致性(主库和从库,不论何时,对外提供的服务都是一致的)和弱一致性(随着时间的推移,最终达到了一致性),这里指的是强一致性。
比如说我有一个数据库集群,客户端向数据库集群发送了一个数据修改的请求,数据库集群需要向客户端进行响应,响应的时机分为以下两种:
- 主库接收到请求,并处理成功,此时数据库还未完全同步到从库,随着时间的推移,主库和从库的数据,最终会达到一个一致性。
- 主库接收到请求,并且所有数据库同步成功时,才返回响应。
CAP三要素:
要素 | 含义 | 典型场景 |
---|---|---|
C:一致性(Consistency) | 所有节点在同一时刻看到的数据完全相同(强一致性) | 银行转账系统:A 转 B 100元后,所有节点必须立即看到更新 |
A:可用性(Availability) | 每个请求都能获得非错误响应(系统始终可操作) | 电商网站:双十一高峰期间必须保证用户能正常下单 |
P:分区容错性(Partition Tolerance) | 网络分区发生时系统仍能继续运行(容忍网络故障) | 跨数据中心部署:当机房之间网络中断时,系统仍能提供服务 |
CAP理论告诉我们:一个分布式系统不可能同时满足数据一致性,服务可用性和分区容错性这是三个基本需求,最多只能同时满足其中的两个。
在分布式系统中,系统间的网络不能100%保证健康,服务又必须对外保证服务,因此PartitionTolerance不可避免,那就只能在C和A中选择一个,也就是CP或者AP架构。
正常情况:
异常情况:
CP架构:为了保证分布式系统对外的数据一致性,于是选择不返回任何数据。
AP架构:为了保证分布式系统的可用性,节点2返回V0版本的数据(即使这个数据不正确)
常见的注册中心有:
- Zookeeper(CP)
- nacos(CP或AP,默认是AP)
- eureka(AP)
Eureka介绍
Eureka是Netflix OSS套件中关于服务注册和发现的解决⽅案. Spring Cloud对Eureka进⾏了集成, 并
作为优先推荐⽅案进⾏宣传, 虽然⽬前Eureka 2.0已经停⽌维护, 新的微服务架构设计中, 也不再建议使
⽤, 但是⽬前依然有⼤量公司的微服务系统使⽤Eureka作为注册中⼼。
Eureka主要分为两个部分:
- Eureka Server:作为注册中心Server端,向微服务应用程序提供服务注册,发现健康检查等能力。
- Eureka Client:服务提供者,服务启动时,会向Eureka Server 注册自己的信息(IP,端口,服务信息等),Eureka Server会存储这些信息。
搭建注册中心
1.创建项目
2.pom加入Eureka的环境
<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>
3.配置文件,增加Eureka相关的配置
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/
logging:
pattern:
console: '%d{MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n'
4.启动类,开启Eureka的功能
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}
5.启动测试
启动服务,访问注册中心:http://127.0.0.1:10010/
可以看到能正常访问注册中心了。
服务注册
我们的需求是订单查询订单信息的时候,根据订单里的产品ID,获取产品的详细信息,也就是需要order-service远程调用product-service,也就是说product-service是服务提供者,就需要把product-sevrice服务注册到注册中心里去。
1.加入Eureka的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.完善配置信息
#服务名称
spring:
application:
name: product-service
#eureka地址
eureka:
client:
service-url:
defaultZone: <http://127.0.0.1:10010/eureka/>
#日志
logging:
pattern:
console: '%d{MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n'
3.启动测试
可以看到product-service已经成功注册到eureka-service上了。
服务发现
order-service是服务消费者,在远程调用时候,会从eureka-server拉取product-service的服务信息,实现服务发现。
1.加入Eureka依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.完善配置信息
spring:
application:
name: order-service
eureka:
client:
service-url:
defaultZone: <http://127.0.0.1:10010/eureka/>
logging:
pattern:
console: '%d{MM-dd HH:mm:ss.SSS} %c %M %L [%thread] %m%n'
3.修改远程调用的代码
public OrderInfo selectOrderById(Integer orderId){
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
//String url = "<http://127.0.0.1:9090/product/>" + orderInfo.getProductId();
//从Eureka中获取服务列表
List<ServiceInstance> instances = discoveryClient.getInstances("product-service");
//http://127.0.0.1:9090
//instances.get(0):服务可能有多个,这里只获取第一个
String uri = instances.get(0).getUri().toString();
String url = uri + "/product/" + orderInfo.getProductId();
log.info("远程调用url:{}",url);
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
4.启动测试
可以看到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发⽣故障时, 需要重新选举, 选举过程集群会有短暂时间的不可⽤.